<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//VoiceXML Forum//DTD XHTML+Voice 1.2//EN" "http://www.voicexml.org/specs/multimodal/x+v/12/dtd/xhtml+voice12.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:vxml="http://www.w3.org/2001/vxml" 
    xmlns:ev="http://www.w3.org/2001/xml-events"
    xmlns:xv="http://www.voicexml.org/2002/xhtml+voice"
    xml:lang="en-US">
    
<!--***********************************************************************
Copyright (c) 2008
Richard A. Frost, University of Windsor. All rights reserved.
Redistribution and use of this .xml page, with or without
modification, is permitted provided that the following conditions
are met:

	• Redistributions of this page in any form must retain the above copyright
	  notice, this list of conditions and the following disclaimer.
	• Redistributions  must reproduce the above copyright
	  notice, this list of conditions and the following disclaimer in the
	  documentation and/or other materials provided with the distribution.
	• Redistribution in any form must be accompanied by the following  information:

        1. This page was designed by Dr. Richard A. Frost of the University of Windsor. 
        2.The first implementation was written by Mr. (William) Xiaoli Ma, Master’s student – 2006.
	3.The page has been updated by: Dr. Frost – 20 November 2007.  
	                                                                             
                                                        Mr. Ali Karaki – 8 May 2008.
							
	4.The page implements the LRRP SpeechWeb architecture and serves as an interface to the Public-Domain SpeechWeb as described in:
	i.Richard A. Frost: Call for a public-domain SpeechWeb. Commun. ACM 48(11): 45-49 (2005).
	ii.Richard A. Frost, Nabil Abdullah, Kunal Bhatia, Sanjay Chitte, Fadi Hanna, Maxim Roy, Yue Shi, Li Su (2004) LRRP SpeechWebs. Proceedings of the 2nd Annual Conference on Communication Networks and Services Research (CNSR 2004) Fredericton, N.B., Canada May 19-21, 2004, 91-98.

	5.Information on the MySpeechWeb research project at the University of Windsor, Canada, and the Public-Domain SpeechWeb that is being constructed can be found at the web sites:
	  
                    www.myspeechweb.org
                    www.myspeechweb.com
                    www.myspeechweb.ca
                    www.publicdomainspeechweb.org
                    www.publicdomainspeechweb.com
                    www.publicdomainspeechweb.ca

Disclaimer
THIS SOFTWARE IS PROVIDED BY RICHARD FROST  ``AS IS'' AND ANY EXPRESS 
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL RICHARD FROST
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Note that the above disclaimer is an adaptation of the disclaimer used in the Berkeley 
Databse license otherwise known as the Sleepycat  Public License

*********************************************************************-->

<head>

<!-- Please modify the value of variable sv_greeting, and cgiLink to fit your application. -->
<script type="text/javascript">
/** The greeting message that will say to the user, only at the first time the user visits this page. **/
var sv_greeting ="I am the menu page to browse through the applications generated by SWAC. You can also go to the public domain Speechweb through this page. Please say, go to, followed by the name of the application you want to talk to or say, what are the applications, for a list of the available applications.";
/** The link to your CGI interpreter location. 
Notice, you have to place the CGI interpreter program with this page in the same domain to prevent a cross-domain security error.**/
var cgiLink="public/menu.cgi";
</script>

<!-- VoiceXML form. -->
<vxml:form id="vxml_form">
<vxml:field name="st_field" xv:id="voice_input" modal="true">
   <!-- NOTICE!!! PLEASE MODIFY THE VALUE OF 'src' ATTRIBUTE IN THE NEXT LINE <grammar> ELEMENT TO YOUR GRAMMAR FILE LOCATION.-->
   <vxml:grammar type="application/x-jsgf" 
src="public/menu.jsgf" />
   <!-- The following greeting will only speak out when user connects to a new interpreter. -->
     <vxml:prompt cond="sayGreetings==true"><vxml:break time="500ms"/><vxml:value expr="sv_greeting"/>
     <vxml:value expr="updateShowFrame('GREETING: '+sv_greeting);"/></vxml:prompt>
   <vxml:filled>
 <!--***********************************
  This "filled" element will be run after user speech input has recognized. 
  Inside this element, first step, i have assign the user input to the variable 'question', 
  because VoiceXML code can access a JavaScript defined variable, but JavaScript can not see a VoiceXML defined variable.
  Then, in the next step, i call a JavaScript function "runCode()' to proceed AJAX submit process.
************************************-->
     <vxml:assign name="question" expr="st_field"/>
     <!--*********************
       Calls to javascript mainControl() function to do the logical process based on user voice input.
     *************************-->
     <vxml:assign name="javacode" expr="mainControl();"/>
     <vxml:prompt><vxml:break time="300ms"/><vxml:value expr="answer"/></vxml:prompt>
<!-- If the answer is not a link to next interpreter, then repeat the voice dialog. -->    
     <vxml:if cond="isLink==false">
       <vxml:throw event="repeat.st_field"/>
     </vxml:if>
   </vxml:filled>
   <vxml:catch event="nomatch noinput">
     <vxml:prompt>Sorry. i didn't understand. Can you say it again?</vxml:prompt>
     <vxml:reprompt/>
   </vxml:catch>
   <vxml:catch event="help">
     No help is available! Restart the dialog!
     <vxml:clear namelist="st_field"/>
     <vxml:reprompt/>
   </vxml:catch>
</vxml:field>

<!-- Catch the 'repeat.st_field' event. -->
<vxml:catch event="repeat.st_field">
  <vxml:clear namelist="st_field"/>
<!-- Restart the voice form without change the speech grammar. -->
  <vxml:reprompt/>
</vxml:catch>
</vxml:form>


<script type="text/javascript">
/******* Declare global variables shared by JavaScript and VoiceXML *********/
var sayGreetings=true;
var defaultGreetingMsg="Hi, i'm ready to talk now.";
/*** The location of next remote speech-application/CGI-application interpeter. ***/
var nextPage="";
/*** Question query recognized from user's speech (request). ***/
var question="";
/*** Answer query returned from remote CGI interpter (response). ***/
var answer="";
var answerRecieved=false;
/*** Answer query contains a link to next CGI interpreter. ***/
var isLink=false;
var gotoNext= false;
/*** This variable needed for VXML to call JavaScript code. ***/
var javacode="";
/*** menu page of the demo public-domain speechweb. ***/
var startPage="http://luna.cs.uwindsor.ca/~speechweb/p_d_speechweb/menu/demo_menu.xml";

if(sv_greeting=="")
  sv_greeting=defaultGreetingMsg;


/************************************************************************************************
This is the main control function to the whole question submit and answer retrived procedures.
It will call submitReq() method to send the question to the CGI program.
then it will check the answer whether it is a link to new CGI program or a simply answer string.
if it is a link to another interpreter, then retrieve the data from there, 
and call the 'changeData' function to change the neccessary information for the next round dialog.
*************************************************************************************************/
function mainControl()
{
  updateShowFrame("QUESTION: "+question+"<br/>");

  answer="";
  answerRecieved=false;
  isLink=false;
  sayGreetings=false;
  
  /* call submitReq() method to send the question to the CGI program. */
  submitReq("POST", cgiLink);
  /** Cannot recieve data from CGI interpreter. Network problem. **/
  if(answerRecieved==false)
    return "-1";
  answer = getAnswer(xmlhttp.responseText);
    
  /******* 
  Check whether the recieved answer is a link or not. 
  And, assign the result to the global variable isLink. 
  ******/
  checkAnswer(xmlhttp.responseText);
  
  /****** if the answer is not a link, then show the answer to the user and return. ******/
  if(!isLink)
  {
    gotoNext=false;
    updateShowFrame("RESPONSE: "+answer+"<br/>");
    return "1";
  } 

  nextPage=getNextInterpreter(xmlhttp.responseText);

  updateShowFrame("RESPONSE: "+answer+"<br/><br/>");
  
  if(gotoNext==true)
    window.location=nextPage;
  
  return "1";
}


/**********************************************************************************************
This function returns the substring that has to be spoken as a result of the user's question. 
Same procedure is applied for extracting the content to be spoken out.
**********************************************************************************************/
function getAnswer(answer)
{
  var ex=answer;
  var index;
  if((ex.indexOf('LINK=',0)) == -1)
    return ex;
  ex= ex.slice(5);
  index = ex.indexOf(";",0);
  ex = ex.substring(0,index);				
  return ex;
}


/********************************************************************************************
This function uses AJAX, it will submit the question to the given URI if it use a 'POST' method.
Or, it will retrieve data from the given URI if it use a 'GET' method.
*********************************************************************************************/
function submitReq(method, url)
{
  /***** Initialize AJAX XMLHttpRequest object. ****/
  xmlhttp=new XMLHttpRequest();
  /****** 
  Assign a event listener to the 'onreadystatechange' event.
  Different listerner assigned depends on a 'GET' or a 'POST' method. 
  ******/
  if(method=="GET")
    xmlhttp.onreadystatechange=stateChange_GET;
  else
    xmlhttp.onreadystatechange=stateChange_POST;


  /** Check whether the url involves a cross-domain security error before send the request. **/
  if(isCrossDomain(url)==true)
  {
    /** if method is 'GET', it means this function is called from loadPage() function to validate a user input URL. **/
    if(method=="GET")
      alert("Cannot validate input URL since it involves a cross-domain security issue. Load URL immediately.");
    /** 
    if method is not 'GET', which means 'POST' method, 
    it means this method is called from main control to submit a question query to the interpreter. 
    **/
    else
      updateShowFrame("SYSTEM ERROR: An error which against the web browser cross-domain security issue."
      			+" Your CGI interpreter has to be placed in the same domain with this voice page."+
                      "Please contact to your application provider to fix this problem. \n"
                      +"Your CGI interpreter location: "+ url+"   Current voice page host domain: "
                      +window.location.host);
    answerRecieved=true;
    answer="An error which against the web browser cross-domain security issue has occured. Please check the error message to continue.";
    return;
  }
    
  /** Open the connect, sychronized.  ***/
  xmlhttp.open(method,url,false);
  
  if(method=="GET")
    xmlhttp.send();
  else
  {
    xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xmlhttp.send("question="+question);
  }
}

function stateChange_POST()
{
  /******* if xmlhttp shows loaded  ******/
  if (xmlhttp.readyState==4)
  {
    if (xmlhttp.status==200 || xmlhttp.status==304)
    {
      answerRecieved = true;
    }
    else
    {
      answerRecieved = false;
      xmlhttp.responseText = "";
    }
  }
}

function stateChange_GET()
{
  /******* if xmlhttp shows loaded  ******/
  if (xmlhttp.readyState==4)
  {
    if (xmlhttp.status==200 || xmlhttp.status==304)
    {
      answerRecieved = true;
    }
    else
    {
      answerRecieved = false;
      xmlhttp.responseText = "";
    }
  }
}

/** Validate the given url with the current page domain(hostname), to see whether they are in the same domain or cross-domain(different domain). **/
function isCrossDomain(url)
{
  var domain = url;
  var i = domain.indexOf("//");
  if(i==-1)
    return false;
  
  domain = domain.slice(i+2);
  
  var k = domain.indexOf("/");
  if(k!=-1)
    domain = domain.slice(0, k);
  else
    return true;
  
  var host = window.location.hostname;
  
  if(host==domain)
    return false;
  else
    return true;
  
}

/********************************************************************************************************
Check whether there is a occurrence of '=' character in the answer, which means a link existed in it.
And, assign the result to the global variable 'isLink'.
*******************************************************************************************************/
function checkAnswer(answer)
{
  if((answer.indexOf('LINK=',0))== -1)
    isLink=false;
  else
    isLink=true;

  return isLink;
}

/****************************************************************************************************************
if the answer is a link, this function will return the next interpreter's URI as a string. Otherwise, return "-1".
******************************************************************************************************************/
function getNextInterpreter(answer)
{
  var loc;
  var ex = answer;
  var index;

/****** 
Check if the answer is a link to next speech application interpreter. 
It should never be evaluated as true, otherwise error. 
******/
  if(!isLink)
    return "-1";
    
/***********************************************************************************************************************
If the answer is a link, then its formation should be: "LINK=_answer;SIHLO=_location;".
e.g. Question send to judy.cgi: "can i talk to solar man".
     Answer recieved from judy.cgi: "LINK=yes. here he is;SIHLO=http://luna.cs.uwindsor.ca/~speechweb/p_d_speechweb/judy/judy.xml"
**********************************************************************************************************************/
/****** extracts the LINK=  substring from the string and assigns it to the variable ex ******/
    ex = ex.slice(5);
/****** gets the index position of ';'  ******/
   index = ex.indexOf(";",0);
   index = index+1;
/***************************************************************************************************************
The string after the '=' and upto ';' are eliminated because this is the content which is the answer-query of the user's input.
*******************************************************************************************************************/
   loc = ex.substr(index);
/**********************************************************************************************
eliminating 'SIHLO=' from the loc variable. 
SIHLO contains the server address starting right after '=' and ended by the delimiter ';'.
*************************************************************************************************/
   ex = loc.slice(6);
   index = ex.indexOf(";",0);
   loc = ex.substring(0,index);
   
   return loc;	
}

/** Update the text area in the HTML and show message on it. **/
function updateShowFrame(message)
{
  var objTable = document.getElementById("logFrame");

  objTable.insertRow(0);
  objTable.rows[0].insertCell(0);
  objTable.rows[0].insertCell(1);
  var cell0 = objTable.rows[0].cells[0];
  
  var cell1 = objTable.rows[0].cells[1];
  cell1.align="left";  
  cell0.align="left";
  cell0.width="105";
  if(message.indexOf("SYSTEM ERROR: ")!=-1)
  {
    var objFont = document.createElement("font");
    objFont.color="red";
    objFont.size="-1";
    objFont.appendChild(document.createElement("b"));
    objFont.firstChild.innerHTML = message.slice(0, message.indexOf(":")+1);
    cell0.appendChild(objFont);
    
    var objFont2 = document.createElement("font");
    var objIta = document.createElement("i");
    objFont2.color="black";
    objFont2.size="-1";
    objFont2.appendChild(objIta);
    cell1.appendChild(objFont2);
    objIta.innerHTML=message.slice(message.indexOf(":")+1);
  }else
  {
    var index = message.indexOf(":");
    var ex=message.slice(0,index+1);
    var objFont = document.createElement("font");
    if(ex.indexOf("QUESTION:")!=-1)
      objFont.color= "blue";
    else if(ex.indexOf("RESPONSE:")!=-1)
      objFont.color="green";
    else 
      objFont.color="purple";

    objFont.appendChild(document.createElement("b"));
    cell0.appendChild(objFont);
    objFont.firstChild.innerHTML = ex;
    cell1.appendChild(document.createElement("font"));
    cell1.firstChild.innerHTML = message.slice(index+1);
  }
  /****** Insert a table row as an empty line after a response and greeting message. ***********/
  if(message.indexOf("QUESTION")==-1)
  { 
    objTable.insertRow(0);
    objTable.rows[0].insertCell(0);
    objTable.rows[0].colspan="2";
    objTable.rows[0].cells[0].innerHTML = "<br/>&nbsp;";
  }  
  
  return "";
}

/** Load user's application. **/
function loadPage(checkInput)
{
  /** Get user's input. **/
  var loc = document.getElementById("id_nextPage").value;
  
  /** if user's input is empty, then return a error message. **/
  if(loc=="")
  {
    updateShowFrame("SYSTEM ERROR: Please input the URL to your voice page in the above text field. It can not be empty!" );
  }
  /** if user input is not empty, and user asked to validate URL before go. **/
  else if(checkInput==true)
  {
    submitReq("GET", loc);
    /** if the valicating process return a false as result, which means invalid URL. **/
    if(answerRecieved==false)
    {
      if(xmlhttp.status==404)
        updateShowFrame("SYSTEM ERROR: Unable to load your voice page. File does not exist at: "+ loc );
      else
        updateShowFrame("SYSTEM ERROR: Unable to load your voice page. Network problem, error code: "
        +xmlhttp.status+". Please check your internet connection.");
    }
    /** if user's input is not empty, it is a valid URL to next page. **/
    else
      window.location=loc;
  }  
  /** if user's input is not empty, and user asked to load URL page immediately. **/
  else
    window.location=loc;
}

function menuPage()
{
  window.location=startPage;
}

function processQuestion()
{
  gotoNext = true;
  question = document.getElementById("id_questionField").value;
  document.getElementById("id_questionField").value="";
  mainControl();
}

</script>

<!--*****************************************************
  The following script will only be run after a 'vxmldone' event is thrown after the VoiceXML form finish all its process.
  It also means that the answer returned from interpreter contains a link to next interpreter, so it needs to go there.
*********************************************************-->
<script type="text/javascript" id="gotoNextPage" declare="declare">
  window.location=nextPage;
</script>

<title>Public-Domain SpeechWeb</title>
</head>
<body id="page.body">
<center><h2>Welcome to our new voice browser!</h2></center>
<br/>
<center>
<table>
<tr><td colspan="6">Load your own speech application : 
<input type="text" id="id_nextPage" size="50" value="http://luna.cs.uwindsor.ca/~speechweb/p_d_speechweb/menu/demo_menu.xml"/>
<br/><br/></td></tr>
<!-- Call loadPage() function to setup the interprter and speech-grammar location according to the above input text field value; -->
<tr><td colspan="2"><input type="button" name="submitValidate" value="Validate Before Go" onclick="loadPage(true)"/></td>
<td colspan="2"><input type="button" name="submitGo" value="Go Immediately" onclick="loadPage(false)"/></td>
<td colspan="2"><input type="button" name="menuGo" value="SpeechWeb Menu Page" onclick="menuPage()"/></td></tr>
<tr><td colspan="6"><br/><br/><br/><b>Say your question or type it in here:</b></td></tr>
<tr><td colspan="6"><form onsubmit="processQuestion(); return false;">
<input type="text" size="70" name="questionField" id="id_questionField" value=""/></form></td></tr>
</table>
<br/><br/>
<table id="logFrame" width="600"></table><br/>
<br/><br/><br/>
</center>
</body>
<!-- Call a script to reload the vxml form when the current vxml form has done its process. -->
<ev:listener ev:observer="page.body" ev:event="vxmldone" ev:handler="#gotoNextPage" ev:propagate="stop" />
<!-- Load 'vxml_form' when the page.body loaded. -->
<ev:listener ev:observer="page.body" ev:event="load" ev:handler="#vxml_form" ev:propagate="stop" />
</html>
