/* Include file for the Cleverscript API. Created 27/2/2013 This function will generate a form for the user to type into and will get the bot's reply and show them on the screen, along with the reaction/emotion image. If jQuery is available it will fade between the reaction and emotion. The main ShowCleverscriptForm function has two arguments, the API key and a list of options. Only the API key is required. The form and subsequent chat are written into a div with a class of cleverscriptreply at the point where you call the function (unless you pass in the form_id option). You can have more than one bot in a single page. Use it like this: Alternatively, if you would like to create the form yourself, you can set it up like this, intially calling CleverscriptSetup to put in all the options including your callback function and CleverscriptInput to process in the user's input.
The bot can not start the conversation when used this way, instead call CleverscriptInput('') to get the first bot reply. If you have more than one bot on a page, call each with a number at the end: CleverscriptInput (this.value, 0). To pass in extra variables to the API pass an object or string as the 3rd argument to CleverscriptInput. The options and their default values are: -server: whether to use the "test" or "live" server (live) -you_label: text before the form input box (You) -you_size: size of the form input box (40) -submit_label: label for the submit button (Say it) -wating_message: waiting message that appears while waiting for API to respond (Waiting...) -timeout: timeout in milliseconds to wait for the API to reply (10000) -timeout_message: message to display if the API times out (No response, please try again.) -bot_start: whether the bot should start the conversation (yes) -form_id: put the form in this HTML id (or else adds to page in place) -reaction_image: whether to show reaction image: "none", "text" or "tone" (none) -reaction_default: default image to show when nothing returned (agreeable or 0) -reaction_prefix: path and prefix to the reaction image (blank) -reaction_postfix: postfix to the reaction image (blank) -emotion_image: whether to show emotion image: "none", "text" or "tone" (none) -emotion_default: default image to show when nothing returned (agreeable or 0) -emotion_prefix: path and prefix to the emotion image (blank) -emotion_postfix: postfix to the emotion image (blank) -image_width: width in pixels that images should be displayed at (80) -image_delay: delay in milliseconds before fading between reaction and emotion (1000) -image_fade: length of fade in ms (1000) -bot_label: label for the bot's reply (Bot) -you_capitalise: whether to capitalise the user's input automatically (yes) -you_punctuate: whether to add a full stop to the user's input automatically (yes) -interactions: how many interactions to show on the screen (0 = all) -text_id: show the full text conversation in this HTML id (nowhere) -text_count: only start showing it after this many interactions with display:block (0) -callback_before: Javascript function to call before outputting bot's response with data as arg (none) -callback_after: Javascript function to call before outputting bot's response with data as arg (none) -csstate: the initial Cleverscript variable to restart a session (blank) -avatar: name of avatar SWF file (none, costs more credits to use this) -avatar_id: put the avatar into this div (or else adds to page in place) -avatar_size: width and height of the avatar in pixels (478) -tts_voice: voice to use for TTS (serena) -asr_prefix: prefix for the microphone images if Google ASR is wanted (empty) -debug: pass this in to see an alert with all the variables, 0=none, 1=data, 2=data+options, 3=request, 4=show POST form/iframe, 5=force POST (0) Images: You must have images for all the reactions and/or emotions (as listed in the manual) or for the tones (from -2 to 2) in a directory on your server. The image name is formed from the prefix, reaction/emotion full/tone and postfix. For example: {reaction_image: 'tone', reaction_prefix:'/images/reaction', reaction_postfix: '.jpg'} Would create an image like: /images/reacdtion-2.jpg if the reaction_tone was -2. Classes used with some default styles applied: form.cleverscriptform form.cleverscriptform input.cleverscriptinput form.cleverscriptform input.cleverscriptsubmit div.cleverscriptreply div.cleverscriptreply div.cleverscriptimages {float:left; padding-right:10px; width: #px;} div.cleverscriptreply div.cleverscriptimages img.cleverscriptreaction div.cleverscriptreply div.cleverscriptimages img.cleverscriptemotion {position:absolute; width: #px;} div.cleverscriptreply div.cleverscriptdivider {clear:both; padding-top: 15px;} */ /////////////////////////// ShowCleverscriptForm /////////////////////////// var CSBOTS = new Array(); //an array to store all the bots on the apge var CSSWFOBJECT = null; //will store the SWF object if needed var CSASROBJECT = null; //for Google speech recognition function CleverscriptSetup (apikey, options) { //added 1/8/2013 to separate form from options ////////////////// options for the object ////////////////// if (!options) options = new Object(); options.apikey = apikey; if (!options.server) options.server = 'live'; //live server by default if (!options.you_label) options.you_label = 'You'; if (!options.you_size) options.you_size = 40; if (!options.submit_label) options.submit_label = 'Say it'; if (!options.waiting_message) options.waiting_message = 'Waiting...'; options.waiting = 0; //are we waiting for a response if (!options.timeout) options.timeout = 10000; if (!options.timeout_message) options.timeout_message = 'No response, please try again.'; if (options.bot_start != 'no') options.bot_start = 'yes'; if (!options.form_id) options.form_id = ''; if (!options.reaction_image) options.reaction_image = 'none'; if (!options.reaction_default) options.reaction_default = options.reaction_image=='tone' ? '0' : 'agreeable'; if (!options.reaction_prefix) options.reaction_prefix = ''; if (!options.reaction_postfix) options.reaction_postfix = ''; if (!options.emotion_image) options.emotion_image = 'none'; if (!options.emotion_default) options.emotion_default = options.emotion_image=='tone' ? '0' : 'agreeable'; if (!options.emotion_prefix) options.emotion_prefix = ''; if (!options.emotion_postfix) options.emotion_postfix = ''; if (!options.image_width) options.image_width = 80; if (!options.image_delay) options.image_delay = 1000; if (!options.image_fade) options.image_fade = 1000; if (!options.bot_label) options.bot_label = 'Bot'; if (options.you_capitalise != 'no') options.you_capitalise = 'yes'; if (options.you_punctuate != 'no') options.you_punctuate = 'yes'; if (!options.interactions) options.interactions = 0; if (!options.text_id) options.text_id = ''; if (!options.text_count) options.text_count = 0; if (!options.callback_before) options.callback_before = ''; if (!options.callback_after) options.callback_after = ''; if (!options.csstate) options.csstate = ''; //the initial cleverscript state if (!options.avatar) options.avatar = ''; if (options.avatar) FillSwfObject(); //include the code for a SWF object which we will need if (!options.avatar_id) options.avatar_id = ''; if (!options.avatar_size) options.avatar_size = 478; //default size if (!options.tts_voice) options.tts_voice = 'serena'; if (!options.debug) options.debug = 0; if (!options.asr_prefix) options.asr_prefix = false; if (!options.extra_variables) options.extra_variables = ''; //any extra variables to be passed in, now passed directly into CleverscriptInput options.history = new Array(); //history of interactions options.tts_url = ''; //the waiting URL for TTS var botnum = CSBOTS.length; //counter for this bot ////////////////// create a wrapper for the callback ////////////////// //This makes CleverscriptCallBack0 just another way to call CleverscriptCallBack allowing for more than one of these forms on a page. window['CleverscriptCallBack'+botnum] = new Function ('data', 'CleverscriptCallBack(data,' + botnum + ')'); CSBOTS.push (options); return botnum; //return the counter for this bot } function CleverscriptSetState (cs, botnum) {var options = CSBOTS[botnum ? botnum : 0]; if (options) options.csstate = cs;} /////////////////////////// Setup the bot with a form /////////////////////////// function ShowCleverscriptForm (apikey, options) { botnum = CleverscriptSetup (apikey, options); //set up all the options options = CSBOTS[botnum]; //get the options back ////////////////// create the avatar ////////////////// if (options.avatar) { //added to here 27/3/2014 var dr = ''; var el = false; if (options.avatar_id) el = document.getElementById (options.avatar_id); //where to add the avatar if (el) el.innerHTML = dr; else document.write (dr); //add to the page if (CSSWFOBJECT.hasFlashPlayerVersion("8.0.0")) { var flashvars = {"exp":options.avatar.replace('.swf','.xml'),"positionX":"-1","positionY":"0","avatarNumber":"1"}; var params = {allowScriptAccess:'always', swliveconnect:'true', wmode:'transparent'}; var attributes = {id:'csavatarswf'+botnum, name:'csavatarswf'+botnum, wmode:'transparent'}; CSSWFOBJECT.embedSWF (options.avatar, 'csavatar'+botnum, options.avatar_size, options.avatar_size, "8.0.0", "", flashvars, params, attributes); } } ////////////////// enable speech recognition if requested ////////////////// //Set up the Google ASR object which will listen for the input for a bot. //Mostly copied from cleverbot library. if (options.asr_prefix && !CSASROBJECT && typeof webkitSpeechRecognition != 'undefined') { CSASROBJECT = new webkitSpeechRecognition(); //added by PAUL CSASROBJECT.mystate = 0; CSASROBJECT.botnum = -1; //which bot num we are listening for CSASROBJECT.request = function (botnum) { //initiates an ASR request, this function is called by the Javascript if (CSASROBJECT.mystate==2) return; //not if we are currently submitting a request or doing TTS CSASROBJECT.botnum = botnum; //the bot num we are listening for CSASROBJECT.mystate = 1; //we have made the request setTimeout (function() {if (CSASROBJECT.mystate==1) alert ('Please Allow this website to use your microphone.')}, 7000); document.getElementById('csmic' + CSASROBJECT.botnum).src = options.asr_prefix + 'microphone-request.png'; CSASROBJECT.start(); }; CSASROBJECT.onstart = function() { //called by ASR when it has started recording, this automatically times out after a few seconds CSASROBJECT.mystate=2; //set the mystate to 2 for starting to record document.getElementById('csmic' + CSASROBJECT.botnum).src = options.asr_prefix + 'microphone-recording.png'; }; CSASROBJECT.onresult = function(event) { //called by ASR when successfully complete var s = document.getElementById('csinput' + CSASROBJECT.botnum); //get the input element if (event && event.results && event.results[0] && event.results[0][0] && event.results[0][0].transcript) { s.value = event.results[0][0].transcript; CleverscriptInput (s.value, CSASROBJECT.botnum); //s.parentNode.submit(); } CSASROBJECT.mystate=3; //completed ASR, after this onend is called to change the microphone back to normal }; CSASROBJECT.onend = function() { //called if there was an error (no input) or after recording has completed if (CSASROBJECT.mystate==0 || CSASROBJECT.mystate==2) alert ("Sorry, that didn't work. Please check your microphone or speak louder."); document.getElementById('csmic' + CSASROBJECT.botnum).src = options.asr_prefix + 'microphone-ready.png'; CSASROBJECT.mystate = 0; //set back to ready state }; } ////////////////// create the bot form ////////////////// var r = ''; //a variable for the HTML r += ''; r += ''; var el = false; //used below if (options.form_id) el = document.getElementById (options.form_id); //where to add the form if (el) el.innerHTML = r; else document.write (r); //add to the page if (options.bot_start=='yes') CleverscriptInput ('', botnum, options.extra_variables); //make the bot start the conversation, pass in extra variables } /////////////////////////// If you want to pass in extra variables /////////////////////////// //Commented 7/11/2014 as they are now sent in to CleverscriptInput //function CleverscriptExtraVariables (vars, botnum) { // if (!botnum) botnum = 0; // var options = CSBOTS[botnum]; // var r=''; //this will be used for the extra variables // if (typeof vars == 'object') for (var p in vars) r += p + '=' + encodeURIComponent (vars[p]) + '&'; // else if (typeof vars == 'string') r = vars; // options.extra_variables = r; //} /////////////////////////// Called whenever there is form input /////////////////////////// function CleverscriptInput (inputel, botnum, extravars) { if (!botnum) botnum = 0; var options = CSBOTS[botnum]; if (options.waiting) return; //we are currently waiting for a reply var csinput = ''; //the user's input if (typeof inputel == 'string') csinput = inputel; //we were passed an input else if (inputel) {csinput = inputel.value; inputel.value = options.waiting_message;} //show the loading message var url = ''; //protocol added below so that it could be https var protocol = window.location && window.location.protocol=='https:' ? 'https:' : 'http:'; //added 29/5/2015 if (options.server=='test') url += protocol + '//testapi.cleverscript.com/csapi'; //test server else if (options.server=='live') url += protocol + '//api.cleverscript.com/csapi'; //live server else url += options.server; //use server that was passed in if (url.indexOf ('http') < 0) url = protocol + '//' + url; //prefix by http:// if it doesn't already have it //url += '/csapi'; //added directly to test/live so that it doesn't have to be part of a passed in server var params = new Object(); params.key = options.apikey; csinput = csinput.replace (/<[^>]+>/g, ''); //replace any HTML in the input 4/8/2015 params.input = encodeURIComponent (csinput); params.cs = options.csstate; params.callback = 'CleverscriptCallBack' + botnum; if (options.avatar && options.tts_voice) params.ttsvoice = options.tts_voice; //request TTS as well var geturl = url + '?'; for (var p in params) geturl += p + '=' + params[p] + '&'; rex = ''; //extra variables if (typeof extravars == 'object') for (var p in extravars) rex += p + '=' + encodeURIComponent (vars[p]) + '&'; else if (typeof extravars == 'string') rex = extravars; else if (typeof extravars == 'undefined') rex = ''; geturl += rex; //parse the extra variables here from 7/11/2014 //geturl += options.extra_variables; //extra variables added 1/8/2013 //From 7/11/2014 since long URLs cause me such problems for my own web server, I'll only limit them to 2083 in IE and more in other browsers var isie = (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0); //is this ie var maxurl = isie ? 2048 : 20000; //http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers if (options.debug >= 5) maxurl = 0; //force the browser to do a POST if (options.debug >= 3) alert ("About to " + (geturl.length >= maxurl && window.postMessage ? "POST" : "GET") + " " + geturl.length + " characters: " + geturl); var appendel = document.getElementById('csreply'+botnum); if (!appendel) appendel = document.body; //use the document body //Whether to do a POST message or a GET. POST is done for really long inputs on newer browsers as IE has a 2048 character limit. Added 1/8/2013 if (geturl.length >= maxurl && window.postMessage) { //window.postMessage is only supported on newer browsers var csframename = 'CleverscriptPostFrame'; //the name of the cleverscript frame var csformname = 'CleverscriptPostForm'; //the name of the form var myframe = window.frames ? window.frames[csframename] : 0; //look up this iframe if (!myframe) { //make the iframe myframe = document.createElement ("iframe"); if (options.debug >= 4) {myframe.style.width = "500px"; myframe.style.height = "100px"; myframe.style.background = "yellow";} //show the iframe 7/11/2014 else myframe.style.display = "none"; appendel.appendChild (myframe); myframe.contentWindow.name = csframename; } var myform = document.forms ? document.forms[csformname] : 0; //look up my form params.input = csinput; //so that it does not get double encoded, as it is going into a form and has already been encoded 6/11/2014 if (myform) {for (var p in params) myform.elements[p].value = params[p];} //set the parameters else { //make the form myform = document.createElement ("form"); myform.name = csformname; myform.target = csframename; myform.action = url; //the URL to POST to myform.method = "POST"; appendel.appendChild (myform); for (var p in params) { var hiddenField = document.createElement ("input"); hiddenField.setAttribute ("type", options.debug >= 4 ? "text" : "hidden"); //show the form elements hiddenField.setAttribute ("name", p); hiddenField.setAttribute ("value", params[p]); //must be done here and not separately for IE8 from 19/8/2013 myform.appendChild (hiddenField); } } //alert ('Submitting form ' + myform.outerHTML); myform.submit(); } else { //a get request via JSONP var el = document.createElement ('script'); el.setAttribute ('action', 'text/javascript'); el.setAttribute ('src', geturl); appendel.appendChild (el); } CSBOTS[botnum].waiting = options.history.length + 1; //we are currently waiting if (document.getElementById('csinput'+botnum)) setTimeout ('CleverscriptTimeout(' + botnum + ',' + options.history.length + ')', options.timeout); } function CleverscriptTimeout (botnum, ic) { var options = CSBOTS[botnum]; if (options.waiting != ic + 1) return; //already dealt with document.getElementById('csinput'+botnum).value = options.timeout_message; CSBOTS[botnum].waiting = 0; } /////////////////////////// Called to process the return from the form /////////////////////////// function CleverscriptCallBack (data, botnum) { ////////////////// set up some variables ////////////////// if (!botnum) botnum = 0; var options = CSBOTS[botnum]; if (!data || !options) return; //no data or no bot options var ic = data.interaction_count; if (!ic) ic = options.history.length; if (options.callback_before && typeof (options.callback_before) == 'function') options.callback_before (data); ////////////////// show debug alert ////////////////// if (options.debug >= 1) CleverscriptShowObject ('BOT ' + botnum + ' DATA', data, 1); if (options.debug >= 2) CleverscriptShowObject ('BOT ' + botnum + ' OPTIONS', options, 1); ////////////////// deal with reaction ////////////////// var reaction='', reactionimage=''; if (options.reaction_image != 'none') { reactionimage = options.reaction_prefix; reaction = options.reaction_image=='tone' ? data.reaction_tone : data.reaction; if (!reaction) reaction = options.reaction_default; reactionimage += reaction.replace('-', '').replace(' ', '_'); reactionimage += options.reaction_postfix; } ////////////////// deal with emotion ////////////////// var emotion='', emotionimage=''; if (options.emotion_image != 'none') { emotionimage = options.emotion_prefix; emotion = options.emotion_image=='tone' ? data.emotion_tone : data.emotion; if (!emotion) emotion = options.emotion_default; emotionimage += emotion.replace('-', '').replace(' ', '_'); emotionimage += options.emotion_postfix; } ////////////////// form the text ////////////////// var reply = ''; if (data.input) { if (options.you_punctuate) {var lc = data.input.substr(-1); lc = (lc == '.' || lc == '?' || lc == '!') ? '' : '.'; data.input += lc;} if (options.you_capitalise) data.input = data.input.substr(0,1).toUpperCase() + data.input.substr(1); reply += options.you_label + ': ' + data.input + '