1 /******************************************************************************
  2  *
  3  *  Copyright 2014-2017 Paphus Solutions Inc.
  4  *
  5  *  Licensed under the Eclipse Public License, Version 1.0 (the "License");
  6  *  you may not use this file except in compliance with the License.
  7  *  You may obtain a copy of the License at
  8  *
  9  *      http://www.eclipse.org/legal/epl-v10.html
 10  *
 11  *  Unless required by applicable law or agreed to in writing, software
 12  *  distributed under the License is distributed on an "AS IS" BASIS,
 13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  *  See the License for the specific language governing permissions and
 15  *  limitations under the License.
 16  *
 17  ******************************************************************************/
 18 
 19 /**
 20  * Bot Libre open SDK.
 21  * This JavaScript SDK lets you access chat bot, live chat, chatroom, forum, script, graphic, user services on
 22  * the Bot Libre compatible websites, including:
 23  * - Bot Libre!
 24  * - Bot Libre for Business
 25  * - Live Chat libre!
 26  * - Forums libre!
 27  * 
 28  * This JavaScript script can be used directly, or copied/modified on your own website.
 29  * 
 30  * The SDK consist of two main class, SDKConnection and LiveChatConnection.
 31  * 
 32  * SDKConnection uses AJAX calls to provide access to the libre REST API.
 33  * This is used for chat bots, forums, user admin, and domains.
 34  * 
 35  * LiveChatConnection uses web sockets to provide access to live chat and chatrooms.
 36  * 
 37  * Version: 6.0.0-2017-10-16
 38  */
 39 
 40 /**
 41  * Static class for common util functions and static properties.
 42  * @class
 43  */
 44 var SDK = {};
 45 
 46 SDK.DOMAIN = "www.botlibre.com";
 47 SDK.NAME = "Bot Libre!";
 48 SDK.APP = "";
 49 
 50 SDK.DOMAIN = window.location.host;
 51 SDK.APP = "/botlibre";
 52 
 53 SDK.PATH = "/rest/api";
 54 SDK.MAX_FILE_UPLOAD = 5000000;
 55 
 56 SDK.host = SDK.DOMAIN;
 57 SDK.app = SDK.APP;
 58 SDK.scheme = 'https:' == document.location.protocol ? "https" : "http";
 59 SDK.url = SDK.scheme + "://" + SDK.DOMAIN + SDK.APP;
 60 SDK.rest = SDK.url + SDK.PATH;
 61 SDK.backlinkURL = SDK.url;
 62 SDK.backlink = true;
 63 SDK.commands = true;
 64 
 65 /**
 66  * You must set your application ID to use the SDK.
 67  * You can obtain your application ID from your user page.
 68  * @static
 69  */
 70 SDK.applicationId = null;
 71 
 72 /**
 73  * Set the active language code.
 74  * This is used for voice recognition.
 75  */
 76 SDK.lang = "en";
 77 
 78 /**
 79  * Enable debug logging.
 80  * @static
 81  */
 82 SDK.debug = false;
 83 
 84 /**
 85  * Escape and filter bot messages for HTML and JavaScript content for XSS security.
 86  * This prevents bots sending advanced HTML and JavaScript in their messages.
 87  * Set this to false to allow your bot to send JavaScript.
 88  * @static
 89  */
 90 SDK.secure = true;
 91 
 92 /**
 93  * Force avatars to enable or disable canvas for video (currently used only for Chrome and Firefox).
 94  * @static
 95  */
 96 SDK.useCanvas = null;
 97 
 98 /**
 99  * Force avatars to enable or disable video (currently disabled for Safari on iPhone).
100  * @static
101  */
102 SDK.useVideo = null;
103 
104 /**
105  * Attempt to fix grey mp4 video background (only used for Chrome).
106  * @static
107  */
108 SDK.fixBrightness = null;
109 
110 /**
111  * Attempt to fix an issue with Chrome not processing the CSS after correctly when the chat bubble resizes.
112  * @static
113  */
114 SDK.fixChromeResizeCSS = true;
115 
116 /**
117  * Set the error static field to trap or log any errors.
118  */
119 SDK.error = function(message) {
120 	console.log(message);
121 }
122 
123 /**
124  * Allow our native speech API to use the third party ResponsiveVoice API.
125  * You must create an account with ResponsiveVoice to use their API, see https://responsivevoice.com
126  * @static
127  */
128 SDK.responsiveVoice = false;
129 SDK.speechSynthesis = 'speechSynthesis' in window;
130 /**
131  * The speechRate can be set to change the native speech voice speed.
132  * It can range between 0.1 (lowest) and 10.0 (highest).
133  * 1.0 is the default rate for the current platform or voice.
134  * Other values act as a percentage relative to this, so for example 2.0 is twice as fast, 0.5 is half as fast.
135  */
136 SDK.speechRate = null;
137 /**
138  * The speechPitch can be set to change the native speech voice pitch.
139  * It can range between 0.0 (lowest) and 2.0 (highest), with 1.0 being the default pitch for the current platform or voice.
140  */
141 SDK.speechRate = null;
142 SDK.initResponsiveVoice = function() {
143 	if (!('responsiveVoice' in window)) {
144 		console.log("ResponsiveVoice missing, you must load its script first");
145 		return;
146 	}
147 	SDK.responsiveVoice = true;
148 	SDK.speechSynthesis = true;
149 }
150 if (!('SpeechSynthesisUtterance' in window)) {
151 	function SpeechSynthesisUtterance2(text) {
152 		this.text = text;
153 	}
154 }
155 
156 SDK.currentAudio = null;
157 SDK.recognition = null;
158 SDK.recognitionActive = false;
159 SDK.backgroundAudio = null;
160 SDK.currentBackgroundAudio = null;
161 SDK.timers = {};
162 /**
163  * Track if auto play of media is enabled in the browser (mobile Chrome/Safari)
164  * Enable or disable to force audio auto play.
165  */
166 SDK.canPlayAudio = null;
167 /**
168  * Track if auto play of media is enabled in the browser (mobile Chrome/Safari)
169  * Enable or disable to force video auto play.
170  */
171 SDK.canPlayVideo = true;
172 SDK.audio = null;
173 SDK.autoPlayActionAudio = null;
174 SDK.autoPlayBackgroundAudio = null;
175 SDK.autoPlayDelay = 2000;
176 /**
177  * Play the audio file given the url.
178  */
179 SDK.play = function(file, channelaudio) {
180 	SDK.pauseSpeechRecognition();
181 	var audio = null;
182 	if (SDK.audio != null) {
183 		audio = SDK.audio;
184 		audio.pause();
185 		audio.onended = null;
186 		audio.onpause = null;
187 		audio.oncanplay = null;
188 		audio.src = file;
189 	} else {
190 		audio = new Audio(file);
191 	}
192 	if (SDK.recognitionActive) {
193 		audio.onended = function() {
194 			SDK.startSpeechRecognition();
195 			if (channelaudio != false) {
196 				SDK.currentAudio = null;
197 			}
198 		};
199 	} else if (channelaudio != false) {
200 		audio.onended = function() {
201 			SDK.currentAudio = null;
202 		};
203 	}
204 	if (SDK.canPlayAudio != true) {
205 		if (!SDK.isMobile()) {
206 			SDK.canPlayAudio = true;
207 		} else {
208 			audio.onplaying = function() {
209 				SDK.canPlayAudio = true;
210 			};
211 		}
212 	}
213 	if (channelaudio == false) {
214 		audio.play();
215 	} else {
216 		if (SDK.currentAudio != null && !SDK.currentAudio.ended && !SDK.currentAudio.paused) {
217 			SDK.currentAudio.onpause = function() {
218 				SDK.currentAudio = audio;
219 				audio.play();
220 			};
221 			SDK.currentAudio.pause();
222 		} else {
223 			SDK.currentAudio = audio;
224 			audio.play();
225 		}
226 	}
227 	if (SDK.canPlayAudio == null) {
228 		SDK.canPlayAudio = false;
229 		/**
230 		 * This allows the audio to be initialized inside a button callback.
231 		 * This is require to auto play on mobile Chrome and Safari.
232 		 */
233 		setTimeout(function() {
234 			if (SDK.canPlayAudio == false) {
235 				SDK.initAudio = function() {
236 					SDK.audio = new Audio(file);
237 					SDK.currentAudio = SDK.audio;
238 					SDK.currentAudio.onended = function() {
239 						SDK.currentAudio = null;
240 					};
241 					SDK.canPlayAudio = true;
242 					SDK.audio.play();
243 					document.getElementById("sdkplaybutton").style.display = "none";
244 				}
245 				var body = document.body || document.getElementsByTagName('body')[0];
246 				var playButton = document.createElement('div');
247 				var html = "<div id='sdkplaybutton' style='position:fixed;bottom:32px;left:32px;z-index:164;'><img onclick='SDK.initAudio()' width='64' src='"
248 					+ SDK.url + "/images/playsound.png'/></div>"
249 				playButton.innerHTML = html;
250 				body.appendChild(playButton);
251 				setTimeout(function() {
252 					document.getElementById("sdkplaybutton").style.display = "none";
253 				}, 10000);
254 			}
255 		}, SDK.autoPlayDelay);
256 	}
257 	return audio;
258 }
259 
260 SDK.playChime = true;
261 /**
262  * Play the chime sound.
263  */
264 SDK.chime = function() {
265 	if (SDK.playChime) {
266 		this.play(SDK.url + '/chime.mp3');
267 		SDK.playChime = false;
268 		var timer = setInterval(function () {
269 			SDK.playChime = true;
270 			clearInterval(timer);
271 		}, 1000);
272 	}
273 }
274 
275 /**
276  * Convert the text to speech and play it either using the browser native TTS support, or as server generated an audio file.
277  * The voice is optional and can be any voice supported by the server (see the voice page for a list of voices).
278  * For native voices a language code can be given.
279  * If the browser supports TTS the native voice will be used by default.
280  */
281 SDK.tts = function(text, voice, native, lang, nativeVoice, mod) {
282 	try {
283 		if ((native || (native == null && voice == null)) && SDK.speechSynthesis) {
284 			var utterance = null;
285 			if ('SpeechSynthesisUtterance' in window) {
286 				utterance = new SpeechSynthesisUtterance(text);
287 			} else {
288 				utterance = new SpeechSynthesisUtterance2(text);
289 			}
290 			SDK.nativeTTS(utterance, lang, nativeVoice);
291 		} else {		
292 			var url = SDK.rest + '/form-speak?&text=';
293 			url = url + encodeURIComponent(text);
294 			if (voice != null) {
295 				url = url + '&voice=' + voice;
296 			}
297 			if (mod != null) {
298 				url = url + '&mod=' + mod;
299 			}
300 			if (SDK.applicationId != null) {
301 				url = url + '&application=' + SDK.applicationId;
302 			}
303 	
304 			var request = new XMLHttpRequest();
305 			var self = this;
306 			request.onreadystatechange = function() {
307 				if (request.readyState != 4) return;
308 				if (request.status != 200) {
309 					console.log('Error: Speech web request failed');
310 					return;
311 				}
312 				self.play(SDK.url + "/" + request.responseText);
313 			}
314 			
315 			request.open('GET', url, true);
316 			request.send();
317 		}
318 	} catch (error) {
319 		console.log('Error: Speech web request failed');
320 	}
321 }
322 
323 /**
324  * Use the ResponsiveVoice API.
325  */
326 SDK.responsiveVoiceTTS = function(utterance, lang, voice) {
327 	var events = {};
328 	try {
329 		SDK.pauseSpeechRecognition();
330 		if (voice == null || voice == "") {
331 			voice = "US English Female";
332 		}
333 		if (SDK.recognitionActive) {
334 			events.onend = function() {
335 				SDK.startSpeechRecognition();
336 			}
337 		}
338 		if (utterance.onend != null) {
339 			events.onend = utterance.onend;
340 		}
341 		if (utterance.onstart != null) {
342 			events.onstart = utterance.onstart;
343 		}
344 		responsiveVoice.speak(utterance.text, voice, events);
345 	} catch (error) {
346 		console.log(error);
347 	}
348 }
349 
350 /**
351  * Speak the native utterance first setting the voice and language.
352  */
353 SDK.nativeTTS = function(utterance, lang, voice) {
354 	if (SDK.speechRate != null) {
355 		utterance.rate = SDK.speechRate;
356 	}
357 	if (SDK.speechPitch != null) {
358 		utterance.pitch = SDK.speechPitch;
359 	}
360 	if (SDK.responsiveVoice) {
361 		SDK.responsiveVoiceTTS(utterance, lang, voice);
362 		return;
363 	}
364 	if (lang == null) {
365 		lang = SDK.lang;
366 	}
367 	SDK.pauseSpeechRecognition();
368 	if (SDK.recognitionActive) {
369 		utterance.addEventListener("end", function() {
370 			SDK.startSpeechRecognition();
371 		});
372 	}
373 	speechSynthesis.cancel();
374 	if (lang == null && voice == null) {
375 		// Events don't always get fired unless this is done...
376 		setTimeout(function() {
377 			speechSynthesis.speak(utterance);
378 		}, 100);
379 		return;
380 	}
381 	var voices = speechSynthesis.getVoices();
382 	var foundVoice = null;
383 	var foundLang = null;
384 	var spoken = false;
385 	if (voices.length == 0) {
386 		speechSynthesis.onvoiceschanged = function() {
387 			if (spoken) {
388 				return;
389 			}
390 			voices = speechSynthesis.getVoices();
391 	    	for (i = 0; i < voices.length; i++) {
392 	    		if (voice != null && (voice.length != 0) && voices[i].name.toLowerCase().indexOf(voice.toLowerCase()) != -1) {
393 	    			if (foundVoice == null || voices[i].name == voice) {
394 		    			foundVoice = voices[i];	    				
395 	    			}
396 	    		} else if (lang != null && (lang.length != 0) && voices[i].lang.toLowerCase().indexOf(lang.toLowerCase()) != -1) {
397 	    			if (foundLang == null || voices[i].lang == lang) {
398 	    				foundLang = voices[i];	    				
399 	    			}
400 	    		}
401 	    	}
402 	    	if (foundVoice != null) {
403 	    		utterance.voice = foundVoice;
404 	    	} else if (foundLang != null) {
405 	    		utterance.voice = foundLang;	    		
406 	    	}
407 	    	spoken = true;
408 			setTimeout(function() {
409 				speechSynthesis.speak(utterance);
410 			},100);
411 	    };
412 	} else {
413     	for (i = 0; i < voices.length; i++) {
414     		if (voice != null && (voice.length != 0) && voices[i].name.toLowerCase().indexOf(voice.toLowerCase()) != -1) {
415     			if (foundVoice == null || voices[i].name == voice) {
416 	    			foundVoice = voices[i];	    				
417     			}
418     		} else if (lang != null && (lang.length != 0) && voices[i].lang.toLowerCase().indexOf(lang.toLowerCase()) != -1) {
419     			if (foundLang == null || voices[i].lang == lang) {
420     				foundLang = voices[i];	    				
421     			}
422     		}
423     	}
424     	if (foundVoice != null) {
425     		utterance.voice = foundVoice;
426     	} else if (foundLang != null) {
427     		utterance.voice = foundLang;	    		
428     	}
429 		setTimeout(function() {
430 			speechSynthesis.speak(utterance);
431 		},100);
432 	}
433 }
434 
435 /**
436  * Allow text to be translated into another language is the interface elements.
437  */
438 SDK.translator = null;
439 SDK.translate = function(text) {
440 	if (SDK.translator == null) {
441 		SDK.translator = SDK.translators[SDK.lang];
442 		if (SDK.translator == null) {
443 			SDK.translator = {};
444 		}
445 	}
446 	var translated = SDK.translator[text];
447 	if (translated != null) {
448 		return translated;
449 	}
450 	return text
451 }
452 SDK.translators = {
453 	"pt" : {
454 		"Name" : "Nome",
455 		"Phone" : "Telemóvel",
456 		"Connect" : "Ligar"
457 	}
458 }
459 
460 /**
461  * Detect Chrome browser.
462  */
463 SDK.isChrome = function() {
464 	var agent = navigator.userAgent.toLowerCase()
465 	return agent.indexOf('chrome') != -1 && agent.indexOf('edge') == -1;
466 }
467 
468 /**
469  * Detect Firefox browser.
470  */
471 SDK.isFirefox = function() {
472 	return navigator.userAgent.toLowerCase().indexOf('firefox') != -1;
473 }
474 
475 /**
476  * Detect Safari browser.
477  */
478 SDK.isSafari = function() {
479 	return navigator.userAgent.toLowerCase().indexOf('safari') != -1;
480 }
481 
482 /**
483  * Detect mobile browser.
484  */
485 SDK.isMobile = function() {
486 	if (navigator.userAgent.match(/Android/i)
487 		 || navigator.userAgent.match(/webOS/i)
488 		 || navigator.userAgent.match(/iPhone/i)
489 		 || navigator.userAgent.match(/iPad/i)
490 		 || navigator.userAgent.match(/iPod/i)
491 		 || navigator.userAgent.match(/BlackBerry/i)
492 		 || navigator.userAgent.match(/Windows Phone/i)) {
493 		return true;
494 	} else {
495 		return false;
496 	}
497 }
498 
499 /**
500  * Detect iPhone OS.
501  */
502 SDK.isIPhone = function() {
503 	if (navigator.userAgent.match(/iPhone/i)) {
504 		return true;
505 	}
506 	return false;
507 }
508 
509 /**
510  * Detect Mac OS.
511  */
512 SDK.isMac = function() {
513 	return navigator.platform.toLowerCase().indexOf('mac') != -1;
514 }
515 
516 SDK.hd = false;
517 SDK.format = (SDK.isChrome() || SDK.isFirefox()) ? "webm" : "mp4";
518 // Safari displays HTML5 video very poorly on iPhone.
519 if (SDK.isSafari() && SDK.isIPhone()) {
520 	SDK.format = "img";
521 }
522 
523 /**
524  * Insert the text into the input field.
525  */
526 SDK.insertAtCaret = function(element, text) {
527     if (document.selection) {
528         element.focus();
529         var sel = document.selection.createRange();
530         sel.text = text;
531         element.focus();
532     } else if (element.selectionStart || element.selectionStart == 0) {
533         var startPos = element.selectionStart;
534         var endPos = element.selectionEnd;
535         var scrollTop = element.scrollTop;
536         element.value = element.value.substring(0, startPos) + text + element.value.substring(endPos, element.value.length);
537         element.focus();
538         element.selectionStart = startPos + text.length;
539         element.selectionEnd = startPos + text.length;
540         element.scrollTop = scrollTop;
541     } else {
542         element.value += text;
543         element.focus();
544     }
545 }
546 
547 /**
548  * Fix innerHTML for IE and Safari.
549  */
550 SDK.innerHTML = function(element) {
551 	var html = element.innerHTML;
552 	if (html == null) {
553 		var serializer = new XMLSerializer();
554 		html = "";
555 		for (var index = 0; index < element.childNodes.length; index++) {
556 			html = html + serializer.serializeToString(element.childNodes[index]);
557 		}
558 	}
559 	var index = html.indexOf("<");
560 	var index2 = html.indexOf(">")
561 	if (index != -1 && index2 > index) {
562 		html = html.replace(/</g, "<");
563 		html = html.replace(/>/g, ">");
564 	}
565 	if (html.indexOf("&") != -1) {
566 		html = html.replace(/&/g, "&");
567 	}
568 	return html;
569 }
570 
571 /**
572  * Strip HTML tags from text.
573  * Return plain text.
574  */
575 SDK.stripTags = function(html) {
576 	var element = document.createElement("p");
577 	element.innerHTML = html;
578 	SDK.removeTags(element);
579 	return element.innerText || element.textContent;
580 }
581 
582 SDK.removeTags = function(node) {
583 	if (node.className == 'nospeech' || node.tagName == 'SCRIPT' || node.tagName == 'SELECT' || node.tagName == 'BUTTON' || node.tagName == 'OPTION') {
584 		node.parentNode.removeChild(node);
585 	} else {
586 		var index = 0;
587 		var childNodes = node.childNodes;
588 		var children = [];
589 		while (index < childNodes.length) {
590 			children[index] = childNodes[index];
591 			index++;
592 		}
593 		var index = 0;
594 		while (index < children.length) {
595 			SDK.removeTags(children[index]);
596 			index++;
597 		}
598 	}
599 	return node;
600 }
601 
602 /**
603  * Replace reserved HTML character with their HTML escape codes.
604  */
605 SDK.escapeHTML = function(html) {
606 	return html.replace(/&/g, "&")
607     	.replace(/</g, "<")
608     	.replace(/>/g, ">")
609     	.replace(/"/g, """)
610     	.replace(/'/g, "'");
611 }
612 
613 /**
614  * Replace URL and email references in the text with HTML links.
615  */
616 SDK.linkURLs = function(text) {
617 	var http = text.indexOf("http") != -1;
618 	var www = text.indexOf("www.") != -1;
619 	var email = text.indexOf("@") != -1;
620 	if (!http && !www && !email) {
621 		return text;
622 	}
623 	if (text.indexOf("<") != -1 && text.indexOf(">") != -1) {
624 		return text;
625 	}
626 	if (http) {
627 	    var regex = /\b(?:https?|ftp|file):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim;
628 	    text = text.replace(regex, function(url, b, c) {
629 	    	var lower = url.toLowerCase();
630 	    	if (lower.indexOf(".png") != -1 || lower.indexOf(".jpg") != -1 || lower.indexOf(".jpeg") != -1 || lower.indexOf(".gif") != -1) {
631 	    		return '<a href="' + url + '" target="_blank"><img src="' + url + '" height="50"></a>';
632 	    	} else if (lower.indexOf(".mp4") != -1 || lower.indexOf(".webm") != -1 || lower.indexOf(".ogg") != -1) {
633 	    		return '<a href="' + url + '" target="_blank"><video src="' + url + '" height="50"></a>';
634 	    	} else if (lower.indexOf(".wav") != -1 || lower.indexOf(".mp3") != -1) {
635 	    		return '<a href="' + url + '" target="_blank"><audio src="' + url + '" controls>audio</a>';
636 	    	} else {
637 	    		return '<a href="' + url + '" target="_blank">' + url + '</a>';
638 	    	}
639 	    });
640 	} else if (www) {
641 	    var regex = /((www\.)[^\s]+)/gim;
642 	    text = text.replace(regex, function(url, b, c) {
643 	        return '<a href="http://' + url + '" target="_blank">' + url + '</a>';
644 	    });
645 	}
646     
647     // http://, https://, ftp://
648     //var urlPattern = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim;
649 
650     // www. 
651     // var wwwPattern = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
652 
653     // name@domain.com
654 	if (email) {
655     	var emailPattern = /(([a-zA-Z0-9_\-\.]+)@[a-zA-Z_]+?(?:\.[a-zA-Z]{2,6}))+/gim;
656     	text = text.replace(emailPattern, '<a target="_blank" href="mailto:$1">$1</a>');
657 	}
658 	return text;
659 }
660 
661 /**
662  * Enable speech recognition if supported by the browser, and insert the voice to text to the input field.
663  * Optionally call click() on the button.
664  */
665 SDK.registerSpeechRecognition = function(input, button) {
666 	if (SDK.recognition == null) {
667 		if ('webkitSpeechRecognition' in window) {
668 			SDK.recognition = new webkitSpeechRecognition();
669 			if (SDK.lang != null) {
670 				SDK.recognition.lang = SDK.lang;
671 			}
672 			SDK.recognition.continuous = true;
673 			SDK.recognition.onresult = function (event) {
674 			    for (var i = event.resultIndex; i < event.results.length; ++i) {
675 			        if (event.results[i].isFinal) {
676 			        	SDK.insertAtCaret(input, event.results[i][0].transcript);	        	
677 			        }
678 			    }
679 			    if (button != null && button.click != null) {
680 			    	button.click();
681 				} else if (button != null) {
682 					button();
683 				}
684 			};
685 		} else {
686 			return;
687 		}
688 	}
689 }
690 
691 SDK.startSpeechRecognition = function() {
692 	if (SDK.recognition != null) {
693 		if (SDK.lang != null) {
694 			SDK.recognition.lang = SDK.lang;
695 		}
696 		SDK.recognition.start();
697 		SDK.recognitionActive = true;
698 	}
699 }
700 
701 SDK.pauseSpeechRecognition = function() {
702 	if (SDK.recognition != null) {
703 		SDK.recognition.stop();
704 	}
705 }
706 
707 SDK.stopSpeechRecognition = function() {
708 	if (SDK.recognition != null) {
709 		SDK.recognition.stop();
710 		SDK.recognitionActive = false;
711 	}
712 }
713 
714 SDK.popupwindow = function(url, title, w, h) {
715 	var left = (screen.width)-w-10;
716 	var top = (screen.height)-h-100;
717 	window.open(url, title, 'scrollbars=yes, resizable=yes, toolbar=no, location=no, directories=no, status=no, menubar=no, copyhistory=no, width='+w+', height='+h+', top='+top+', left='+left);
718 	return false;
719 }
720 
721 SDK.dataURLToBlob = function(dataURL) {
722     var marker = ';base64,';
723     if (dataURL.indexOf(marker) == -1) {
724         var parts = dataURL.split(',');
725         var contentType = parts[0].split(':')[1];
726         var raw = parts[1];
727 
728         return new Blob([raw], {type: contentType});
729     }
730 
731     var parts = dataURL.split(marker);
732     var contentType = parts[0].split(':')[1];
733     var raw = window.atob(parts[1]);
734     var rawLength = raw.length;
735 
736     var blobarray = new Uint8Array(rawLength);
737 
738     for (var i = 0; i < rawLength; ++i) {
739     	blobarray[i] = raw.charCodeAt(i);
740     }
741 
742     return new Blob([blobarray], {type: contentType});
743 }
744 
745 SDK.uploadImage = function(fileInput, url, width, height, properties, onFinish) {
746 	if (window.File && window.FileReader && window.FileList && window.Blob) {
747 		var files = fileInput.files;
748 		for (var i = 0; i < files.length; i++) {
749 			SDK.resizeAndUploadImage(files[i], url, width, height, properties, ((i == (files.length - 1) ? onFinish : null)))
750 		}
751 		return false;
752 	} else {
753 		alert('The File APIs are not fully supported in this browser.');
754 		return false;
755 	}
756 }
757 			
758 SDK.resizeAndUploadImage = function(file, url, width, height, properties, onFinish) {
759 	var reader = new FileReader();
760 	reader.onloadend = function() {
761 		var tempImg = new Image();
762 		tempImg.src = reader.result;
763 		tempImg.onload = function() {
764 			var MAX_WIDTH = width;
765 			var MAX_HEIGHT = height;
766 			if (width == null) {
767 				MAX_WIDTH = tempImg.width;
768 			}
769 			if (height == null) {
770 				MAX_HEIGHT = tempImg.height;
771 			}
772 			var tempW = tempImg.width;
773 			var tempH = tempImg.height;
774 			if (tempW > MAX_WIDTH) {
775 				 tempH *= MAX_WIDTH / tempW;
776 				 tempW = MAX_WIDTH;
777 			}
778 			if (tempH > MAX_HEIGHT) {
779 				 tempW *= MAX_HEIGHT / tempH;
780 				 tempH = MAX_HEIGHT;
781 			}
782 			var canvas = document.createElement('canvas');
783 			canvas.width = tempW;
784 			canvas.height = tempH;
785 			var ctx = canvas.getContext("2d");
786 			ctx.fillStyle = '#fff';
787 			ctx.fillRect(0, 0, canvas.width, canvas.height);
788 			ctx.drawImage(this, 0, 0, tempW, tempH);
789 			var dataUrl = canvas.toDataURL('image/jpeg');
790 			var blob = SDK.dataURLToBlob(dataUrl);
791 			var formData = new FormData();
792 			if (properties != null) {
793 				for (property in properties) {
794 					formData.append(property, properties[property]);
795 				}
796 			}
797 			formData.append('file', blob, file.name);
798 			var request = new XMLHttpRequest();
799 			request.onreadystatechange = function() {
800 				if (request.readyState != 4) {
801 					return;
802 				}
803 				if (onFinish != null) {
804 					onFinish();
805 				}
806 			}
807 			request.open("POST", url);
808 			request.send(formData);
809 		}
810  
811 	 }
812 	 reader.readAsDataURL(file);
813 }
814 
815 /**
816  * Open a JQuery error message dialog.
817  */
818 SDK.showError = function(message, title) {
819 	if (title == null) {
820 		title = "Error";
821 	}
822 	$("<div></div>").html(message).dialog({
823 		title: title,
824 		resizable: false,
825 		modal: true,
826 		buttons: {
827 			"Ok": function() {
828 				$(this).dialog("close");
829 			}
830 		}
831 	});
832 }
833 
834 /**
835  * Open a JQuery confirm dialog.
836  */
837 SDK.showConfirm = function(message, title, onYes, onNo) {
838 	if (title == null) {
839 		title = "Confirm";
840 	}
841 	$("<div></div>").html(message).dialog({
842 	    title: title,
843 	    resizable: false,
844 	    modal: true,
845 	    buttons: {
846 	        "Yes": function() {
847 	        	onYes();
848 	            $(this).dialog("close");
849 	        },
850 		    "No": function() {
851 		    	onNo();
852 		        $(this).dialog("close");
853 		    }
854 	    }
855 	});
856 }
857 
858 /**
859  * Evaluate any script tags in the node's descendants.
860  * This is required when innerHtml contains script nodes as they are not evaluated.
861  */
862 SDK.evalScripts = function(node) {
863     if (node.tagName == 'SCRIPT') {
864         var script  = document.createElement("script");
865         script.text = node.innerHTML;
866         for (var index = node.attributes.length-1; index >= 0; i--) {
867         	script.setAttribute(node.attributes[index].name, node.attributes[index].value);
868         }
869     	node.parentNode.replaceChild(script, node);
870     } else {
871         var index = 0;
872         var children = node.childNodes;
873         while (index < children.length) {
874         	SDK.evalScripts(children[index]);
875         	index++;
876         }
877     }
878     return node;
879 }
880 
881 /**
882  * Remove any script tags from the node.
883  */
884 SDK.removeScripts = function(node) {
885     if (node.tagName == 'SCRIPT') {
886         node.parentNode.removeChild(node);
887     } else {
888         var index = 0;
889         var children = node.childNodes;
890         while (index < children.length) {
891         	SDK.removeScripts(children[index]);
892         	index++;
893         }
894     }
895     return node;
896 }
897 
898 /**
899  * Add a stylesheet link to the page.
900  */
901 SDK.addStylesheet = function(fileName) {
902   var head = document.head;
903   var link = document.createElement('link');
904   link.type = 'text/css';
905   link.rel = 'stylesheet';
906   link.href = fileName;
907   head.appendChild(link);
908 }
909 
910 /**
911  * Add a style tag to the page.
912  */
913 SDK.addStyle = function(css) {
914 	var body = document.body || document.getElementsByTagName('body')[0];
915 	var style = document.createElement('style');
916 	style.type = 'text/css';
917 	if (style.styleSheet) {
918 		style.styleSheet.cssText = css;
919 	} else {
920 		style.appendChild(document.createTextNode(css));
921 	}
922 	body.appendChild(style);
923 }
924 
925 /**
926  * Graphics upload dialog and shared repositry browser.
927  * This provides a generic media upload dialog with many features:
928  * <ul>
929  *     <li>Upload dialog UI
930  *     <li>Locally resize images before upload
931  *     <li>Upload from a web URL
932  *     <li>Upload a media file from a shared graphics repository
933  * </ul>
934  * @class
935  */
936 function GraphicsUploader() {
937 	this.id = "graphics-browser";
938 	this.title = "Media Browser";
939 	this.browserClass = "dialog";
940 	this.dialogId = "graphics-uploader";
941 	this.dialogTitle = "Upload Media";
942 	this.dialogClass = "dialog";
943 	this.uploadURL = "upload-media";
944 	this.uploadFormProperties;
945 	this.reloadOnSubmit = true;
946 	this.fileInput;
947 	this.urlInput;
948 	this.prefix = "uploader-";
949 	this.renderedDialog = false;
950 	this.submit = true;
951 	this.showFile = true;
952 	this.showURL = true;
953 	this.showBrowse = true;
954 	this.sdk = null;
955 	this.url;	
956 	
957 	/**
958 	 * Open JQyery upload dialog.
959 	 */
960 	this.openUploadDialog = function() {
961 		if (!this.renderedDialog) {
962 			this.renderUploadDialog();
963 		}
964 		$( '#' + this.dialogId ).dialog("open");
965 	}
966 	
967 	/**
968 	 * Open JQyery browser dialog.
969 	 */
970 	this.openBrowser = function() {
971 		var browser = document.getElementById(this.id);
972 		if (browser != null) {
973 			$( '#' + this.id ).remove();
974 		}
975 		this.renderBrowser();
976 		$( '#' + this.id ).dialog("open");
977 		this.fetchMedia();
978 	}
979 
980 	/**
981 	 * Render JQyery upload dialog.
982 	 */
983 	this.renderUploadDialog = function() {
984 		var uploadDialog = document.createElement('div');
985 		uploadDialog.setAttribute('id', this.dialogId);
986 		uploadDialog.setAttribute('title', this.dialogTitle);
987 		uploadDialog.setAttribute('class', this.dialogClass);
988 		uploadDialog.style.display = "none";
989 		var html =
990 				"<style>\n"
991 				+ "." + this.prefix + "button { text-decoration:none; padding: 12px 2px 12px 2px; }\n"
992 				+ "." + this.prefix + "dialog-div { margin-top: 10px;margin-bottom: 10px; }\n"
993 				+ "</style>\n";
994 		if (this.showBrowse) {
995 			html = html
996 				+ "<div class='" + this.prefix + "dialog-div'>\n"
997 					+ "<a id='" + this.prefix + "browse-library' onclick='return false;' href='#' class='" + this.prefix + "button' title='Browse our shared media library'>\n"
998 					+ "<img src='images/importr.png' style='vertical-align: middle'>\n"
999 					+ "Browse media library\n"
1000 					+ "</a>\n"
1001 				+ "</div>\n";
1002 		}
1003 		if (this.showFile) {
1004 			if (this.showBrowse) {
1005 				html = html + "<hr>\n";
1006 			}
1007 			html = html
1008 				+ "<div class='" + this.prefix + "dialog-div'>\n"
1009 					+ "<a id='" + this.prefix + "upload-media' onclick='return false;' href='#' class='" + this.prefix + "button' title='Upload an image or media file from your computer or device'>\n"
1010 					+ "<img src='images/upload.png' style='vertical-align: middle'>\n"
1011 					+ "Upload from computer or device</a>\n"
1012 				+ "</div>\n"
1013 				+ "<div class='" + this.prefix + "dialog-div'>\n"
1014 					+ "<input id='" + this.prefix + "file-input' style='display:none' type='file' name='file' style='display:none'/>\n"
1015 					+ "<input id='" + this.prefix + "resize' type='checkbox' title='Resize the image file locally to the max pixel width, to srink large images, and save upload bandwidth (only use on image files)'>\n"
1016 					+ "Resize to <input id='" + this.prefix + "resize-width' type='number' value='300' style='width:50px;height:25px' title='Image resize width in pixels'> pixels\n"
1017 				+ "</div>\n";
1018 		}
1019 		if (this.showURL) {
1020 			if (this.showFile || this.showBrowse) {
1021 				html = html + "<hr>\n";
1022 			}
1023 			html = html
1024 				+ "<div class='" + this.prefix + "dialog-div'>\n"
1025 					+ "<a id='" + this.prefix + "upload-url' onclick='return false;' href='#' class='" + this.prefix + "button' title='Import an image or media file from the web URL'>\n"
1026 					+ "<img src='images/importr.png' style='vertical-align: middle'>\n"
1027 					+ "Import from web URL\n"
1028 					+ "</a>\n"
1029 				+ "</div>\n"
1030 				+ "<input id='" + this.prefix + "url-input' type='text' style='width:100%'>\n";
1031 		}
1032 		uploadDialog.innerHTML = html;
1033 		document.body.appendChild(uploadDialog);
1034 		
1035 		var self = this;
1036 		var element = document.getElementById(this.prefix + "upload-media");
1037 		if (element != null) {
1038 			element.addEventListener("click", function(event) {
1039 				if (document.getElementById(self.prefix + 'resize').checked) {
1040 					document.getElementById(self.prefix + 'file-input').click();
1041 				} else {
1042 					self.fileInput.click();
1043 				}
1044 				return false;			
1045 			});
1046 		}
1047 		element = document.getElementById(this.prefix + "file-input");
1048 		if (element != null) {
1049 			element.addEventListener("change", function(event) {
1050 				var width = parseInt(document.getElementById(self.prefix + 'resize-width').value);
1051 				SDK.uploadImage(
1052 						document.getElementById(self.prefix + 'file-input'),
1053 						self.uploadURL,
1054 						width,
1055 						null,
1056 						self.uploadFormProperties,
1057 						function() {
1058 							if (self.reloadOnSubmit) {
1059 								location.reload();
1060 							}
1061 						});
1062 				return false;
1063 			});
1064 		}
1065 		element = document.getElementById(this.prefix + "upload-url");
1066 		if (element != null) {
1067 			element.addEventListener("click", function(event) {
1068 				self.urlInput.value = document.getElementById(self.prefix + "url-input").value;
1069 				if (self.submit) {
1070 					self.urlInput.form.submit();
1071 				}
1072 				return false;			
1073 			});
1074 		}
1075 		element = document.getElementById(this.prefix + "browse-library");
1076 		if (element != null) {
1077 			element.addEventListener("click", function(event) {
1078 				self.openBrowser(function(url) {
1079 					if (url == null) {
1080 						return false;					
1081 					}
1082 					self.urlInput.value = url;
1083 					if (self.submit) {
1084 						self.urlInput.form.submit();
1085 					}
1086 				});
1087 				return false;			
1088 			});
1089 		}
1090 		
1091 		$( '#' + this.dialogId ).dialog({
1092 			autoOpen: false,
1093 			modal: true,
1094 		    buttons: {
1095 		        "Cancel": function() {
1096 		            $(this).dialog("close");
1097 		        }
1098 		    }
1099 		});
1100 		this.renderedDialog = true;
1101 	}
1102 	
1103 	/**
1104 	 * Render JQyery browser dialog.
1105 	 */
1106 	this.renderBrowser = function() {
1107 		var browser = document.createElement('div');
1108 		browser.setAttribute('id', this.id);
1109 		browser.setAttribute('title', this.title);
1110 		browser.setAttribute('class', this.browserClass);
1111 		browser.style.display = "none";
1112 
1113 		var self = this;
1114 		GraphicsUploader.updateSearch = function() {
1115 			self.fetchMedia();
1116 		}
1117 		var height = window.innerHeight - (window.innerHeight * 0.2);
1118 		var width = window.innerWidth - (window.innerWidth * 0.2);
1119 		var html =
1120 				"<style>\n"
1121 				+ "." + this.prefix + "button { text-decoration:none; padding: 12px 2px 12px 2px; }\n"
1122 				+ "." + this.prefix + "browser-div { }\n"
1123 				+ "." + this.prefix + "search-div { width:264px;margin:2px;display:inline-block;font-size:13px; }\n"
1124 				+ "." + this.prefix + "search-span { display:inline-block;width:78px; }\n"
1125 				+ "." + this.prefix + "browse-categories, ." + this.prefix + "browse-tags, , ." + this.prefix + "browse-filter { width:150px; }\n"
1126 				+ "." + this.prefix + "browse-sort { width:150px; }\n"
1127 				+ "." + this.prefix + "browse-div { display:inline-block;margin:2px;vertical-align:top; }\n"
1128 				+ "." + this.prefix + "browse-details { font-size:12px;color:grey; }\n"
1129 				+ "." + this.prefix + "browse-img { max-width:100px;max-height:100px; }\n"
1130 				+ "." + this.prefix + "browse-span div { position:absolute;margin:-1px 0 0 0;padding:3px 3px 3px 3px;background:#fff;border-style:solid;border-color:black;border-width:1px;max-width:300px;min-width:100px;z-index:152;visibility:hidden;opacity:0;transition:visibility 0s linear 0.3s, opacity 0.3s linear; } \n"
1131 				+ "." + this.prefix + "browse-span:hover div { display:inline;visibility:visible;opacity:1;transition-delay:0.5s; }\n"
1132 				+ "</style>\n"
1133 				+ "<div><div class='" + this.prefix + "search-div'><span class='" + this.prefix + "search-span'>Categories</span><input id='" + this.prefix + "browse-categories' type='text'/></div>"
1134 				+ " <div class='" + this.prefix + "search-div'><span class='" + this.prefix + "search-span'>Tags</span><input id='" + this.prefix + "browse-tags' type='text'/></div>"
1135 				+ " <div class='" + this.prefix + "search-div'><span class='" + this.prefix + "search-span'>Filter</span><input id='" + this.prefix + "browse-filter' type='text'/></div>"
1136 				+ " <div class='" + this.prefix + "search-div'><span class='" + this.prefix + "search-span'>Sort</span><select id='" + this.prefix + "browse-sort' onchange='GraphicsUploader.updateSearch()'><option value='name'>name</option><option value='Date'>date</option><option value='thumbs up'>thumbs up</option>\n"
1137 				+ "<option value='thumbs down'>thumbs down</option><option value='Stars'>stars</option><option value='connects'>connects</option></select>\n"
1138 				+ "<a href='#' onclick='GraphicsUploader.updateSearch()' title='Search'><img src='images/inspect.png' style='vertical-align: middle'></a></div>\n"
1139 				+ "</div>\n"				
1140 				+ "<div id='" + this.prefix + "browser-div' class='" + this.prefix + "browser-div'>\n"
1141 				+ "</div>\n";
1142 		browser.innerHTML = html;
1143 		document.body.appendChild(browser);
1144 
1145 		var self = this;
1146 		SDK.debug = true;
1147 		if (this.sdk == null) {
1148 			this.sdk = new SDKConnection();
1149 		}
1150 		var autocompleteEvent = function(event) {
1151 			var self = this;
1152 			$(self).autocomplete('search', '');
1153 		}
1154 		if (GraphicsUploader.tags.length == 0) {
1155 			var contentConfig = new ContentConfig();
1156 			contentConfig.type = "Graphic";
1157 			this.sdk.fetchTags(contentConfig, function(results) {
1158 				GraphicsUploader.tags = results;
1159 				$( "#" + self.prefix + "browse-tags" ).autocomplete({ source: GraphicsUploader.tags, minLength: 0, appendTo: $("#" + self.prefix + "browse-tags").parent() }).on('focus', autocompleteEvent);
1160 			});
1161 		} else {
1162 			$( "#" + this.prefix + "browse-tags" ).autocomplete({ source: GraphicsUploader.tags, minLength: 0, appendTo: $("#" + self.prefix + "browse-tags").parent() }).on('focus', autocompleteEvent);
1163 		}
1164 		if (GraphicsUploader.categories.length == 0) {
1165 			var contentConfig = new ContentConfig();
1166 			contentConfig.type = "Graphic";
1167 			this.sdk.fetchCategories(contentConfig, function(results) {
1168 				GraphicsUploader.categories = results;
1169 				$( "#" + self.prefix + "browse-categories" ).autocomplete({ source: GraphicsUploader.categories, minLength: 0, appendTo: $("#" + self.prefix + "browse-categories").parent() }).on('focus', autocompleteEvent);
1170 			});
1171 		} else {
1172 			$( "#" + this.prefix + "browse-categories" ).autocomplete({ source: GraphicsUploader.categories, minLength: 0, appendTo: $("#" + self.prefix + "browse-categories").parent() }).on('focus', autocompleteEvent);
1173 		}
1174 		var keyPressed = function search(e) {
1175 		    if (e.keyCode == 13) {
1176 		    	self.fetchMedia();
1177 		    }
1178 		}
1179 		$( "#" + this.prefix + "browse-tags" ).on("keydown", keyPressed);
1180 		$( "#" + this.prefix + "browse-categories" ).on("keydown", keyPressed);
1181 		$( "#" + this.prefix + "browse-filter" ).on("keydown", keyPressed);
1182 		
1183 		$( '#' + this.id ).dialog({
1184 			autoOpen: false,
1185 			modal: true,
1186             height: height,
1187             width: width,
1188 		    buttons: {
1189 		        "Cancel": function() {
1190 		            $(this).dialog("close");
1191 		        }
1192 		    }
1193 		});
1194 	}
1195 	
1196 	/**
1197 	 * Query and display graphics.
1198 	 */
1199 	this.fetchMedia = function() {
1200 		var browseConfig = new BrowseConfig();
1201 		browseConfig.type = "Graphic";
1202 		browseConfig.category = document.getElementById(this.prefix + 'browse-categories').value;
1203 		browseConfig.tag = document.getElementById(this.prefix + 'browse-tags').value;
1204 		browseConfig.filter = document.getElementById(this.prefix + 'browse-filter').value;
1205 		browseConfig.sort = document.getElementById(this.prefix + 'browse-sort').value;
1206 		var self = this;
1207 		var urlprefix = self.sdk.credentials.url + "/";
1208 		GraphicsUploader.chooseMedia = function(id) {
1209 			var config = new GraphicConfig();
1210 			config.id = id;
1211 			self.sdk.fetch(config, function(result) {
1212 				self.url = urlprefix + result.media;
1213 				if (self.urlInput != null) {
1214 					self.urlInput.value = self.url;
1215 					if (self.submit) {
1216 						self.urlInput.form.submit();
1217 					}
1218 				}
1219 			});
1220 		}
1221 		this.sdk.browse(browseConfig, function(results) {
1222 			var div = document.getElementById(self.prefix + "browser-div");
1223 			while (div.firstChild) {
1224 				div.removeChild(div.firstChild);
1225 			}
1226 			for (var index = 0; index < results.length; index++) {
1227 				var result = results[index];
1228 				var graphicDiv = document.createElement('div');
1229 				graphicDiv.setAttribute('id', self.prefix + 'browse-div');
1230 				graphicDiv.setAttribute('class', self.prefix + 'browse-div');
1231 				var html =
1232 					"<span id='" + self.prefix + "browse-span' class='" + self.prefix + "browse-span'>"
1233 					+ "<table style='border-style:solid;border-color:grey;border-width:1px'><tr><td style='height:100px;width:100px;' align='center' valign='middle'>"
1234 					+ "<a href='#' onclick='GraphicsUploader.chooseMedia(" + result.id + ")'><img id='" + self.prefix + "browse-img' class='" + self.prefix + "browse-img' src='" + urlprefix + result.avatar + "'></a>\n"
1235 					+ "</td></tr></table>"
1236 					+ "<div>"
1237 					+ "<span><b>" + result.name + "</b><br/>" + result.description + "</span><br/>"
1238 					+ "<span id='" + self.prefix + "browse-details' class='" + self.prefix + "browse-details'>";
1239 				if (result.categories != null && result.categories != "") {
1240 					html = html + "Categories: " + result.categories + "<br/>";
1241 				}
1242 				if (result.tags != null && result.tags != "") {
1243 					html = html + "Tags: " + result.tags + "<br/>";
1244 				}
1245 				if (result.license != null && result.license != "") {
1246 					html = html + "License: " + result.license + "<br/>";
1247 				}
1248 				html = html
1249 					+ "</div>"
1250 					+ "</span>\n"
1251 					+ "<div style='max-width:100px'><a href='#' style='text-decoration:none;' onclick='GraphicsUploader.chooseMedia(" + result.id + ")'><span id='" + self.prefix + "browse-details' class='" + self.prefix + "browse-details'>" + result.name + "</span></div></a>\n";
1252 				graphicDiv.innerHTML = html;
1253 				var img = document.createElement('img');
1254 				img.setAttribute('src', urlprefix + result.avatar);
1255 				div.appendChild(graphicDiv);
1256 			}
1257 		});
1258 	}
1259 }
1260 
1261 GraphicsUploader.map = {};
1262 
1263 GraphicsUploader.tags = [];
1264 GraphicsUploader.categories = [];
1265 
1266 /**
1267  * Open a media uploader dialog initialized with a form.
1268  * The dialog will use the form's action to upload media as 'file' for a file, or 'upload-url' for a URL.
1269  * The form should define an ID if to be used on multiple forms in the same document.
1270  * This can be used on the onclick on an input element to open the dialog i.e. <input type="submit" onclick="return GraphicsUploader.openUploadDialog(this.form)" value="Upload">
1271  * This will create a hidden input of type file ('file'), and a hidden input of type text ('upload-url'), these will be passed to your server when the dialog submits the form.
1272  */
1273 GraphicsUploader.openUploadDialog = function(form, title, showFile, showURL, showBrowse) {    	
1274 	var id = form.getAttribute('id');
1275 	var prefix = "uploader-";
1276 	var dialogId = "graphics-uploader";
1277 	var browserId = "graphics-browser";
1278 	if (id == null) {
1279 		id = "uploader";
1280 	} else {
1281 		prefix = id + '-' + prefix;
1282 		dialogId = id + '-' + dialogId;
1283 		browserId = id + '-' + browserId;
1284 	}
1285 	var uploader = GraphicsUploader.map[id];
1286 	if (uploader == null) {
1287 		uploader = new GraphicsUploader();
1288 		GraphicsUploader.map[id] = uploader;
1289 		var div = document.createElement('div');
1290 		var html =
1291 			"<input id='" + id + "file-input' style='display:none' onchange='this.form.submit()' type='file' name='file'/>\n"
1292 			+ "<input id='" + id + "url-input' style='display:none' name='upload-url' type='text'>";
1293 		div.innerHTML = html;
1294 		form.appendChild(div);
1295 		if (title != null) {
1296 			uploader.dialogTitle = title;
1297 		}
1298 		if (showFile != null) {
1299 			uploader.showFile = showFile;
1300 		}
1301 		if (showURL != null) {
1302 			uploader.showURL = showURL;
1303 		}
1304 		if (showBrowse != null) {
1305 			uploader.showBrowse = showBrowse;
1306 		}
1307 		uploader.prefix = prefix;
1308 		uploader.dialogId = dialogId;
1309 		uploader.id= browserId;
1310 		uploader.fileInput = document.getElementById(id + 'file-input');
1311 		uploader.urlInput = document.getElementById(id + 'url-input');
1312 		uploader.uploadURL = form.action;
1313 	}
1314 	uploader.openUploadDialog();
1315 	return false;
1316 }
1317 
1318 /**
1319  * Credentials used to establish a connection.
1320  * Defines the url, host, app, rest, which are all defaulted and should not need to be changed,
1321  * Requires an application id.
1322  * You can obtain your application id from your user details page on the hosting website.
1323  * @class
1324  */
1325 function Credentials() {
1326 	this.host = SDK.host;
1327 	this.app = SDK.app;
1328 	this.url = SDK.url;
1329 	this.rest = SDK.rest;
1330 	this.applicationId = SDK.applicationId;
1331 }
1332 
1333 /**
1334  * Credentials for use with hosted services on the BOT libre website, a free bot hosting service.
1335  * http://www.botlibre.com
1336  * @class
1337  */
1338 function BOTlibreCredentials()  {
1339 	this.DOMAIN = "www.botlibre.com";
1340 	this.APP = "";
1341 	this.PATH = "/rest/api";
1342 	
1343 	this.host = this.DOMAIN;
1344 	this.app = this.APP;
1345 	this.url = "http://" + this.DOMAIN + this.APP;
1346 	this.rest = this.url + this.PATH;
1347 	this.applicationId = SDK.applicationId;
1348 }
1349 
1350 /**
1351  * Credentials for use with hosted services on the Bot Libre for Business website,
1352  * a commercial bot, live chat, chatroom, and forum, hosting service.
1353  * https://www.botlibre.biz
1354  * @class
1355  */
1356 function PaphusCredentials()  {
1357 	this.DOMAIN = "www.botlibre.biz";
1358 	this.APP = "";
1359 	this.PATH = "/rest/api";
1360 	
1361 	this.host = this.DOMAIN;
1362 	this.app = this.APP;
1363 	this.url = "http://" + this.DOMAIN + this.APP;
1364 	this.rest = this.url + this.PATH;
1365 	this.applicationId = SDK.applicationId;
1366 }
1367 
1368 /**
1369  * Credentials for use with hosted services on the LIVE CHAT libre website, a free live chat, chatrooms, forum, and chat bots that learn.
1370  * http://www.livechatlibre.com
1371  * @class
1372  */
1373 function LIVECHATlibreCredentials()  {
1374 	this.DOMAIN = "www.livechatlibre.com";
1375 	this.APP = "";
1376 	this.PATH = "/rest/api";
1377 	
1378 	this.host = this.DOMAIN;
1379 	this.app = this.APP;
1380 	this.url = "http://" + this.DOMAIN + this.APP;
1381 	this.rest = this.url + this.PATH;
1382 	this.applicationId = SDK.applicationId;
1383 }
1384 
1385 /**
1386  * Credentials for use with hosted services on the FORUMS libre website, a free embeddable forum hosting service.
1387  * http://www.forumslibre.com
1388  * @class
1389  */
1390 function FORUMSlibreCredentials()  {
1391 	this.DOMAIN = "www.forumslibre.com";
1392 	this.APP = "";
1393 	this.PATH = "/rest/api";
1394 	
1395 	this.host = this.DOMAIN;
1396 	this.app = this.APP;
1397 	this.url = "http://" + this.DOMAIN + this.APP;
1398 	this.rest = this.url + this.PATH;
1399 	this.applicationId = SDK.applicationId;
1400 }
1401 
1402 /**
1403  * Listener interface for a LiveChatConnection.
1404  * This gives asynchronous notification when a channel receives a message, or notice.
1405  * @class
1406  */
1407 function LiveChatListener() {
1408 	/**
1409 	 * A user message was received from the channel.
1410 	 */
1411 	this.message = function(message) {};
1412 	
1413 	/**
1414 	 * An informational message was received from the channel.
1415 	 * Such as a new user joined, private request, etc.
1416 	 */	
1417 	this.info = function(message) {};
1418 
1419 	/**
1420 	 * An error message was received from the channel.
1421 	 * This could be an access error, or message failure.
1422 	 */	
1423 	this.error = function(message) {};
1424 	
1425 	/**
1426 	 * Notification that the connection was closed.
1427 	 */
1428 	this.closed = function() {};
1429 	
1430 	/**
1431 	 * The channels users changed (user joined, left, etc.)
1432 	 * This contains a comma separated values (CSV) list of the current channel users.
1433 	 * It can be passed to the SDKConnection.getUsers() API to obtain the UserConfig info for the users.
1434 	 */
1435 	this.updateUsers = function(usersCSV) {};
1436 
1437 	/**
1438 	 * The channels users changed (user joined, left, etc.)
1439 	 * This contains a HTML list of the current channel users.
1440 	 * It can be inserted into an HTML document to display the users.
1441 	 */
1442 	this.updateUsersXML = function(usersXML) {};
1443 }
1444 
1445 /**
1446  * The WebLiveChatListener provides an integration between a LiveChatConnection and an HTML document.
1447  * It updates the document to message received from the connection, and sends messages from the document's form.
1448  * The HTML document requires the following elements:
1449  * - chat - <input type='text'> chat text input for sending messages
1450  * - send - <input type='submit'> button for sending chat input
1451  * - response - <p> paragraph for last chat message
1452  * - console - <table> table for chat log, and user log
1453  * - scroller - <div> div for chat log scroll pane
1454  * - online - <table> table for chat user list
1455  * @class
1456  */
1457 function WebLiveChatListener() {
1458 	/** Set the caption for the button bar button. */
1459 	this.caption = null;
1460 	this.switchText = true;
1461 	this.playChime = true;
1462 	this.speak = false;
1463 	this.voice = null;
1464 	this.nativeVoice = null;
1465 	this.nativeVoiceName = null;
1466 	this.lang = null;
1467 	this.nick = "";
1468 	this.connection = null;
1469 	this.sdk = null;
1470 	/** Configure if chat should be given focus after message. */
1471 	this.focus = true;
1472 	/** Element id and class prefix. Can be used to have multiple avatars in the same page, or avoid naming collisions. */
1473 	this.prefix = "";
1474 	/** Allow the chat box button color to be set. */
1475 	this.color = "#009900";
1476 	/** Allow the user to modify style sheets. */
1477 	this.version = null;
1478 	/** Allow the chat box hover button color to be set. */
1479 	this.hoverColor = "grey";
1480 	/** Allow the chat box background color to be set. */
1481 	this.background = null;
1482 	/** Chat box width. */
1483 	this.width = 300;
1484 	/** Chat box height. */
1485 	this.height = 320;
1486 	/** Chat box offset from side. */
1487 	this.offset = 30;
1488 	/** Chat Button Vertial Offset*/
1489 	this.verticalOffset = 0;
1490 	/** Print response in chat bubble. */
1491 	this.bubble = false;
1492 	/** Set the location of the button and box, one of "bottom-right", "bottom-left", "top-right", "top-left". */
1493 	this.boxLocation = "bottom-right";
1494 	/** Set styles explicitly to avoid inheriting page styles. Disable this to be able to override styles. */
1495 	this.forceStyles = true;
1496 	/** Override the URL used in the chat box popup. */
1497 	this.popupURL = null;
1498 	/** Set if the box chat log should be shown. */
1499 	this.chatLog = true;
1500 	/** Box chat loading message to display. */
1501 	this.loading = "loading...";
1502 	/** Box chat show online users option. */
1503 	this.online = false;
1504 	/** Link to online user list users to their profile page. */
1505 	this.linkUsers = true;
1506 	/** Configure message log format (table or log). */
1507 	this.chatLogType = "log";
1508 	/** Configure the chat to auto accept a bot after waiting */
1509 	this.autoAccept = null;
1510 	/** Prompt for name/email before connecting. */
1511 	this.promptContactInfo = false;
1512 	this.hasContactInfo = false;
1513 	/** Set if the back link should be displayed. */
1514 	this.backlink = SDK.backlink;
1515 	this.contactName = null;
1516 	this.contactEmail = null;
1517 	this.contactPhone = null;
1518 	this.contactInfo = "";
1519 	/** Allow the close button on the box button bar to be removed, and maximize on any click to the button bar. */
1520 	this.showClose = true;
1521 	/** Provide an email chat log menu item. */
1522 	this.emailChatLog = false;
1523 	/** Ask the user if they want an email of the chat log on close. */
1524 	this.promptEmailChatLog = false;
1525 	this.windowTitle = document.title;
1526 	this.isActive = true;
1527 	/** Variables used to get the user and bot images. */
1528 	this.botThumb = {};
1529 	this.userThumb = {};
1530 	self = this;
1531 	/** JSON object of all currently logged in users in the chat room take from updateUsersXML. */
1532 	this.users = {};
1533 	/** Box chat chat room option. */
1534 	this.chatroom = false;
1535 	/** Show and hides menu bar */
1536 	this.showMenubar = true;
1537 	/** Show Box Max */
1538 	this.showBoxmax = true;
1539 	/** Show Send Image */
1540 	this.showSendImage = true;
1541 	
1542 	/**
1543 	 * Create an embedding bar and div in the current web page.
1544 	 */
1545 	this.createBox = function() {
1546 		if (this.prefix == "" && this.elementPrefix != null) {
1547 			this.prefix = this.elementPrefix;
1548 		}
1549 		if (this.caption == null) {
1550 			this.caption = this.instanceName;
1551 		}
1552 		var backgroundstyle = "";
1553 		var buttonstyle = "";
1554 		var buttonHoverStyle = "";
1555 		var hidden = "hidden";
1556 		var border = "";
1557 		if (this.backgroundIfNotChrome && SDK.isChrome()) {
1558 			this.background = null;
1559 		}
1560 		if (this.background != null) {
1561 			backgroundstyle = " style='background-color:" + this.background + "'";
1562 			hidden = "visible";
1563 			border = "border:1px;border-style:solid;border-color:black;";
1564 		}
1565 		if (this.color != null) {
1566 			buttonstyle = "background-color:" + this.color + ";";
1567 		}
1568 		if (this.hoverColor != null) {
1569 			buttonHoverStyle = "background-color:" + this.hoverColor + ";";
1570 		}
1571 		
1572 		var minWidth = "";
1573 		var divWidth = "";
1574 		var background = "";
1575 		var minHeight = "";
1576 		var divHeight = "";
1577 		var maxDivWidth = "";
1578 		if (this.width != null) {
1579 			minWidth = "width:" + this.width + "px;";
1580 			background = "background-size:" + this.width + "px auto;";
1581 			divWidth = minWidth;
1582 			divHeight = "min-height:" + this.width + "px;";
1583 			responseWidth = "width:" + (this.width - 32) + "px;";
1584 			maxDivWidth = "max-width:" + (this.width - 50) + "px;";
1585 		}
1586 		if (this.height != null) {
1587 			minHeight = "height:" + this.height + "px;";
1588 			divHeight = minHeight;
1589 			if (this.width != null) {
1590 				background = "background-size:" + this.width + "px " + this.height + "px;";
1591 			} else {
1592 				background = "background-size: auto " + this.height + "px;";
1593 				divWidth = "min-width:" + this.height + "px;";
1594 			}
1595 		}
1596 		var boxloc = "bottom:10px;right:10px";
1597         if (this.boxLocation == "top-left") {
1598             boxloc = "top:10px;left:10px";
1599         } else if (this.boxLocation == "top-right") {
1600             boxloc = "top:10px;right:10px";
1601         } else if (this.boxLocation == "bottom-left") {
1602             boxloc = "bottom:10px;left:10px";
1603         } else if (this.boxLocation == "bottom-right") {
1604             boxloc = "bottom:10px;right:10px";
1605         }
1606         var locationBottom = 20;
1607         if (this.version < 6.0 || this.prefix != "botplatformchat") {
1608         	locationBottom = 2;
1609         }
1610         var boxbarloc = "bottom:" + (locationBottom + this.verticalOffset) + "px;right:" + this.offset + "px";
1611         if (this.boxLocation == "top-left") {
1612             boxbarloc = "top:" + (locationBottom + this.verticalOffset) + "px;left:" + this.offset + "px";
1613         } else if (this.boxLocation == "top-right") {
1614             boxbarloc = "top:" + (locationBottom + this.verticalOffset) + "px;right:" + this.offset + "px";
1615         } else if (this.boxLocation == "bottom-left") {
1616             boxbarloc = "bottom:" + (locationBottom + this.verticalOffset) + "px;left:" + this.offset + "px";
1617         } else if (this.boxLocation == "bottom-right") {
1618             boxbarloc = "bottom:" + (locationBottom + this.verticalOffset) + "px;right:" + this.offset + "px";
1619         }
1620 		var box = document.createElement('div');
1621 		var html =
1622 			"<style>\n"
1623 				+ "." + this.prefix + "box { position:fixed;" + boxloc + ";z-index:152;margin:2px;display:none;" + border + " }\n"
1624 				+ "." + this.prefix + "boxmenu { visibility:" + hidden + "; }\n" //margin-bottom:12px;
1625 				+ (this.forceStyles ? "#" : ".") + "" + this.prefix + "boxbarmax { font-size:18px;margin:2px;padding:0px;text-decoration:none; }\n"
1626 				+ "." + this.prefix + "boxbar { position:fixed;" + boxbarloc + ";z-index:152;margin:0;padding:6px;" + buttonstyle + " }\n"
1627 				+ "." + this.prefix + "boxbar:hover { " + buttonHoverStyle + " }\n"
1628 				+ "#" + this.prefix + "emailchatlogdialog { " + minWidth + " }\n"
1629 				+ "#" + this.prefix + "contactinfo { " + minHeight + minWidth + " }\n"
1630 				+ "." + this.prefix + "contactconnect { margin:4px;padding:8px;color:white;text-decoration:none;" + buttonstyle + " }\n"
1631 				+ "." + this.prefix + "no-bubble-text { " + responseWidth + "; max-height:100px; overflow:auto; }\n"
1632 				+ "." + this.prefix + "bubble-text { " + responseWidth + "; margin:4px; max-height:100px; overflow:auto; }\n"
1633 				+ (this.forceStyles ? "#" : ".") + this.prefix + "chat { width:99%;min-height:22px; }\n"
1634 				+ "." + this.prefix + "chat-1-div { " + maxDivWidth + "}\n"
1635 				+ "." + this.prefix + "chat-2-div { " + maxDivWidth + "}\n"
1636 				+ "." + this.prefix + "online-div { " + minWidth + " overflow-x: auto; overflow-y: hidden; white-space: nowrap; }\n"
1637 				+ "." + this.prefix + "scroller { overflow-x:hidden;" + minHeight + minWidth + " }\n"
1638 				+ "." + this.prefix + "box:hover ." + this.prefix + "boxmenu { visibility:visible; }\n";
1639 		if (this.version < 6.0 || this.prefix != "botplatformchat") {
1640 			html = html
1641 						+ "." + this.prefix + "box img { display:inline; }\n"
1642 						+ "." + this.prefix + "boxbar img { display:inline; }\n"
1643 						+ "." + this.prefix + "box:hover { border:1px;border-style:solid;border-color:black; }\n"
1644 						+ "." + this.prefix + "box:hover ." + this.prefix + "boxmenu { visibility:visible; }\n"
1645 						+ "." + this.prefix + "boxclose, ." + this.prefix + "boxmin, ." + this.prefix + "boxmax { font-size:22px;margin:2px;padding:0px;text-decoration:none; }\n"
1646 						+ "." + this.prefix + "boxclose:hover, ." + this.prefix + "boxmin:hover, ." + this.prefix + "boxmax:hover { color: #fff;background: grey; }\n"
1647 						+ "#" + this.prefix + "emailchatlogdialog span { margin-left:0px;margin-top:4px; }\n"
1648 						+ "#" + this.prefix + "emailchatlogdialog input { margin:4px;font-size:13px;height:33px;width:90%;border:1px solid #d5d5d5; }\n"
1649 						+ "." + this.prefix + "emailconfirm { margin:4px;padding:8px;color:white;background-color:grey;text-decoration:none; }\n"
1650 						+ "#" + this.prefix + "contactinfo span { margin-left:4px;margin-top:4px; }\n"
1651 						+ "#" + this.prefix + "contactinfo input { margin:4px;font-size:13px;height:33px;width:90%;border:1px solid #d5d5d5; }\n"
1652 						+ "." + this.prefix + "no-bubble { margin:4px; padding:8px; border:1px; border-style:solid; border-color:black; background-color:white; color:black; }\n"
1653 						+ "." + this.prefix + "boxbutton { width:20px;height:20px;margin:2px; }\n"
1654 						+ "." + this.prefix + "bubble-div { padding-bottom:15px;position:relative; }\n"
1655 						+ "." + this.prefix + "no-bubble-plain { margin:4px; padding:8px; border:1px; }\n"
1656 						+ "." + this.prefix + "bubble { margin:4px; padding:8px; border:1px; border-style:solid; border-color:black; border-radius:10px; background-color:white; color:black; }\n"
1657 						+ "." + this.prefix + "bubble:before { content:''; position:absolute; bottom:0px; left:40px; border-width:20px 0 0 20px; border-style:solid; border-color:black transparent; display:block; width:0;}\n"
1658 						+ "." + this.prefix + "bubble:after { content:''; position:absolute; bottom:3px; left:42px; border-width:18px 0 0 16px; border-style:solid; border-color:white transparent; display:block; width:0;}\n"
1659 						+ "." + this.prefix + "box-input-span { display:block; overflow:hidden; margin:4px; padding-right:4px; }\n"
1660 						+ "a." + this.prefix + "menuitem { text-decoration: none;display: block;color: #585858; }\n"
1661 						+ "a." + this.prefix + "menuitem:hover { color: #fff;background: grey; }\n"
1662 						+ "tr." + this.prefix + "menuitem:hover { background: grey; }\n"
1663 						+ "." + this.prefix + "powered { margin:4px;font-size:10px; }\n"
1664 						+ "img." + this.prefix + "menu { width: 24px;height: 24px;margin: 2px;cursor: pointer;vertical-align: middle; }\n"
1665 						+ "span." + this.prefix + "menu { color: #818181;font-size: 12px; }\n"
1666 						+ "img." + this.prefix + "toolbar { width: 25px;height: 25px;margin: 1px;padding: 1px;cursor: pointer;vertical-align: middle;border-style: solid;border-width: 1px;border-color: #fff; }\n"
1667 						+ "td." + this.prefix + "toolbar { width: 36px;height: 36px }\n"
1668 						/*+ "." + this.prefix + "online { height: 97px;width: 300px;overflow-x: auto;overflow-y: hidden;white-space: nowrap; }\n"*/
1669 						+ "." + this.prefix + "menupopup div { position:absolute;margin: -1px 0 0 0;padding: 3px 3px 3px 3px;background: #fff;border-style:solid;border-color:black;border-width:1px;width:160px;max-width:300px;z-index:152;visibility:hidden;opacity:0;transition:visibility 0s linear 0.3s, opacity 0.3s linear; }\n"
1670 						+ "." + this.prefix + "menupopup:hover div { display:inline;visibility:visible;opacity:1;transition-delay:0.5s; }\n"
1671 						+ "img.chat-user-thumb { height: 50px; }\n"
1672 						+ "a.user { text-decoration: none; }\n"
1673 						+ "td." + this.prefix + "chat-1 { width:100%;background-color: #d5d5d5;}\n"
1674 						+ "span." + this.prefix + "chat-1 { color:#333;}\n"
1675 						+ "span." + this.prefix + "chat-user { color:grey;font-size:small; }\n"
1676 						+ "." + this.prefix + "console { width:100%; }\n"
1677 						+ "." + this.prefix + "online-div { display: none; }\n"
1678 						+ "." + this.prefix + "-channel-title { display: none; }\n"
1679 						+ "#" + this.prefix + "boxtable { background:none; border:none; margin:0; }\n"
1680 						+ "#" + this.prefix + "boxbar3 { display:none; }\n"
1681 						+ "#" + this.prefix + "boxbarmax { color: white; }\n";
1682 		}
1683 		html = html
1684 			+ "</style>\n"
1685 			+ "<div id='" + this.prefix + "box' class='" + this.prefix + "box' " + backgroundstyle + ">"
1686 				+ "<div class='" + this.prefix + "boxmenu'>"
1687 					+ (this.backlink ? "<span class='" + this.prefix + "powered'>powered by <a href='" + SDK.backlinkURL + "' target='_blank'>" + SDK.NAME + "</a></span>" : "")
1688 					+ "<span class=" + this.prefix + "-channel-title>" + this.instanceName + "</span>"
1689 					+ "<span style='float:right'><a id='" + this.prefix + "boxmin' class='" + this.prefix + "boxmin' onclick='return false;' href='#'><img src='" + SDK.url + "/images/minimize.png'></a>";
1690 					if (this.showBoxmax) {
1691 						html = html + "<a id='" + this.prefix + "boxmax' class='" + this.prefix + "boxmax' onclick='return false;' href='#'><img src='" + SDK.url + "/images/open.png'></a></span><br/>";
1692 					} else {
1693 						html = html + "</span><br/>";
1694 					}	
1695 				html = html + "</div>";
1696 		if (this.online) {
1697 			html = html
1698         		+ "<div id='" + this.prefix + "online-div' class='" + this.prefix + "online-div'>"
1699         			+ "<div id='" + this.prefix + "online' class='" + this.prefix + "online'" + "style='display:none;'>"
1700         				+ "<table></table>"
1701         			+ "</div>"
1702         		+ "</div>";
1703 		}
1704 		if (this.chatLog) {
1705 			html = html
1706         		+ "<div id='" + this.prefix + "scroller' class='" + this.prefix + "scroller'>"
1707         		+ "<table id='" + this.prefix + "console' class='" + this.prefix + "console' width=100% cellspacing=2></table>"
1708         		+ "</div>"
1709 		}
1710 		var urlprefix = this.sdk.credentials.url + "/";
1711 		html = html
1712 				+ "<div>\n"
1713 					+ "<div " + (this.bubble ? "id='" + this.prefix + "bubble-div' class='" + this.prefix + "bubble-div'" : "") + ">"
1714 					+ "<div "
1715 					+ "id='" + this.prefix + (this.bubble ? "bubble" : (this.background == null ? "no-bubble" : "no-bubble-plain") )
1716 					+ "' class='" + this.prefix + (this.bubble ? "bubble" : (this.background == null ? "no-bubble" : "no-bubble-plain") )
1717 					+ "'><div id='" + this.prefix + (this.bubble ? "bubble-text" : "no-bubble-text" ) + "' "
1718 						+ "class='" + this.prefix + (this.bubble ? "bubble-text" : "no-bubble-text" ) + "'>"
1719 						+ "<span id='" + this.prefix + "response'>" + this.loading + "</span><br/>"
1720 					+ "</div></div></div>\n";
1721 		html = html
1722 			+ "<table id='" + this.prefix + "boxtable' class='" + this.prefix + "boxtable' style='width:100%'><tr>\n";
1723 			if (this.showMenubar) {
1724 				html = html
1725 					+ "<td class='" + this.prefix + "toolbar'><span class='" + this.prefix + "menu'>\n"
1726 					+ "<div style='inline-block;position:relative'>\n"
1727 					+ "<span id='" + this.prefix + "menupopup' class='" + this.prefix + "menupopup'>\n"
1728 					+ "<div style='text-align:left;bottom:30px'>\n"
1729 					+ "<table>\n"
1730 					+ "<tr class='" + this.prefix + "menuitem'>"
1731 					+ "<td><a id='" + this.prefix + "ping' class='" + this.prefix + "menuitem' onclick='return false;' href='#'><img class='" + this.prefix + "menu' src='" + SDK.url + "/images/empty.png' title='" + SDK.translate("Verify your connection to the server") + "'> " + SDK.translate("Ping server") + "</a></td>"
1732 					+ "</tr>\n";
1733 		    	if (this.chatroom) {
1734 				    html = html
1735 		        		+ "<tr class='" + this.prefix + "menuitem'>"
1736 		        			+ "<td><a id='" + this.prefix + "flag' class='" + this.prefix + "menuitem' onclick='return false;' href='#'><img class='" + this.prefix + "menu' src='" + SDK.url + "/images/flag2.png' title='" + SDK.translate("Flag a user for offensive content") + "'> " + SDK.translate("Flag user") + "</a></td>"
1737 		        		+ "</tr>\n"
1738 		        		+ "<tr class='" + this.prefix + "menuitem'>"
1739 		        			+ "<td><a id='" + this.prefix + "whisper' class='" + this.prefix + "menuitem' onclick='return false;' href='#'><img class='" + this.prefix + "menu' src='" + SDK.url + "/images/whisper.png' title='" + SDK.translate("Send a private message to another user") + "'> " + SDK.translate("Whisper user") + "</a></td>"
1740 		        		+ "</tr>\n"
1741 		        		+ "<tr class='" + this.prefix + "menuitem'>"
1742 		        			+ "<td><a id='" + this.prefix + "pvt' class='" + this.prefix + "menuitem' onclick='return false;' href='#'><img class='" + this.prefix + "menu' src='" + SDK.url + "/images/accept.png' title='" + SDK.translate("Invite another user to a private channel") + "'> " + SDK.translate("Request private") + "</a></td>"
1743 		        		+ "</tr>\n"
1744 		    	}
1745 				html = html
1746 					+ "<tr class='" + this.prefix + "menuitem'>"
1747 						+ "<td><a id='" + this.prefix + "clear' class='" + this.prefix + "menuitem' onclick='return false;' href='#'><img class='" + this.prefix + "menu' src='" + SDK.url + "/images/empty.png' title='" + SDK.translate("Clear the local chat log") + "'> " + SDK.translate("Clear log") + "</a></td>"
1748 					+ "</tr>\n"
1749 					+ "<tr class='" + this.prefix + "menuitem'>"
1750 						+ "<td><a id='" + this.prefix + "accept' class='" + this.prefix + "menuitem' onclick='return false;' href='#'><img class='" + this.prefix + "menu' src='" + SDK.url + "/images/accept.png' title='" + SDK.translate("Accept a private request from an operator, bot, or another user") + "'> " + SDK.translate("Accept private") + "</a></td>"
1751 					+ "</tr>\n";
1752 					if (this.showSendImage) {
1753 						html = html
1754 						+ "<tr class='" + this.prefix + "menuitem'>"
1755 							+ "<td><a id='" + this.prefix + "sendImage' class='" + this.prefix + "menuitem' onclick='return false;' href='#'><img class='" + this.prefix + "menu' src='" + SDK.url + "/images/image.png' title='" + SDK.translate("Resize and send an image attachment") + "'> " + SDK.translate("Send image") + "</a></td>"
1756 						+ "</tr>\n"
1757 						+ "<tr class='" + this.prefix + "menuitem'>"
1758 							+ "<td><a id='" + this.prefix + "sendAttachment' class='" + this.prefix + "menuitem' onclick='return false;' href='#'><img class='" + this.prefix + "menu' src='" + SDK.url + "/images/attach.png' title='" + SDK.translate("Send an image or file attachment") + "'> " + SDK.translate("Send file") + "</a></td>"
1759 						+ "</tr>\n";
1760 					}
1761 				if (this.emailChatLog) {
1762 					html = html
1763 						+ "<tr class='" + this.prefix + "menuitem'>"
1764 						+ "<td><a id='" + this.prefix + "emailChatLog' class='" + this.prefix + "menuitem' onclick='return false;' href='#'><img id='email' class='" + this.prefix + "menu' src='" + SDK.url + "/images/message.png' title='" + SDK.translate("Send yourself an email of the conversation log") + "'> " + SDK.translate("Email Chat Log") + "</a></td>"
1765 				}
1766 				html = html
1767 					+ "<tr class='" + this.prefix + "menuitem'>"
1768 						+ "<td><a id='" + this.prefix + "toggleChime' class='" + this.prefix + "menuitem' onclick='return false;' href='#'><img id='boxchime' class='" + this.prefix + "menu' src='" + SDK.url + "/images/sound.png' title='" + SDK.translate("Play a chime when a message is recieved") + "'> " + SDK.translate("Chime") + "</a></td>"
1769 					+ "</tr>\n"
1770 					+ "<tr class='" + this.prefix + "menuitem'>"
1771 						+ "<td><a id='" + this.prefix + "toggleSpeak' class='" + this.prefix + "menuitem' onclick='return false;' href='#'><img id='boxtalk' class='" + this.prefix + "menu' src='" + SDK.url + "/images/talkoff.png' title='" + SDK.translate("Speak each message using voice synthesis") + "'> " + SDK.translate("Text to speech") + "</a></td>"
1772 					+ "</tr>\n"
1773 					+ "<tr class='" + this.prefix + "menuitem'>"
1774 						+ "<td><a id='" + this.prefix + "toggleListen' class='" + this.prefix + "menuitem' onclick='return false;' href='#'>"
1775 								+ "<img id='boxmic' class='" + this.prefix + "menu' src='" + SDK.url + "/images/micoff.png' title='" + SDK.translate("Enable speech recognition (browser must support HTML5 speech recognition, such as Chrome)") + "'> " + SDK.translate("Speech recognition") + "</a>"
1776 						+ "</td>"
1777 					+ "</tr>\n"
1778 					+ "<tr class='" + this.prefix + "menuitem'>"
1779 						+ "<td><a id='" + this.prefix + "exit' class='" + this.prefix + "menuitem' onclick='return false;' href='#'><img class='" + this.prefix + "menu' src='" + SDK.url + "/images/quit.png' title='" + SDK.translate("Exit the channel or active private channel") + "'> " + SDK.translate("Quit private or channel") + "</a></td>"
1780 					+ "</tr>\n"
1781 					+ "</table>\n"
1782 					+ "</div>\n"
1783 					+ "<img class='" + this.prefix + "toolbar' src='" + SDK.url + "/images/menu.png'>\n"
1784 					+ "</span>\n"
1785 					+ "</div>\n"
1786 		    		+ "</span></td>\n";
1787 			}
1788 		html = html
1789 			+ "<td><span class='" + this.prefix + "box-input-span'><input id='" + this.prefix
1790 				+ "chat' type='text' id='" + this.prefix + "box-input' "
1791 				+ "class='" + this.prefix + "box-input'/></span></td>"
1792 			+ "</tr></table>"
1793 			+ "</div>\n"
1794 			+ "</div>\n"
1795 			+ "<div id='" + this.prefix + "boxbar' class='" + this.prefix + "boxbar'>"
1796 				+ "<div id='" + this.prefix + "boxbar2' class='" + this.prefix + "boxbar2'>"
1797 					+ "<span><a id='" + this.prefix + "boxbarmax' class='" + this.prefix + "boxbarmax' " + " onclick='return false;' href='#'><img id='" + this.prefix + "boxbarmaximage' " + "src='" + SDK.url + "/images/maximizew.png'> " + this.caption + " </a>";
1798 		if (this.showClose) {
1799 			html = html + " <a id='" + this.prefix + "boxclose' class='" + this.prefix + "boxclose' onclick='return false;' href='#'> <img src='" + SDK.url + "/images/closeg.png'></a>";
1800 		}
1801 		html = html
1802 				+ "</span><br>"
1803 			+ "</div>\n"
1804 			+ "<div id='" + this.prefix + "boxbar3' class='" + this.prefix + "boxbar3'" + ">"
1805 				+ "<span><a id='" + this.prefix + "boxbarmax2' class='" + this.prefix + "boxbarmax2' " + (this.forceStyles ? "style='color:white' " : "") + " onclick='return false;' href='#'>" + "</a>"
1806 				+ "</span><br>";
1807 		if (this.showClose) {
1808 			html = html + " <a id='" + this.prefix + "boxclose2' class='" + this.prefix + "boxclose2' onclick='return false;' href='#'> <img src='" + SDK.url + "/images/closeg.png'></a>";
1809 		}
1810 		html = html
1811 			+ "</div>\n"
1812 			+ "</span>"
1813 			+ "</div>\n";
1814 		
1815 		if (this.promptContactInfo) {
1816 			html = html
1817 				+ "<div id='" + this.prefix + "contactinfo' class='" + this.prefix + "box' " + backgroundstyle + ">"
1818 					+ "<div class='" + this.prefix + "boxmenu'>"
1819 						+ "<span style='float:right'><a id='" + this.prefix + "contactboxmin' class='" + this.prefix + "contactboxmin' onclick='return false;' href='#'><img src='" + SDK.url + "/images/minimize.png'></a>"
1820 					+ "</div>\n"
1821 					+ "<div style='margin:10px'>\n"
1822 						+ "<span>" + SDK.translate("Name") + "</span><br/><input id='" + this.prefix + "contactname' type='text' /><br/>\n"
1823 						+ "<span>" + SDK.translate("Email") + "</span><br/><input id='" + this.prefix + "contactemail' type='email' /><br/>\n"
1824 						+ "<span>" + SDK.translate("Phone") + "</span><br/><input id='" + this.prefix + "contactphone' type='text' /><br/>\n"
1825 						+ "<br/><a id='" + this.prefix + "contactconnect' class='" + this.prefix + "contactconnect' " + (this.forceStyles ? "style='color:white' " : "") + " onclick='return false;' href='#'>" + SDK.translate("Connect") + "</a>\n"
1826 					+ "</div>\n"
1827 				+ "</div>";
1828 		}
1829 		if (this.promptEmailChatLog) {
1830 			html = html
1831 				+ "<div id='" + this.prefix + "emailchatlogdialog' class='" + this.prefix + "box' " + backgroundstyle + ">"
1832 					+ "<div class='" + this.prefix + "boxmenu'>"
1833 						+ "<span style='float:right'><a id='" + this.prefix + "emailchatlogdialogmin' class='" + this.prefix + "boxmin' onclick='return false;' href='#'><img src='" + SDK.url + "/images/minimize.png'></a>"
1834 					+ "</div>\n"
1835 					+ "<div style='margin:10px;margin-bottom:20px;margin-top:20px;'>\n"
1836 						+ "<span>" + SDK.translate("Would you like a copy of the chat log sent to your email?") + "</span><br/><input id='" + this.prefix + "emailchatlogemail' type='email' /><br/>\n"
1837 						+ "<br/><a id='" + this.prefix + "emailchatlogdialogyes' class='" + this.prefix + "emailconfirm' " + (this.forceStyles ? "style='color:white' " : "") + " onclick='return false;' href='#'>" + SDK.translate("Yes") + "</a>\n"
1838 						+ " <a id='" + this.prefix + "emailchatlogdialogno' class='" + this.prefix + "emailconfirm' " + (this.forceStyles ? "style='color:white' " : "") + " onclick='return false;' href='#'>" + SDK.translate("No") + "</a>\n"
1839 					+ "</div>\n"
1840 				+ "</div>";
1841 		}
1842 		
1843 		box.innerHTML = html;
1844 		document.body.appendChild(box);
1845 		
1846 		if (this.online && this.chatroom) {
1847 			document.getElementById(this.prefix + "online").style.height = "95px";
1848 		}
1849 		if (this.chatLog && !this.bubble) {
1850 			var bubbleDiv = document.getElementById(this.prefix + 'bubble-div');
1851 			if (bubbleDiv != null) {
1852 				bubbleDiv.style.display = "none";
1853 			}
1854 			document.getElementById(this.prefix + 'no-bubble-plain').style.display = "none";
1855 			document.getElementById(this.prefix + 'response').style.display = "none";
1856 		}
1857 		
1858 		var self = this;
1859 		var listen = false;
1860 		if (document.getElementById(this.prefix + "chat") != null) {
1861 			document.getElementById(this.prefix + "chat").addEventListener("keypress", function(event) {
1862 				if (event.keyCode == 13) {
1863 					self.sendMessage();
1864 					return false;
1865 				}
1866 			});
1867 		}
1868 		if (document.getElementById(this.prefix + "exit") != null) {
1869 			document.getElementById(this.prefix + "exit").addEventListener("click", function() {
1870 				self.exit();
1871 				return false;
1872 			});
1873 		}
1874 		if (document.getElementById(this.prefix + "ping")!= null) {
1875 			document.getElementById(this.prefix + "ping").addEventListener("click", function() {
1876 				self.ping();
1877 				return false;
1878 			});
1879 		}
1880 		if (document.getElementById(this.prefix + "clear") != null) {
1881 			document.getElementById(this.prefix + "clear").addEventListener("click", function() {
1882 				self.clear();
1883 				return false;
1884 			});
1885 		}
1886 		if (document.getElementById(this.prefix + "accept") != null) {
1887 			document.getElementById(this.prefix + "accept").addEventListener("click", function() {
1888 				self.accept();
1889 				return false;
1890 			});
1891 		}
1892 		if (document.getElementById(this.prefix + "sendImage") != null) {
1893 			document.getElementById(this.prefix + "sendImage").addEventListener("click", function() {
1894 				self.sendImage();
1895 				return false;
1896 			});
1897 		}
1898 		if (document.getElementById(this.prefix + "sendAttachment") != null) {
1899 			document.getElementById(this.prefix + "sendAttachment").addEventListener("click", function() {
1900 				self.sendAttachment();
1901 				return false;
1902 			});
1903 		}
1904 		if (this.emailChatLog) {
1905 			if (document.getElementById(this.prefix + "emailChatLog") != null) {
1906 				document.getElementById(this.prefix + "emailChatLog").addEventListener("click", function() {
1907 					self.emailChatLog();
1908 					return false;
1909 				});
1910 			}
1911 		}
1912 		if (this.promptEmailChatLog) {
1913 			document.getElementById(this.prefix + "emailchatlogdialogyes").addEventListener("click", function() {
1914 				self.sendEmailChatLogBox();
1915 				return false;
1916 			});
1917 			document.getElementById(this.prefix + "emailchatlogdialogno").addEventListener("click", function() {
1918 				self.minimizeEmailChatLogBox();
1919 				return false;
1920 			});
1921 			document.getElementById(this.prefix + "emailchatlogdialogmin").addEventListener("click", function() {
1922 				self.minimizeEmailChatLogBox();
1923 				return false;
1924 			});
1925 		}
1926 		var menu = document.getElementById(this.prefix + "flag");
1927 		if (menu != null) {
1928 			menu.addEventListener("click", function() {
1929 				self.flag();
1930 				return false;
1931 			});
1932 		}
1933 		menu = document.getElementById(this.prefix + "whisper");
1934 		if (menu != null) {
1935 			menu.addEventListener("click", function() {
1936 				self.whisper();
1937 				return false;
1938 			});
1939 		}
1940 		menu = document.getElementById(this.prefix + "pvt");
1941 		if (menu != null) {
1942 			menu.addEventListener("click", function() {
1943 				self.pvt();
1944 				return false;
1945 			});
1946 		}
1947 		if (document.getElementById(this.prefix + "toggleChime") != null) {
1948 			document.getElementById(this.prefix + "toggleChime").addEventListener("click", function() {
1949 				self.toggleChime();
1950 				if (self.playChime) {
1951 					document.getElementById('boxchime').src = SDK.url + "/images/sound.png";
1952 				} else {
1953 					document.getElementById('boxchime').src = SDK.url + "/images/mute.png";
1954 				}
1955 			});
1956 		}
1957 		if (document.getElementById(this.prefix + "toggleSpeak") != null) {
1958 			document.getElementById(this.prefix + "toggleSpeak").addEventListener("click", function() {
1959 				self.toggleSpeak();
1960 				if (self.speak) {
1961 					document.getElementById('boxtalk').src = SDK.url + "/images/talk.png";
1962 				} else {
1963 					document.getElementById('boxtalk').src = SDK.url + "/images/talkoff.png";
1964 				}
1965 				return false;
1966 			});
1967 		}
1968 		if (document.getElementById(this.prefix + "toggleListen") != null) {
1969 			document.getElementById(this.prefix + "toggleListen").addEventListener("click", function() {
1970 				listen = !listen;
1971 				if (listen) {
1972 					SDK.startSpeechRecognition();
1973 					document.getElementById('boxmic').src = SDK.url + "/images/mic.png";
1974 				} else {
1975 					SDK.stopSpeechRecognition();
1976 					document.getElementById('boxmic').src = SDK.url + "/images/micoff.png";
1977 				}
1978 				return false;
1979 			});
1980 		}
1981 		document.getElementById(this.prefix + "boxmin").addEventListener("click", function() {
1982 			self.minimizeBox();
1983 			return false;
1984 		});
1985 		if (this.promptContactInfo) {
1986 			document.getElementById(this.prefix + "contactboxmin").addEventListener("click", function() {
1987 				self.minimizeContactInfoBox();
1988 				return false;
1989 			});
1990 			document.getElementById(this.prefix + "contactconnect").addEventListener("click", function() {
1991 				self.contactConnect();
1992 				return false;
1993 			});
1994 		}
1995 		if (document.getElementById(this.prefix + "boxmax") != null) {
1996 			document.getElementById(this.prefix + "boxmax").addEventListener("click", function() {
1997 				self.popup();
1998 				return false;
1999 			});
2000 		}
2001 		if (this.showClose) {
2002 			document.getElementById(this.prefix + "boxclose").addEventListener("click", function() {
2003 				self.closeBox();
2004 				return false;
2005 			});
2006 			document.getElementById(this.prefix + "boxclose2").addEventListener("click", function() {
2007 				self.closeBox();
2008 				return false;
2009 			});
2010 			document.getElementById(this.prefix + "boxbarmax").addEventListener("click", function() {
2011 				self.maximizeBox();
2012 				return false;
2013 			});
2014 			document.getElementById(this.prefix + "boxbarmax2").addEventListener("click", function() {
2015 				self.maximizeBox();
2016 				return false;
2017 			});
2018 		} else {
2019 			document.getElementById(this.prefix + "boxbar").addEventListener("click", function() {
2020 				self.maximizeBox();
2021 				return false;
2022 			});
2023 		}
2024 	}
2025 	
2026 	/**
2027 	 * Minimize the live chat embedding box.
2028 	 */
2029 	this.minimizeBox = function() {
2030 		this.onlineBar = false;
2031 		if (this.promptContactInfo) {
2032 			document.getElementById(this.prefix + "contactinfo").style.display = 'none';
2033 		}
2034 		document.getElementById(this.prefix + "box").style.display = 'none';
2035 		var onlineDiv = document.getElementById(this.prefix + "online");
2036 		if (onlineDiv != null) {
2037 			onlineDiv.style.display = 'none';
2038 		}
2039 		if (this.promptEmailChatLog) {
2040 			document.getElementById(this.prefix + "emailchatlogemail").value = this.contactEmail;
2041 			document.getElementById(this.prefix + "emailchatlogdialog").style.display = 'inline';
2042 			return false;
2043 		}
2044 		document.getElementById(this.prefix + "boxbar").style.display = 'inline';
2045 		if (this.prefix.indexOf("livechat") != -1) {
2046 			var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("livechat")) + "boxbar");
2047 			if (chatbot != null) {
2048 				chatbot.style.display = 'inline';
2049 			}
2050 		}
2051 		if (this.prefix.indexOf("chat") != -1) {
2052 			var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("chat")) + "boxbar");
2053 			if (chatbot != null) {
2054 				chatbot.style.display = 'inline';
2055 			}
2056 		}
2057 		this.exit();
2058 		setTimeout(function() {
2059 		    self.exit();
2060 		}, 100);
2061 		return false;
2062 	}
2063 	
2064 	/**
2065 	 * Minimize the email chat log confirm dialog.
2066 	 */
2067 	this.minimizeEmailChatLogBox = function() {
2068 		if (this.promptContactInfo) {
2069 			document.getElementById(this.prefix + "contactinfo").style.display = 'none';
2070 		}
2071 		document.getElementById(this.prefix + "box").style.display = 'none';
2072 		document.getElementById(this.prefix + "emailchatlogdialog").style.display = 'none';
2073 		document.getElementById(this.prefix + "boxbar").style.display = 'inline';
2074 		if (this.prefix.indexOf("livechat") != -1) {
2075 			var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("livechat")) + "boxbar");
2076 			if (chatbot != null) {
2077 				chatbot.style.display = 'inline';
2078 			}
2079 		}
2080 		if (this.prefix.indexOf("chat") != -1) {
2081 			var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("chat")) + "boxbar");
2082 			if (chatbot != null) {
2083 				chatbot.style.display = 'inline';
2084 			}
2085 		}
2086 		this.exit();
2087 		setTimeout(function() {
2088 		    self.exit();
2089 		}, 100);
2090 		return false;
2091 	}
2092 	
2093 	/**
2094 	 * Minimize the email chat log confirm dialog.
2095 	 */
2096 	this.sendEmailChatLogBox = function() {
2097 		this.contactEmail = document.getElementById(this.prefix + "emailchatlogemail").value;
2098 		this.sendEmailChatLog();
2099 		if (this.promptContactInfo) {
2100 			document.getElementById(this.prefix + "contactinfo").style.display = 'none';
2101 		}
2102 		document.getElementById(this.prefix + "box").style.display = 'none';
2103 		document.getElementById(this.prefix + "emailchatlogdialog").style.display = 'none';
2104 		document.getElementById(this.prefix + "boxbar").style.display = 'inline';
2105 		if (this.prefix.indexOf("livechat") != -1) {
2106 			var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("livechat")) + "boxbar");
2107 			if (chatbot != null) {
2108 				chatbot.style.display = 'inline';
2109 			}
2110 		}
2111 		if (this.prefix.indexOf("chat") != -1) {
2112 			var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("chat")) + "boxbar");
2113 			if (chatbot != null) {
2114 				chatbot.style.display = 'inline';
2115 			}
2116 		}
2117 		setTimeout(function() {
2118 		    self.exit();
2119 		}, 100);
2120 		return false;
2121 	}
2122 	
2123 	/**
2124 	 * Minimize the contact info box.
2125 	 */
2126 	this.minimizeContactInfoBox = function() {
2127 		if (this.promptContactInfo) {
2128 			document.getElementById(this.prefix + "contactinfo").style.display = 'none';
2129 		}
2130 		document.getElementById(this.prefix + "box").style.display = 'none';
2131 		document.getElementById(this.prefix + "boxbar").style.display = 'inline';
2132 		if (this.prefix.indexOf("livechat") != -1) {
2133 			var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("livechat")) + "boxbar");
2134 			if (chatbot != null) {
2135 				chatbot.style.display = 'inline';
2136 			}
2137 		}
2138 		if (this.prefix.indexOf("chat") != -1) {
2139 			var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("chat")) + "boxbar");
2140 			if (chatbot != null) {
2141 				chatbot.style.display = 'inline';
2142 			}
2143 		}
2144 		return false;
2145 	}
2146 	
2147 	/**
2148 	 * Check contact info and connect.
2149 	 */
2150 	this.contactConnect = function() {
2151 		this.hasContactInfo = true;
2152 		this.contactName = document.getElementById(this.prefix + "contactname").value;
2153 		var ok = true;
2154 		if (this.contactName != null && this.contactName == "") {
2155 			ok = false;
2156 			document.getElementById(this.prefix + "contactname").style.borderColor = "red";
2157 			document.getElementById(this.prefix + "contactname").placeholder = SDK.translate("Enter name");
2158 		}
2159 		this.contactEmail = document.getElementById(this.prefix + "contactemail").value;
2160 		if (this.contactEmail != null && this.contactEmail.indexOf("@") == -1) {
2161 			ok = false;
2162 			document.getElementById(this.prefix + "contactemail").style.borderColor = "red";
2163 			document.getElementById(this.prefix + "contactemail").placeholder = SDK.translate("Enter valid email");
2164 		}
2165 		this.contactPhone = document.getElementById(this.prefix + "contactphone").value;
2166 		this.contactInfo = this.contactName + " " + this.contactEmail + " " + this.contactPhone;
2167 		if (ok) {
2168 			this.maximizeBox();
2169 		}
2170 	}
2171 	
2172 	/**
2173 	 * Maximize the embedding div in the current webpage.
2174 	 */
2175 	this.maximizeBox = function() {
2176 		this.onlineBar = true;
2177 		if (this.promptContactInfo && !this.hasContactInfo) {
2178 			document.getElementById(this.prefix + "contactinfo").style.display = 'inline';
2179 			document.getElementById(this.prefix + "boxbar").style.display = 'none';
2180 			document.getElementById(this.prefix + "box").style.display = 'none';
2181 			if (this.prefix.indexOf("livechat") != -1) {
2182 				var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("livechat")) + "boxbar");
2183 				if (chatbot != null) {
2184 					chatbot.style.display = 'none';
2185 				}
2186 			}
2187 			if (this.prefix.indexOf("chat") != -1) {
2188 				var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("chat")) + "boxbar");
2189 				if (chatbot != null) {
2190 					chatbot.style.display = 'none';
2191 				}
2192 			}
2193 		} else {
2194 			if (this.promptContactInfo) {
2195 				document.getElementById(this.prefix + "contactinfo").style.display = 'none';
2196 			}
2197 			document.getElementById(this.prefix + "boxbar").style.display = 'none';
2198 			document.getElementById(this.prefix + "box").style.display = 'inline';
2199 			if (this.prefix.indexOf("livechat") != -1) {
2200 				var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("livechat")) + "boxbar");
2201 				if (chatbot != null) {
2202 					chatbot.style.display = 'none';
2203 				}
2204 			}
2205 			if (this.prefix.indexOf("chat") != -1) {
2206 				var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("chat")) + "boxbar");
2207 				if (chatbot != null) {
2208 					chatbot.style.display = 'none';
2209 				}
2210 			}
2211 		    var chat = new LiveChatConnection();
2212 		    chat.sdk = this.sdk;
2213 		    if (this.contactInfo != null) {
2214 		    	chat.contactInfo = this.contactInfo;
2215 		    }
2216 		    var channel = new ChannelConfig();
2217 		    channel.id = this.instance;
2218 		    chat.listener = this;
2219 			chat.connect(channel, this.sdk.user);
2220 			var self = this;
2221 			if (this.autoAccept != null) {
2222 				setTimeout(function() {
2223 					chat.accept();
2224 				}, self.autoAccept);
2225 			}
2226 		}
2227 		return false;
2228 	}
2229 	
2230 	/**
2231 	 * Close the embedding div in the current webpage.
2232 	 */
2233 	this.closeBox = function() {
2234 		if (this.promptContactInfo) {
2235 			document.getElementById(this.prefix + "contactinfo").style.display = 'none';
2236 		}
2237 		document.getElementById(this.prefix + "boxbar").style.display = 'none';
2238 		document.getElementById(this.prefix + "box").style.display = 'none';
2239 		if (this.prefix.indexOf("livechat") != -1) {
2240 			var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("livechat")) + "boxbar");
2241 			if (chatbot != null) {
2242 				chatbot.style.display = 'none';
2243 			}
2244 		}
2245 		if (this.prefix.indexOf("chat") != -1) {
2246 			var chatbot = document.getElementById(this.prefix.substring(0, this.prefix.indexOf("chat")) + "boxbar");
2247 			if (chatbot != null) {
2248 				chatbot.style.display = 'none';
2249 			}
2250 		}
2251 		this.exit();
2252 		var self = this;
2253 		setTimeout(function() {
2254 		    self.exit();
2255 		}, 100);
2256 		return false;		
2257 	}
2258 	
2259 	/**
2260 	 * Create a popup window live chat session.
2261 	 */
2262 	this.popup = function() {
2263 		var box = document.getElementById(this.prefix + "box");
2264 		if (box != null) {
2265 			box.style.display = 'none';
2266 		}
2267 		if (this.popupURL != null) {
2268 			var popupURL = this.popupURL;
2269 			if (popupURL.indexOf("livechat?") != -1 && this.contactInfo != null && this.contactInfo != "") {
2270 				popupURL = popupURL + "&info=" + encodeURI(this.contactInfo);
2271 			}
2272 			SDK.popupwindow(popupURL, 'child', 700, 520);
2273 		} else {
2274 		    var form = document.createElement("form");
2275             form.setAttribute("method", "post");
2276             form.setAttribute("action", SDK.url + "/livechat");
2277             form.setAttribute("target", 'child');
2278  
2279             var input = document.createElement('input');
2280             input.type = "hidden";
2281             input.name = "id";
2282             input.value = this.instance;
2283             form.appendChild(input);
2284  
2285             input = document.createElement('input');
2286             input.type = "hidden";
2287             input.name = "embedded";
2288             input.value = "embedded";
2289             form.appendChild(input);
2290  
2291             input = document.createElement('input');
2292             input.type = "hidden";
2293             input.name = "chat";
2294             input.value = "true";
2295             form.appendChild(input);
2296  
2297             input = document.createElement('input');
2298             input.type = "hidden";
2299             input.name = "application";
2300             input.value = this.connection.credentials.applicationId;
2301             form.appendChild(input);
2302  
2303             input = document.createElement('input');
2304             input.type = "hidden";
2305             input.name = "css";
2306             input.value = this.css;
2307             form.appendChild(input);
2308  
2309             input = document.createElement('input');
2310             input.type = "hidden";
2311             input.name = "info";
2312             input.value = this.contactInfo;
2313             form.appendChild(input);
2314             
2315             document.body.appendChild(form);
2316             
2317 			SDK.popupwindow('','child', 700, 520);
2318 			
2319 			form.submit();
2320             document.body.removeChild(form);
2321 		}
2322 		this.minimizeBox();
2323 		return false;
2324 	}
2325 	
2326 	window.onfocus = function() {
2327 		self.isActive = true;
2328 		if (document.title != self.windowTitle) {
2329 			document.title = self.windowTitle;
2330 		}
2331 	}; 
2332 
2333 	window.onblur = function() {
2334 		self.isActive = false;
2335 	};
2336 
2337 	
2338 	/**
2339 	 * Search for link using <a href="chat:yes">...
2340 	 * Switch them to use an onclick to post the chat back to the chat.
2341 	 */
2342 	this.linkChatPostbacks = function(node) {
2343 		var self = this;
2344         var links = node.getElementsByTagName("a");
2345         for (var index = 0; index < links.length; index++) {
2346         	var a = links[index];
2347         	var href = a.getAttribute("href");
2348         	if (href != null && href.indexOf("chat:") != -1) {
2349         		var chat = href.substring("chat:".length, href.length).trim();
2350         		var temp = function(param, element) {
2351         			element.onclick = function() {
2352         				self.connection.sendMessage(param);
2353 	        			return false;
2354 	        		};
2355         		}
2356         		temp(chat, a);
2357         	}
2358         }
2359         var buttons = node.getElementsByTagName("button");
2360         for (var index = 0; index < buttons.length; index++) {
2361         	var button = buttons[index];
2362         	if (button.parentNode.nodeName == "A") {
2363         		continue;
2364         	}
2365         	var chat = button.textContent;
2366         	if (chat != null && chat.length > 0) {
2367         		var temp = function(param, element) {
2368         			element.onclick = function() {
2369 	        			self.connection.sendMessage(param);
2370 	        			return false;
2371 	        		};
2372         		}
2373         		temp(chat, button);
2374         	}
2375         }
2376         var choices = node.getElementsByTagName("select");
2377         for (var index = 0; index < choices.length; index++) {
2378         	var choice = choices[index];
2379     		var temp = function(param) {
2380     			param.addEventListener("change", function() {
2381         			self.connection.sendMessage(param.value);
2382         			return false;
2383         		});
2384     		}
2385     		temp(choice);
2386         }
2387 	}
2388 		
2389 	/**
2390 	 * A user message was received from the channel.
2391 	 */
2392 	this.message = function(message) {
2393 		var index = message.indexOf(':');
2394 		var speaker = '';
2395 		if (index != -1) {
2396 			speaker = message.substring(0, index + 1);
2397 			responseText = message.substring(index + 2, message.length);
2398 		} else {
2399 			responseText = message;
2400 		}
2401 		if (speaker != (this.nick + ':')) {
2402 			if (this.playChime) {
2403 				SDK.chime();
2404 			}
2405 			if (this.speak) {
2406 				SDK.tts(SDK.stripTags(responseText), this.voice, this.nativeVoice, this.lang, this.nativeVoiceName);
2407 			}
2408 			document.getElementById(this.prefix + 'response').innerHTML = SDK.linkURLs(responseText);
2409 			this.linkChatPostbacks(document.getElementById(this.prefix + 'response'));
2410 			// Fix Chrome bug,
2411 			if (SDK.fixChromeResizeCSS && SDK.isChrome()) {
2412 				var padding = document.getElementById(this.prefix + 'response').parentNode.parentNode.style.padding;
2413 		    	document.getElementById(this.prefix + 'response').parentNode.parentNode.style.padding = "7px";
2414 		    	var self = this;
2415 				setTimeout(function() {
2416 		    		document.getElementById(self.prefix + 'response').parentNode.parentNode.style.padding = padding;
2417 				}, 10);
2418 			}
2419 		    this.switchText = false;
2420 		} else {
2421 		    this.switchText = true;
2422 		}
2423 		var scroller = document.getElementById(this.prefix + 'scroller');
2424 		var consolepane = document.getElementById(this.prefix + 'console');
2425 		if (scroller == null || consolepane == null) {
2426 			return;
2427 		}
2428 		if (this.chatLog) {
2429 			var tr = document.createElement('tr');
2430 			tr.style.verticalAlign = "top";
2431 			var td = document.createElement('td');
2432 			var td2 = document.createElement('td');
2433 			var div = document.createElement('div');
2434 			var span = document.createElement('span');
2435 			var br = document.createElement('br');
2436 			var span2 = document.createElement('span');
2437 			var div2 = document.createElement('div');
2438 			var chatClass = this.prefix + 'chat-1';
2439 			div.className = this.prefix + 'chat-1-div';
2440 			div2.className = this.prefix + 'chat-1-div-2';
2441 			span.className = this.prefix + 'chat-user-1';
2442 			td.className = this.prefix + 'chat-user-1';
2443 			if (this.switchText) {
2444 				td.className = this.prefix + 'chat-user-2';
2445 				chatClass = this.prefix + 'chat-2';
2446 			    div.className = this.prefix + 'chat-2-div';
2447 			    div2.className = this.prefix + 'chat-2-div-2';
2448 			    span.className = this.prefix + 'chat-user-2';
2449 			}
2450 			var userImg = document.createElement('img');
2451 			userImg.className = this.prefix + 'chat-user';
2452 			var speakerName = speaker.slice(0, -1);
2453 			if (speakerName != "Info" && speakerName != "Error") {
2454 				for(var key in this.users) {
2455 					if (key === speakerName) {
2456 						userImg.setAttribute('alt', speakerName);
2457 						userImg.setAttribute('src', this.users[key]);
2458 						break;
2459 					}
2460 				}
2461 			}
2462 			td.appendChild(userImg);
2463 			td.setAttribute('nowrap', 'nowrap');
2464 			td2.className = chatClass;
2465 			td2.setAttribute('align', 'left');
2466 			td2.setAttribute('width', '100%');
2467 			
2468 			var date = new Date(); 
2469 			var time = date.getHours() + ":" + ((date.getMinutes() < 10)? "0" : "") + date.getMinutes() + ":" + ((date.getSeconds() < 10)? "0" : "") + date.getSeconds();
2470 			span.innerHTML = speaker + " <small>" + time + "</small>";
2471 			span2.className = chatClass;
2472 			span2.innerHTML = SDK.linkURLs(responseText);
2473 			this.linkChatPostbacks(span2);
2474 			consolepane.appendChild(tr);
2475 			
2476 			tr.appendChild(td);
2477 			tr.appendChild(td2);
2478 			div.appendChild(span);
2479 			div.appendChild(br);
2480 			div.appendChild(div2);
2481 			td2.appendChild(div);
2482 			div2.appendChild(span2);
2483 		}
2484 		this.switchText = !this.switchText;
2485 		while (consolepane.childNodes.length > 500) {
2486 			consolepane.removeChild(consolepane.firstChild);
2487 		}
2488 		scroller.scrollTop = scroller.scrollHeight;
2489 		if (this.focus) {
2490 			document.getElementById(this.prefix + 'chat').focus();
2491 		}
2492 		if (!this.isActive) {
2493 			document.title = SDK.stripTags(responseText);
2494 		}
2495 	};
2496 	
2497 	/**
2498 	 * An informational message was received from the channel.
2499 	 * Such as a new user joined, private request, etc.
2500 	 */	
2501 	this.info = function(message) {
2502 		if (this.connection.nick != null && this.connection.nick != "") {
2503 			this.nick = this.connection.nick;
2504 		}
2505 		this.message(message);
2506 	};
2507 
2508 	/**
2509 	 * An error message was received from the channel.
2510 	 * This could be an access error, or message failure.
2511 	 */	
2512 	this.error = function(message) {
2513 		this.message(message);
2514 	};
2515 	
2516 	/**
2517 	 * Notification that the connection was closed.
2518 	 */
2519 	this.closed = function() {};
2520 	
2521 	/**
2522 	 * The channels users changed (user joined, left, etc.)
2523 	 * This contains a comma separated values (CSV) list of the current channel users.
2524 	 * It can be passed to the SDKConnection.getUsers() API to obtain the UserConfig info for the users.
2525 	 */
2526 	this.updateUsers = function(usersCSV) {};
2527 
2528 	/**
2529 	 * The channels users changed (user joined, left, etc.)
2530 	 * This contains a HTML list of the current channel users.
2531 	 * It can be inserted into an HTML document to display the users.
2532 	 */
2533 	this.updateUsersXML = function(usersXML) {
2534 		if (!this.linkUsers) {
2535 			usersXML = usersXML.split('<a').join('<span');
2536 			usersXML = usersXML.split('</a>').join('</span>');
2537 		}
2538 		var onlineList = document.getElementById(this.prefix + 'online');
2539 		if (onlineList == null) {
2540 			return;
2541 		}
2542 		if (!this.chatLog) {
2543 			onlineList.style.height = "60px";
2544 		}
2545 		var div = document.createElement('div');
2546 		div.innerHTML = usersXML;
2547 		var children = div.childNodes[0].childNodes;
2548 		var usersArray = {};
2549 		var size = children.length;
2550 		for(var i = 0; i < size; i++) {
2551 			var userName = children[i].innerText;
2552 			var child = children[i].childNodes;
2553 			var imageSrc = child[0].getAttribute('src');
2554 			usersArray[userName] = imageSrc;
2555 		}
2556 		this.users = usersArray;
2557 		if (this.onlineBar) {
2558 			var onlineBar = onlineList;
2559 			onlineBar.innerHTML = '';
2560 			if (this.chatroom || this.isFrame) { // displaying list of users on top
2561 				var count = 0;
2562 				var ids = {};
2563 				var length = children.length;
2564 				for (var i = 0; i < length; i++) {
2565 					var child = children[i - count];
2566 					ids[child.id] = child.id;
2567 					if (document.getElementById(child.id) == null) {
2568 						onlineList.appendChild(child);
2569 						count++;
2570 					}
2571 				}
2572 				onlineList.style.margin = "0";
2573 				onlineList.style.display = 'inline';
2574 			}
2575 			else { // displaying only single bot on top
2576 				var length = children.length;
2577 				var child = children[length - 1];
2578 				var keys = [];
2579 				for(var keyItem in this.users) {
2580 					keys.push(keyItem);
2581 				}
2582 				var botName = keys[keys.length - 1];
2583 				var botImageSrc = this.users[botName];
2584 				if (typeof botName === 'undefined' || typeof botImageSrc === 'undefined') {
2585 					return;
2586 				}
2587 				var botImage = document.createElement('img');
2588 				botImage.className = this.prefix + "-bot-image";
2589 				botImage.setAttribute('alt', botName);
2590 				botImage.setAttribute('src', botImageSrc);
2591 				var botSpan = document.createElement('span');
2592 				botSpan.className = this.prefix + "user-bot";
2593 				botSpan.innerHTML = botName;
2594 				onlineBar.append(botImage);
2595 				onlineBar.append(botSpan);
2596 				if (!this.isFrame) {
2597 					onlineList.style.display = 'block';
2598 					var line = document.createElement('hr');
2599 					var onlineDiv = document.getElementById(this.prefix + 'online-div');
2600 					onlineDiv.appendChild(line);
2601 				}
2602 			}
2603 			return;
2604 		}
2605 		onlineList.innerHTML = '';
2606 		var div = document.createElement('div');
2607 		div.innerHTML = usersXML;
2608 		var children = div.childNodes[0].childNodes;
2609 		var count = 0;
2610 		var length = children.length;
2611 		var ids = {};
2612 		// Add missing user
2613 		for (var i = 0; i < length; i++) {
2614 			var child = children[i - count];
2615 			ids[child.id] = child.id;
2616 			if (document.getElementById(child.id) == null) {
2617 				onlineList.appendChild(child);
2618 				count++;
2619 			}
2620 		}
2621 		// Remove missing users
2622 		var onlineDiv = document.getElementById(this.prefix + 'online-div');
2623 		if (onlineDiv == null) {
2624 			return;
2625 		}
2626 		children = onlineDiv.childNodes;
2627 		count = 0;
2628 		length = children.length;
2629 		for (var i = 0; i < length; i++) {
2630 			var child = children[i - count];
2631 			if (child.id != (this.prefix + 'online') && ids[child.id] == null) {
2632 				onlineDiv.removeChild(child);
2633 				count++;
2634 			}
2635 		}
2636 	};
2637 
2638 	/**
2639 	 * Decrease the size of the video element for the userid.
2640 	 */
2641 	this.shrinkVideo = function(user) {
2642 	    var id = 'user-' + encodeURIComponent(user);
2643 	    var userdiv = document.getElementById(id);
2644 	    if (userdiv != null) {
2645 	    	var media = userdiv.firstElementChild;	    	
2646 			if (media != null) {
2647 				media.height = media.height / 1.5;
2648 			}
2649 		}
2650 	};
2651 
2652 	/**
2653 	 * Increase the size of the video element for the userid.
2654 	 */
2655 	this.expandVideo = function(user) {
2656 	    var id = 'user-' + encodeURIComponent(user);
2657 	    var userdiv = document.getElementById(id);
2658 	    if (userdiv != null) {
2659 	    	var media = userdiv.firstElementChild;	    	
2660 			if (media != null) {
2661 				media.height = media.height * 1.5;
2662 			}
2663 		}
2664 	};
2665 
2666 	/**
2667 	 * Mute the audio for the userid.
2668 	 */
2669 	this.muteAudio = function(user) {
2670 	    var id = 'user-' + encodeURIComponent(user);
2671 	    var userdiv = document.getElementById(id);
2672 	    if (userdiv != null) {
2673 	    	var media = userdiv.firstElementChild;	    	
2674 			if (media != null) {
2675 				if (media.muted) {
2676 					if (user != this.nick) {
2677 						media.muted = false;
2678 					}
2679 				} else {
2680 					media.muted = true;					
2681 				}
2682 			}
2683 		}
2684 	};
2685 
2686 	/**
2687 	 * Mute the video for the userid.
2688 	 */
2689 	this.muteVideo = function(user) {
2690 	    var id = 'user-' + encodeURIComponent(user);
2691 	    var userdiv = document.getElementById(id);
2692 	    if (userdiv != null) {
2693 	    	var media = userdiv.firstElementChild;	    	
2694 			if (media != null) {
2695 				if (media.paused) {
2696 					media.play();
2697 					media.style.opacity = 100;
2698 				} else {
2699 					media.pause();
2700 					media.style.opacity = 0;					
2701 				}
2702 			}
2703 		}
2704 	};
2705 
2706 	this.toggleChime = function() {
2707 		this.playChime = !this.playChime;
2708 	}
2709 
2710 	this.toggleSpeak = function() {
2711 		this.speak = !this.speak;
2712 	}
2713 
2714 	this.toggleKeepAlive = function() {
2715 		this.connection.toggleKeepAlive();
2716 	}
2717 	
2718 	this.sendMessage = function() {
2719 		var message = document.getElementById(this.prefix + 'chat').value;
2720 		if (message != '') {
2721 			this.connection.sendMessage(message);
2722 			document.getElementById(this.prefix + 'chat').value = '';
2723 		}
2724 		return false;
2725 	};
2726 
2727 	this.sendImage = function() {
2728 		if (!(window.File && window.FileReader && window.FileList && window.Blob)) {
2729 			alert('The File APIs are not fully supported in this browser.');
2730 			return false;
2731 		}
2732 		var form = document.createElement("form");
2733 		form.enctype = "multipart/form-data";
2734 		form.method = "post";
2735 		form.name = "fileinfo";
2736 		var fileInput = document.createElement("input");
2737 		var self = this;
2738 		fileInput.name = "file";
2739 		fileInput.type = "file";
2740 		form.appendChild(fileInput);
2741 		fileInput.onchange = function() {
2742 			var file = fileInput.files[0];
2743 			self.connection.sendAttachment(file, true, form);
2744 		}
2745 		fileInput.click();
2746 		return false;
2747 	};
2748 
2749 	this.sendAttachment = function() {
2750 		if (!(window.File && window.FileReader && window.FileList && window.Blob)) {
2751 			alert('The File APIs are not fully supported in this browser.');
2752 			return false;
2753 		}
2754 		var form = document.createElement("form");
2755 		form.enctype = "multipart/form-data";
2756 		form.method = "post";
2757 		form.name = "fileinfo";
2758 		var fileInput = document.createElement("input");
2759 		var self = this;
2760 		fileInput.name = "file";
2761 		fileInput.type = "file";
2762 		form.appendChild(fileInput);
2763 		fileInput.onchange = function() {
2764 			var file = fileInput.files[0];
2765 			self.connection.sendAttachment(file, false, form);
2766 		}
2767 		fileInput.click();
2768 		return false;
2769 	};
2770 
2771 	this.ping = function() {
2772 		this.connection.ping();
2773 		document.getElementById(this.prefix + 'chat').value = '';
2774 		return false;
2775 	};
2776 
2777 	this.accept = function() {
2778 		this.connection.accept();
2779 		document.getElementById(this.prefix + 'chat').value = '';
2780 		return false;
2781 	};
2782 
2783 	this.exit = function() {
2784 		if (this.connection != null) {
2785 			this.connection.exit();
2786 			document.getElementById(this.prefix + 'chat').value = '';
2787 		}
2788 		return false;
2789 	};
2790 
2791 	this.spyMode = function() {
2792 		this.connection.spyMode();
2793 		document.getElementById(this.prefix + 'chat').value = '';
2794 		return false;
2795 	};
2796 
2797 	this.normalMode = function() {
2798 		this.connection.normalMode();
2799 		document.getElementById(this.prefix + 'chat').value = '';
2800 		return false;
2801 	};
2802 
2803 	this.boot = function() {
2804 		document.getElementById(this.prefix + 'chat').value = 'boot: user';
2805 		return false;
2806 	};
2807 
2808 	this.emailChatLog = function() {
2809 		document.getElementById(this.prefix + 'chat').value = 'email: ' + (this.contactEmail == null ? 'user@domain.com' : this.contactEmail);
2810 		return false;
2811 	};
2812 
2813 	this.sendEmailChatLog = function() {
2814 		this.connection.sendMessage('email: ' + this.contactEmail);
2815 		return false;
2816 	};
2817 
2818 	this.whisper = function(user) {
2819 		if (user == null) {
2820 			user = 'user';
2821 		}
2822 		document.getElementById(this.prefix + 'chat').value = 'whisper: ' + user + ': message';
2823 		return false;
2824 	};
2825 
2826 	this.flag = function(user) {
2827 		if (user != null) {
2828 			document.getElementById(this.prefix + 'chat').value = 'flag: ' + user + ': reason';
2829 			return false;
2830 		}
2831 		document.getElementById(this.prefix + 'chat').value = 'flag: user: reason';
2832 		return false;
2833 	};
2834 
2835 	this.pvt = function(user) {
2836 		if (user != null) {
2837 			this.connection.pvt(user);
2838 			return false;
2839 		}
2840 		document.getElementById(this.prefix + 'chat').value = 'private: user';
2841 		return false;
2842 	};
2843 
2844 	this.clear = function() {
2845 		document.getElementById(this.prefix + 'response').innerHTML = '';
2846 		var console = document.getElementById(this.prefix + 'console');
2847 		if (console != null) {
2848 			console.innerHTML = '';
2849 		}
2850 		return false;
2851 	};
2852 }
2853 
2854 /**
2855  * Shared method for updating an avatar image/video/audio from the chat response.
2856  */
2857 SDK.updateAvatar = function(responseMessage, speak, urlprefix, elementPrefix, channelaudio, afterFunction, nativeVoice, lang, voice) {
2858 	try {
2859 
2860 	var noMedia = false;
2861 	if (SDK.canPlayVideo == null) {
2862 		if (!SDK.isMobile()) {
2863 			SDK.canPlayVideo = true;
2864 		} else {
2865 			SDK.canPlayVideo = false;
2866 			SDK.canPlayAudio = true;
2867 			// Check if auto play has been disable by the browser (mobile Chrome/Safari)
2868 			setTimeout(function() {
2869 				if (noMedia) {
2870 					SDK.canPlayVideo = null;
2871 					SDK.canPlayAudio = null;
2872 				} else {
2873 					if (SDK.canPlayVideo == false) {
2874 						SDK.initVideo = function() {
2875 							SDK.canPlayVideo = true;
2876 							SDK.audio = new Audio(SDK.url + '/chime.mp3');
2877 							SDK.audio.load();
2878 							SDK.autoPlayActionAudio = new Audio(SDK.url + '/chime.mp3');
2879 							SDK.autoPlayActionAudio.load();
2880 							SDK.autoPlayBackgroundAudio = new Audio(SDK.url + '/chime.mp3');
2881 							SDK.autoPlayBackgroundAudio.load();
2882 							SDK.updateAvatar(responseMessage, speak, urlprefix, elementPrefix, channelaudio, afterFunction, nativeVoice, lang, voice);
2883 							document.getElementById("sdkplaybutton2").style.display = "none";
2884 						};
2885 						var body = document.body || document.getElementsByTagName('body')[0];
2886 						var playButton = document.createElement('div');
2887 						var html = "<div id='sdkplaybutton2' style='position:fixed;bottom:32px;left:32px;z-index:164;'><img onclick='SDK.initVideo()' width='64' src='"
2888 							+ SDK.url + "/images/playsound.png'/></div>"
2889 						playButton.innerHTML = html;
2890 						body.appendChild(playButton);
2891 						setTimeout(function() {
2892 							document.getElementById("sdkplaybutton2").style.display = "none";
2893 						}, 10000);
2894 					}
2895 				}
2896 			}, SDK.autoPlayDelay);
2897 		}
2898 	}
2899 	nativeVoice = nativeVoice && SDK.speechSynthesis;
2900 	if (elementPrefix == null) {
2901 		elementPrefix = "";
2902 	}
2903 	var avatarStatus = document.getElementById(elementPrefix + "avatar-status");
2904 	if (avatarStatus != null) {
2905 		var status = "";
2906 		if (responseMessage.emote != null && responseMessage.emote != "" && responseMessage.emote != "NONE") {
2907 			status = responseMessage.emote.toLowerCase();
2908 		}
2909 		if (responseMessage.action != null && responseMessage.action != "") {
2910 			if (status != "") {
2911 				status = status + " : ";
2912 			}
2913 			status = status + responseMessage.action;
2914 		}
2915 		if (responseMessage.pose != null && responseMessage.pose != "") {
2916 			if (status != "") {
2917 				status = status + " : ";
2918 			}
2919 			status = status + responseMessage.pose;
2920 		}
2921 		avatarStatus.innerHTML = status;
2922 	}
2923 	if (responseMessage.avatarActionAudio != null && speak) {
2924 		var audio = SDK.autoPlayActionAudio;
2925 		if (audio == null) {
2926 			audio = new Audio(urlprefix + responseMessage.avatarActionAudio);
2927 		} else {
2928 			audio.src = urlprefix + responseMessage.avatarActionAudio;
2929 		}
2930 		if (SDK.canPlayVideo == null) {
2931 			audio.onplaying = new function() {
2932 			    SDK.canPlayVideo = true;
2933 			}
2934 		}
2935 		audio.play();
2936 	}
2937 	if (!speak || SDK.currentBackgroundAudio != responseMessage.avatarAudio) {
2938 		// Only switch if different audio.
2939 		if (SDK.backgroundAudio != null) {
2940 			SDK.backgroundAudio.pause();
2941 			SDK.currentBackgroundAudio = null;
2942 		}
2943 		if (responseMessage.avatarAudio != null && speak) {
2944 			SDK.currentBackgroundAudio = responseMessage.avatarAudio;
2945 			SDK.backgroundAudio = SDK.autoPlayBackgroundAudio;
2946 			if (SDK.backgroundAudio == null) {
2947 				SDK.backgroundAudio = new Audio(urlprefix + responseMessage.avatarAudio);
2948 			} else {
2949 				SDK.backgroundAudio.src = urlprefix + responseMessage.avatarAudio;
2950 			}
2951 			SDK.backgroundAudio.loop = true;
2952 			if (SDK.canPlayVideo == null) {
2953 				SDK.backgroundAudio.onplaying = new function() {
2954 				    document.getElementById("native-voice-name").value = "onplaying-back";
2955 					SDK.canPlayVideo = true;
2956 				}
2957 			}
2958 			SDK.backgroundAudio.play();
2959 		}
2960 	}
2961 	var video = document.getElementById(elementPrefix + "avatar-video");
2962 	var isVideo = responseMessage.avatarType != null && responseMessage.avatarType.indexOf("video") != -1;
2963 	var useVideo = video != null && SDK.useVideo != false && (SDK.useVideo == true || !(SDK.isSafari() && SDK.isIPhone()));
2964 	if (isVideo && useVideo) {
2965 		var div = document.getElementById(elementPrefix + "avatar-image-div");
2966 		if (div != null) {
2967 			div.style.display = "none";
2968 		}
2969 		div = document.getElementById(elementPrefix + "avatar-game-div");
2970 		if (div != null) {
2971 			div.style.display = "none";
2972 		}
2973 		div = document.getElementById(elementPrefix + "avatar-video-div");
2974 		var canvas = null;
2975 		if (div != null) {
2976 			div.style.display = "inline-block";
2977 			if (responseMessage.avatarBackground != null) {
2978 				div.style.backgroundImage = "url(" + urlprefix + responseMessage.avatarBackground + ")";
2979 			}
2980 			var canvasDiv = document.getElementById(elementPrefix + "avatar-canvas-div");
2981 			if (((SDK.isChrome() && !SDK.isMobile()) || (SDK.isFirefox() && !SDK.isMac()) || SDK.useCanvas == true) && SDK.useCanvas != false && canvasDiv != null) {
2982 				div.style.position = "fixed";
2983 				div.style.top = "-1000";
2984 				div.style.left = "-1000";
2985 				div.style.opacity = "0";
2986 				div.style.zIndex = "-1";
2987 				canvasDiv.style.display = "inline-block";
2988 				canvas = document.getElementById(elementPrefix + "avatar-canvas");
2989 			}
2990 		}
2991 		if (SDK.canPlayVideo == null) {
2992 			video.onplaying = new function() {
2993 			    document.getElementById("native-voice-name").value = "onplaying-video";
2994 				SDK.canPlayVideo = true;
2995 			}
2996 		}
2997 		if (responseMessage.avatar.indexOf("mp4") != -1 && (SDK.isChrome() || SDK.isFirefox() || SDK.fixBrightness != true) && SDK.fixBrightness != false) {
2998 			// Hack to fix grey background in Chrome.
2999 			if (SDK.isChrome()) {
3000 				video.style.webkitFilter = "brightness(108.5%)";
3001 			} else {
3002 				video.style["filter"] = "brightness(1.085)";
3003 			}
3004 			if (canvas != null) {
3005 				if (SDK.isChrome()) {
3006 					canvas.style.webkitFilter = "brightness(108.5%)";
3007 				} else {
3008 					video.style["filter"] = "brightness(1.085)";
3009 				}
3010 			}
3011 		}
3012 		if (canvas == null) {
3013 			if (responseMessage.avatarBackground != null) {
3014 				video.poster = urlprefix + responseMessage.avatarBackground;				
3015 			}
3016 		}
3017 		var context = null;
3018 		var drawCanvas = null;
3019 		if (canvas != null) {
3020 		    context = canvas.getContext('2d');
3021 			if (SDK.timers[elementPrefix + "avatar-canvas"] == null) {
3022 				drawCanvas = function() {
3023 				    if (!video.paused && !video.ended && video.currentTime > 0) {
3024 				    	if (canvas.width != video.offsetWidth) {
3025 				    		canvas.width = video.offsetWidth;
3026 				    	}
3027 				    	if (canvas.height != video.offsetHeight) {
3028 				    		canvas.height = video.offsetHeight;
3029 				    	}
3030 				    	context.clearRect(0, 0, canvas.width, canvas.height);
3031 				    	context.drawImage(video, 0, 0, video.offsetWidth, video.offsetHeight);
3032 				    }
3033 				}
3034 				SDK.timers[elementPrefix + "avatar-canvas"] = drawCanvas;
3035 				setInterval(drawCanvas, 20);				
3036 			}
3037 		}
3038 		var end = function() {
3039 			video.src = urlprefix + responseMessage.avatar;
3040 			if (responseMessage.avatar2 == null) {
3041 				video.loop = true;
3042 			} else {
3043 				video.loop = false;
3044 				video.onended = function() {
3045 					var index = Math.floor(Math.random() * 5);
3046 					if (index == 4 && responseMessage.avatar5 != null) {
3047 						video.src = urlprefix + responseMessage.avatar5;
3048 					} else if (index == 3 && responseMessage.avatar4 != null) {
3049 						video.src = urlprefix + responseMessage.avatar4;
3050 					} else if (index == 2 && responseMessage.avatar3 != null) {
3051 						video.src = urlprefix + responseMessage.avatar3;
3052 					} else if (index == 1 && responseMessage.avatar2 != null) {
3053 						video.src = urlprefix + responseMessage.avatar2;
3054 					} else {
3055 						video.src = urlprefix + responseMessage.avatar;
3056 					}
3057 					video.play();
3058 				}
3059 			}
3060 			video.play();
3061 			if (afterFunction != null) {
3062 				afterFunction();
3063 			}
3064 		}
3065 		var talk = function() {
3066 			if (responseMessage.message != null && responseMessage.message.length > 0) {
3067 				if (responseMessage.avatarTalk != null) {
3068 					if (speak) {
3069 						if (responseMessage.speech == null && !nativeVoice) {
3070 							end();
3071 						} else {
3072 							video.src = urlprefix + responseMessage.avatar;
3073 							video.loop = true;
3074 							var playing = false;
3075 							video.play();
3076 	
3077 							if (nativeVoice) {
3078 								if ('SpeechSynthesisUtterance' in window) {
3079 									utterance = new SpeechSynthesisUtterance(SDK.stripTags(responseMessage.message));
3080 								} else {
3081 									utterance = new SpeechSynthesisUtterance2(SDK.stripTags(responseMessage.message));
3082 								}
3083 								SDK.utterance = utterance;
3084 								utterance.onstart = function() {
3085 									if (playing) {
3086 										return false;
3087 									}
3088 									if ('speechSynthesis' in window) {
3089 										speechSynthesis.pause();
3090 									}
3091 									video.src = urlprefix + responseMessage.avatarTalk;
3092 									video.loop = true;
3093 									video.oncanplay = function() {
3094 										if (playing) {
3095 											return false;
3096 										}
3097 										playing = true;
3098 										if ('speechSynthesis' in window) {
3099 											speechSynthesis.resume();
3100 										}
3101 									}
3102 									video.play();
3103 								}
3104 								utterance.onerror = function() {
3105 									console.log("error");
3106 									end();
3107 								}
3108 								utterance.onend = function() {
3109 									end();
3110 								}
3111 								
3112 								SDK.nativeTTS(utterance, lang, voice);
3113 							} else {
3114 								//var audio = new Audio(urlprefix + responseMessage.speech, channelaudio);
3115 								var audio = SDK.play(urlprefix + responseMessage.speech, channelaudio);
3116 								//audio.onabort = function() {console.log("abort");}
3117 								audio.oncanplay = function() {
3118 									if (playing) {
3119 										return false;
3120 									}
3121 									audio.pause();
3122 									video.src = urlprefix + responseMessage.avatarTalk;
3123 									video.loop = true;
3124 									video.oncanplay = function() {
3125 										if (playing) {
3126 											return false;
3127 										}
3128 										playing = true;
3129 										audio.play();
3130 									}
3131 									video.play();
3132 								}
3133 								audio.onerror = function() {
3134 									console.log("error");
3135 									end();
3136 								}
3137 								//audio.onloadeddata = function() {console.log("loadeddata");}
3138 								//audio.onloadedmetadata = function() {console.log("loadedmetadata");}
3139 								//audio.onpause = function() {console.log("pause");}
3140 								//audio.onplay = function() {console.log("play");}
3141 								//audio.onplaying = function() {console.log("playing");}
3142 								//audio.ontimeupdate = function() {console.log("timeupdate");}
3143 								var onended = audio.onended;
3144 								audio.onended = function() {
3145 									if (onended != null) {
3146 										onended();
3147 									}
3148 									end();
3149 								}
3150 								audio.play();
3151 								video.play();
3152 							}
3153 						}
3154 					} else {
3155 						video.src = urlprefix + responseMessage.avatarTalk;
3156 						video.loop = false;
3157 						video.play();
3158 						var onended = video.onended;
3159 						video.onended = function() {
3160 							if (onended != null) {
3161 								onended();
3162 							}
3163 							end();
3164 						}
3165 					}
3166 				} else {
3167 					video.src = urlprefix + responseMessage.avatar;
3168 					video.loop = true;
3169 					video.play();
3170 					if (speak) {
3171 						if (nativeVoice) {
3172 							if ('SpeechSynthesisUtterance' in window) {
3173 								utterance = new SpeechSynthesisUtterance(SDK.stripTags(responseMessage.message));
3174 							} else {
3175 								utterance = new SpeechSynthesisUtterance2(SDK.stripTags(responseMessage.message));
3176 							}
3177 							utterance.onend = afterFunction;
3178 							SDK.nativeTTS(utterance, lang, voice);
3179 						} else {
3180 							var audio = SDK.play(urlprefix + responseMessage.speech, channelaudio);
3181 							var onended = audio.onended;
3182 							audio.onended = function() {
3183 								if (onended != null) {
3184 									onended();
3185 								}
3186 								if (afterFunction != null) {
3187 									afterFunction();
3188 								}
3189 							}
3190 						}
3191 					} else if (afterFunction != null) {
3192 						afterFunction();			
3193 					}
3194 				}
3195 			} else {
3196 				end();
3197 			}
3198 		}
3199 		
3200 		if (responseMessage.avatarAction != null) {
3201 			video.src = urlprefix + responseMessage.avatarAction;
3202 			video.loop = false;
3203 			video.play();
3204 			var onended = video.onended;
3205 			video.onended = function() {
3206 				if (onended != null) {
3207 					onended();
3208 				}
3209 				talk();
3210 			}
3211 		} else {
3212 			talk();
3213 		}
3214 	} else {
3215 		var div = document.getElementById(elementPrefix + "avatar-video-div");
3216 		if (div != null) {
3217 			div.style.display = "none";
3218 		}
3219 		div = document.getElementById(elementPrefix + "avatar-canvas-div");
3220 		if (div != null) {
3221 			div.style.display = "none";
3222 		}
3223 		div = document.getElementById(elementPrefix + "avatar-game-div");
3224 		if (div != null) {
3225 			div.style.display = "none";
3226 		}
3227 		div = document.getElementById(elementPrefix + "avatar-image-div");
3228 		if (div != null) {
3229 			div.style.display = "inline-block";
3230 		}
3231 		var img = document.getElementById(elementPrefix + 'avatar');
3232 		if (img != null) {
3233 			if (isVideo) {
3234 				img.src = urlprefix + responseMessage.avatarBackground;
3235 			} else {
3236 				img.src = urlprefix + responseMessage.avatar;
3237 			}
3238 		}
3239 		img = document.getElementById(elementPrefix + 'avatar2');
3240 		if (img != null) {
3241 			if (isVideo) {
3242 				img.src = urlprefix + responseMessage.avatarBackground;
3243 			} else {
3244 				img.src = urlprefix + responseMessage.avatar;
3245 			}
3246 		}
3247 		if (speak && responseMessage.message != null && responseMessage.message.length > 0) {
3248 			if (nativeVoice) {
3249 				noMedia = true;
3250 				SDK.tts(SDK.stripTags(responseMessage.message), null, true, lang, voice);
3251 			} else if (responseMessage.speech != null) {
3252 				var audio = SDK.play(urlprefix + responseMessage.speech, channelaudio);
3253 				var onended = audio.onended;
3254 				audio.onended = function() {
3255 					if (onended != null) {
3256 						onended();
3257 					}
3258 					if (afterFunction != null) {
3259 						afterFunction();
3260 					}
3261 				}
3262 			}
3263 		} else {
3264 			noMedia = true;
3265 			if (afterFunction != null) {
3266 				afterFunction();
3267 			}
3268 		}
3269 	}
3270 	
3271 	} catch(err) {
3272 	}
3273 }
3274 
3275 /**
3276  * The WebChatbotListener provides an integration between a chat bot conversation through a SDKConnection and an HTML document.
3277  * It updates the document to messages received from the connection, and sends messages from the document's form.
3278  * The HTML document requires the following elements:
3279  * <ul>
3280  * <li> chat - input (type='text') element for sending messages
3281  * <li> send - input (type='submit') button for sending chat input
3282  * <li> response - p element paragraph for last chat message
3283  * <li> console - table element for chat log, and avatar
3284  * <li> scroller - div element for chat log scroll pane
3285  * <li> avatar - img element for the bot's avatar (optional)
3286  * <li> avatar2 - img element hover img for the bot's avatar (optional)
3287  * <li> avatar-image-div - div element for the bot's image (optional)
3288  * <li> avatar-video - video element for the bot's video (optional)
3289  * <li> avatar-video-div - div element for the bot's video (optional)
3290  * <li> avatar-status - span element for the bot's current status (optional)
3291  * </ul>
3292  * If a prefix is set, these id will be prefixed by the prefix.
3293  * Or you can call createBox() to have the WebChatbotListener create its own components in the current page.
3294  * @class
3295  */
3296 function WebChatbotListener() {
3297 	/** Set the caption for the button bar button. */
3298 	this.caption = null;
3299 	/** Disallow speech. */
3300 	this.allowSpeech = true;
3301 	/** Disallow image/file upload menus and buttons. */
3302 	this.allowFiles = true;
3303 	/** Add image/file upload buttons to toolbar. */
3304 	this.showFileButtons = false;
3305 	/** Remove menubar. */
3306 	this.showMenubar = true;
3307 	/** Show Box Max*/
3308 	this.showBoxmax = true;
3309 	/** Show Send Image*/
3310 	this.showSendImage = true;
3311 	/** Remove language choice. */
3312 	this.showChooseLanguage = true;
3313 	/** Enable or disable speech. */
3314 	this.speak = true;
3315 	/** Configure if the browser's native voice TTS should be used. */
3316 	this.nativeVoice = false;
3317 	/** Set the voice name for the native voice. */
3318 	this.nativeVoiceName = null;
3319 	/** Set the language for the native voice. */
3320 	this.lang = null;
3321 	/** Translate between the user's language, and the bot's language. */
3322 	this.translate = false;
3323 	/** Enable or disable avatar. */
3324 	this.avatar = true;
3325 	/** Set the avatar. */
3326 	this.avatarId = null;
3327 	/** Set if the avatar should request HD (high def) video/images. */
3328 	this.hd = null;
3329 	/** Set if the avatar should request a specific video or image format. */
3330 	this.format = null;
3331 	/** A SDK connection must be set, be sure to include your app id. */
3332 	this.connection = null;
3333 	/** The id or name of the bot instance to connect to. */
3334 	this.instance = null;
3335 	/** The name to display for the bot. */
3336 	this.instanceName = "Bot";
3337 	/** The name to display for the user. */
3338 	this.userName = "You";
3339 	/** Allow the button color to be set. */
3340 	this.color = "#009900";
3341 	/** Allow the different style sheet options */
3342 	this.version = null;
3343 	/** Set if the box chat log should be shown. */
3344 	this.chatLog = true;
3345 	/** Allow the hover button color to be set. */
3346 	this.hoverColor = "grey";
3347 	/** Allow the background color to be set. */
3348 	this.background = null;
3349 	/** Avatar image/video width. */
3350 	this.width = 300;
3351 	/** Avatar image/video height. */
3352 	this.height = null;
3353 	/** Chat bar offest from side. */
3354 	this.offset = 30;
3355 	/** Chat Button Vertial Offset*/
3356 	this.verticalOffset = 0;
3357 	/** Only apply the background color if not Chrome. */
3358 	this.backgroundIfNotChrome = false;
3359 	/** onresponse event is raised after a response is received. */
3360 	this.onresponse = null;
3361 	/** Configure if chat should be given focus after response. */
3362 	this.focus = true;
3363 	/** Override the URL used in the chat bot box popup. */
3364 	this.popupURL = null;
3365 	/** Print response in chat bubble. */
3366 	this.bubble = false;
3367 	/** Initial message to send to the bot. */
3368 	this.greetingMessage = null;
3369 	/** Initial message to display from the bot. (it is normally better to set a greeting in the bot instead). */
3370 	this.greeting = null;
3371 	/** Loading message to display. */
3372 	this.loading = "loading...";
3373 	/** Element id and class prefix. This allows an id and class prefix to avoid name collisions on the element names for the chat, response, console, and avatar elements.*/
3374 	this.prefix = "";
3375 	/** This can be used to keep the bot's chat bar in synch with a livechat bar. */
3376 	this.livechatPrefix = null;
3377 	/** Allows the bot's thumbnail image to be set for chat log. */
3378 	this.botThumb = {};
3379 	/** Allows the user's thumbnail image to be set for chat log. */
3380 	this.userThumb = {};
3381 	/** Set styles explictly to avoid inheriting page styles. Disable this to be able to override styles. */
3382 	this.forceStyles = true;
3383 	/** Add additional css style code. */
3384 	this.style = "";
3385 	/** Set the location of the button and box, one of "bottom-right", "bottom-left", "top-right", "top-left". */
3386 	this.boxLocation = "bottom-right";
3387 	/** Prompt for name/email before connecting. */
3388 	this.promptContactInfo = false;
3389 	this.hasContactInfo = false;
3390 	this.contactName = null;
3391 	this.contactEmail = null;
3392 	this.contactPhone = null;
3393 	this.contactInfo = "";
3394 	/** Set if the backlink should be displayed. */
3395 	this.backlink = SDK.backlink;
3396 
3397 	/** Support connections to external bots through their web API. */
3398 	this.external = false;
3399 	this.apiURL = null;
3400 	this.apiPost = null;
3401 	this.apiResponse = null;
3402 	
3403 	this.switchText = true;
3404 	this.big = false;
3405 	this.conversation = null;
3406 	this.voiceInit = null;
3407 	this.listen = false;
3408 		
3409 	/**
3410 	 * Create an embedding bar and div in the current webpage.
3411 	 */
3412 	this.createBox = function() {
3413 		if (this.livechatPrefix == null) {
3414 			if (this.version >= 6.0) {
3415 				this.livechatPrefix = "chat";
3416 			} else {
3417 				this.livechatPrefix = "livechat";
3418 			}
3419 		}
3420 		if (this.prefix == "" && this.elementPrefix != null) {
3421 			this.prefix = this.elementPrefix;
3422 		}
3423 		if (this.caption == null) {
3424 			this.caption = this.instanceName;
3425 		}
3426 		var backgroundstyle = "";
3427 		var buttonstyle = "";
3428 		var buttonHoverStyle = "";
3429 		var hidden = "hidden";
3430 		var border = "";
3431 		if (this.backgroundIfNotChrome && SDK.isChrome()) {
3432 			this.background = null;
3433 		}
3434 		if (this.background != null) {
3435 			backgroundstyle = " style='background-color:" + this.background + "'";
3436 			hidden = "visible";
3437 			border = "border:1px;border-style:solid;border-color:black;";
3438 		}
3439 		if (this.color != null) {
3440 			buttonstyle = "background-color:" + this.color + ";";
3441 		}
3442 		if (this.hoverColor != null) {
3443 			buttonHoverStyle = "background-color:" + this.hoverColor + ";";
3444 		}
3445 		var minWidth = "";
3446 		var divWidth = "";
3447 		var background = "";
3448 		var minHeight = "";
3449 		var divHeight = "";
3450 		var maxDivWidth = "";
3451 		var maxHeight = null;
3452 		var responseWidth = "";
3453 		var chatWidth = "";
3454 		var hideAvatar = "";
3455 		var avatarWidth = this.width;
3456 		var minAvatarWidth = "";
3457 		var scrollerHeight = this.height;
3458 		var scrollerMinHeight = "";
3459 		if (this.width != null) {
3460 			if (typeof this.width === "string") {
3461 				this.width = parseInt(width);
3462 			}
3463 			// Auto correct for a short window or screen (assuming square).
3464 			// 250 is total box height minus avatar.
3465 			if ((this.width + 250) > window.innerHeight) {
3466 				avatarWidth = window.innerHeight - 250;
3467 				if (avatarWidth < 100) {
3468 					hideAvatar = "display:none";
3469 				}
3470 			}
3471 			minWidth = "width:" + this.width + "px;";
3472 			minAvatarWidth = "width:" + avatarWidth + "px;";
3473 			background = "background-size:" + avatarWidth + "px auto;";
3474 			divWidth = minWidth;
3475 			divHeight = "min-height:" + avatarWidth + "px;";
3476 			responseWidth = "width:" + (this.width - 16) + "px;";
3477 			chatWidth = "width:" + this.width + "px;";
3478 			maxDivWidth = "max-width:" + (this.width - 50) + "px;";
3479 			scrollerHeight = avatarWidth;
3480 		}
3481 		if (this.height != null) {
3482 			if (typeof this.height === "string") {
3483 				this.height = parseInt(height);
3484 			}
3485 			minHeight = "height:" + this.height + "px;";
3486 			divHeight = minHeight;
3487 			if (this.width != null) {
3488 				background = "background-size:" + this.width + "px " + this.height + "px;";
3489 			} else {
3490 				background = "background-size: auto " + this.height + "px;";
3491 				divWidth = "min-width:" + this.height + "px;";
3492 				responseWidth = "width:" + (this.height - 16) + "px;";
3493 				chatWidth = "width:" + this.height + "px;";
3494 			}
3495 		} else {
3496 			scrollerMinHeight = "height:" + scrollerHeight + "px;";
3497 		}
3498 		var inputFont = "";
3499 		if (SDK.isMobile()) {
3500 			inputFont = "font-size: 16px;";
3501 		}
3502 		var boxloc = "bottom:10px;right:10px";
3503         if (this.boxLocation == "top-left") {
3504             boxloc = "top:10px;left:10px";
3505         } else if (this.boxLocation == "top-right") {
3506             boxloc = "top:10px;right:10px";
3507         } else if (this.boxLocation == "bottom-left") {
3508             boxloc = "bottom:10px;left:10px";
3509         } else if (this.boxLocation == "bottom-right") {
3510             boxloc = "bottom:10px;right:10px";
3511         }
3512         var locationBottom = 20;
3513         if (this.version < 6.0 || this.prefix != "botplatform") {
3514         	locationBottom = 2;
3515         }
3516         var boxbarloc = "bottom:" + (locationBottom + this.verticalOffset) + "px;right:" + this.offset + "px";
3517         if (this.boxLocation == "top-left") {
3518             boxbarloc = "top:" + (locationBottom + this.verticalOffset) + "px;left:" + this.offset + "px";
3519         } else if (this.boxLocation == "top-right") {
3520             boxbarloc = "top:" + (locationBottom + this.verticalOffset) + "px;right:" + this.offset + "px";
3521         } else if (this.boxLocation == "bottom-left") {
3522             boxbarloc = "bottom:" + (locationBottom + this.verticalOffset) + "px;left:" + this.offset + "px";
3523         } else if (this.boxLocation == "bottom-right") {
3524             boxbarloc = "bottom:" + (locationBottom + this.verticalOffset) + "px;right:" + this.offset + "px";
3525         }
3526 		var box = document.createElement('div');	
3527 		var html =
3528 			"<style>\n"
3529 				+ "." + this.prefix + "box { position:fixed;" + boxloc + ";z-index:152;margin:2px;display:none;" + border + " }\n"
3530 				+ "." + this.prefix + "boxmenu { visibility:" + hidden + "; }\n"
3531 				+ (this.forceStyles ? "#" : ".") + "" + this.prefix + "boxbarmax { font-size:18px;margin:2px;padding:0px;text-decoration:none; }\n"
3532 				+ "." + this.prefix + "boxbar { position:fixed;" + boxbarloc + ";z-index:152;margin:0;padding:6px;" + buttonstyle + " }\n"
3533 				+ "." + this.prefix + "boxbar:hover { " + buttonHoverStyle + " }\n"
3534 				+ "." + this.prefix + "no-bubble-text { " + responseWidth + "; max-height:100px; overflow:auto; }\n"
3535 				+ "#" + this.prefix + "contactinfo { " + minHeight + minWidth + " }\n"
3536 				+ "." + this.prefix + "contactconnect { margin:4px;padding:8px;color:white;text-decoration:none;" + buttonstyle + " }\n"
3537 				+ "." + this.prefix + "scroller { overflow-x:hidden;" + scrollerMinHeight + minWidth + " }\n"
353