/****************************************************************************** * * Copyright 2014-2021 Paphus Solutions Inc. * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ /** * Bot Libre open SDK. * This JavaScript SDK lets you access chat bot, live chat, chatroom, forum, script, graphic, user services on * the Bot Libre compatible websites, including: * - Bot Libre * - Bot Libre for Business * - Bot Libre Community Edition * - Bot Libre Enterprise * * This JavaScript script can be used directly, or copied/modified on your own website. * * The SDK consist of two main class, SDKConnection and LiveChatConnection. * * SDKConnection uses AJAX calls to provide access to the Bot Libre REST API. * This is used for chat bots, live chat, chatrooms, forums, issue tracking, user admin, and workspaces. * * LiveChatConnection uses web sockets to provide access to live chat and chatrooms. * * Version: 9.0.0-2022-01-27 */ /** * Static class for common util functions and static properties. * @class */ var SDK = {}; // Production server SDK.DOMAIN = "virtualdreamchat.com"; SDK.NAME = "Virtual Dream Chat"; SDK.APP = ""; SDK.scheme = "https"; // Development server (comment for production or uncomment development) //SDK.DOMAIN = window.location.host; //SDK.DOMAIN = "144.217.65.190"; //SDK.APP = "/virtualdreamchat"; //SDK.scheme = "https:" == document.location.protocol ? "https" : "http"; SDK.PATH = "/rest/api"; SDK.MAX_FILE_UPLOAD = 5000000; SDK.host = SDK.DOMAIN; SDK.app = SDK.APP; SDK.url = SDK.scheme + "://" + SDK.DOMAIN + SDK.APP; SDK.rest = SDK.url + SDK.PATH; SDK.backlinkURL = SDK.url; SDK.backlink = true; SDK.commands = true; SDK.turnServer = null; SDK.turnUser = null; SDK.turnPassword = null; /** * You must set your application ID to use the SDK. * You can obtain your application ID from your user page. * @static */ SDK.applicationId = null; /** * Set the active language code. * This is used for voice recognition. */ SDK.lang = "en"; /** * Enable debug logging. * @static */ SDK.debug = false; /** * Escape and filter bot messages for HTML and JavaScript content for XSS security. * This prevents bots sending advanced HTML and JavaScript in their messages. * Set this to false to allow your bot to send JavaScript. * @static */ SDK.secure = true; /** * Force avatars to enable or disable canvas for video (currently used only for Chrome and Firefox). * @static */ SDK.useCanvas = null; /** * Force avatars to enable or disable video (currently disabled for Safari on iPhone). * @static */ SDK.useVideo = null; /** * Set the background for video avatars. * @static */ SDK.videoBackground = true; /** * Attempt to fix grey mp4 video background (only used for Chrome). * @static */ SDK.fixBrightness = null; /** * Attempt to fix an issue with Chrome not processing the CSS after correctly when the chat bubble resizes. * @static */ SDK.fixChromeResizeCSS = false; // Polyfill IE if (!String.prototype.includes) { String.prototype.includes = function(search, start) { if (typeof start !== 'number') { start = 0; } if (start + search.length > this.length) { return false; } else { return this.indexOf(search, start) !== -1; } }; } /** * Set the error static field to trap or log any errors. */ SDK.error = function(message) { console.log(message); } /** * Allow our native speech API to use the third party ResponsiveVoice API. * You must create an account with ResponsiveVoice to use their API, see https://responsivevoice.com * @static */ SDK.responsiveVoice = false; SDK.speechSynthesis = 'speechSynthesis' in window; /** * The speechRate can be set to change the native speech voice speed. * It can range between 0.1 (lowest) and 10.0 (highest). * 1.0 is the default rate for the current platform or voice. * Other values act as a percentage relative to this, so for example 2.0 is twice as fast, 0.5 is half as fast. */ SDK.speechRate = null; /** * The speechPitch can be set to change the native speech voice pitch. * It can range between 0.0 (lowest) and 2.0 (highest), with 1.0 being the default pitch for the current platform or voice. */ SDK.speechRate = null; SDK.initResponsiveVoice = function() { if (!('responsiveVoice' in window)) { console.log("ResponsiveVoice missing, you must load its script first"); return; } SDK.responsiveVoice = true; SDK.speechSynthesis = true; } if (!('SpeechSynthesisUtterance' in window)) { function SpeechSynthesisUtterance2(text) { this.text = text; } } /** * Allow our native speech API to use the third party Bing Speech API. You must * create an account with Bing Speech to use their API, see * https://azure.microsoft.com/en-us/try/cognitive-services * * @static */ SDK.bingSpeech = false; SDK.initBingSpeech = function(instanceId, type) { SDK.bingSpeech = true; SDK.speechSynthesis = true; SDK.speechInstance = instanceId; SDK.speechType = type; console.log("Initializing Bing speech."); } /** * Allow our native speech API to use the third party QQ Speech API. * * @static */ SDK.qqSpeech = false; SDK.initQQSpeech = function(instanceId, type) { SDK.qqSpeech = true; SDK.speechSynthesis = true; SDK.speechInstance = instanceId; SDK.speechType = type; console.log("Initializing QQ speech."); } /** * Allow our native speech API to use the third party Google Speech API. * * @static */ SDK.googleSpeech = false; SDK.initGoogleSpeech = function(instanceId, type) { SDK.googleSpeech = true; SDK.speechSynthesis = true; SDK.speechInstance = instanceId; SDK.speechType = type; console.log("Initializing Google speech."); } SDK.currentAudio = null; SDK.recognition = null; SDK.recognitionActive = false; SDK.backgroundAudio = null; SDK.currentBackgroundAudio = null; SDK.timers = {}; /** * Track if auto play of media is enabled in the browser (mobile Chrome/Safari) * Enable or disable to force audio auto play. */ SDK.canPlayAudio = null; /** * Track if auto play of media is enabled in the browser (mobile Chrome/Safari) * Enable or disable to force video auto play. */ SDK.canPlayVideo = null; SDK.disableAudioAutoPlay = false; SDK.audio = null; SDK.autoPlayActionAudio = null; SDK.autoPlayBackgroundAudio = null; SDK.autoPlayDelay = 2000; /** * For some browsers audio must be initialized from a click event. */ SDK.initAudio = function() { SDK.canPlayVideo = true; SDK.canPlayAudio = true; if (SDK.audio == null) { SDK.audio = new Audio(SDK.url + '/chime.mp3'); SDK.audio.load(); } if (SDK.autoPlayActionAudio == null) { SDK.autoPlayActionAudio = new Audio(SDK.url + '/chime.mp3'); SDK.autoPlayActionAudio.load(); SDK.autoPlayBackgroundAudio = new Audio(SDK.url + '/chime.mp3'); SDK.autoPlayBackgroundAudio.load(); } } /** * Play the audio file given the url. */ SDK.play = function(file, channelaudio, baseRoot) { var enableShadowDOM = true; SDK.pauseSpeechRecognition(); var audio = null; if (SDK.audio != null) { audio = SDK.audio; audio.pause(); audio.onended = null; audio.onpause = null; audio.oncanplay = null; audio.src = file; } else { audio = new Audio(file); } if (SDK.recognitionActive) { audio.onended = function() { SDK.startSpeechRecognition(); if (channelaudio != false) { SDK.currentAudio = null; } }; } else if (channelaudio != false) { audio.onended = function() { SDK.currentAudio = null; }; } var playPromise = null; if (channelaudio == false) { playPromise = audio.play(); } else { if (SDK.currentAudio != null && !SDK.currentAudio.ended && !SDK.currentAudio.paused) { SDK.currentAudio.onpause = function() { SDK.currentAudio = audio; playPromise = audio.play(); }; SDK.currentAudio.pause(); } else { SDK.currentAudio = audio; playPromise = audio.play(); } } if (playPromise !== undefined && SDK.canPlayAudio == null) { playPromise.then(function() { SDK.canPlayAudio = true; }).catch(function(error) { if (SDK.canPlayAudio == null) { SDK.canPlayAudio = false; var playButton = document.createElement('div'); var html = "
"; playButton.innerHTML = html; SDK.appendChild(baseRoot, playButton); setTimeout(function() { baseRoot.getElementById("sdkplaybutton").style.display = "none"; }, 10000); SDK.playInitAudio = function() { if (SDK.playInitVideo != null) { SDK.playInitVideo(); } else { SDK.audio = new Audio(file); SDK.currentAudio = SDK.audio; SDK.currentAudio.onended = function() { SDK.currentAudio = null; }; SDK.canPlayAudio = true; SDK.audio.play(); baseRoot.getElementById("sdkplaybutton").style.display = "none"; var sdkvideoplaybutton2 = baseRoot.getElementById("sdkvideoplaybutton2"); if (sdkvideoplaybutton2 != null) { sdkvideoplaybutton2.style.display = "none"; } } } } }); } return audio; } SDK.playChime = true; /** * Play the chime sound. */ SDK.chime = function() { if (SDK.playChime) { this.play(SDK.url + '/chime.mp3', null, document); SDK.playChime = false; var timer = setInterval(function () { SDK.playChime = true; clearInterval(timer); }, 1000); } } /** * Convert the text to speech and play it either using the browser native TTS support, or as server generated an audio file. * The voice is optional and can be any voice supported by the server (see the voice page for a list of voices). * For native voices a language code can be given. * If the browser supports TTS the native voice will be used by default. */ SDK.tts = function(text, voice, native, lang, nativeVoice, mod, apiKey, apiEndpoint, baseRoot) { try { if ((native || (native == null && voice == null)) && SDK.speechSynthesis) { var utterance = null; if ('SpeechSynthesisUtterance' in window) { utterance = new SpeechSynthesisUtterance(text); } else { utterance = new SpeechSynthesisUtterance2(text); } SDK.nativeTTS(utterance, lang, nativeVoice, apiKey, apiEndpoint, baseRoot); } else { var url = SDK.rest + '/form-speak?&text='; url = url + encodeURIComponent(text); if (voice != null) { url = url + '&voice=' + voice; } if (mod != null) { url = url + '&mod=' + mod; } if (SDK.applicationId != null) { url = url + '&application=' + SDK.applicationId; } var request = new XMLHttpRequest(); var self = this; request.onreadystatechange = function() { if (request.readyState != 4) return; if (request.status != 200) { console.log('Error: Speech web request failed'); return; } SDK.play(SDK.url + "/" + request.responseText, null, baseRoot); } request.open('GET', url, true); request.send(); } } catch (error) { console.log('Error: Speech web request failed'); } } /** * Use the ResponsiveVoice API. */ SDK.responsiveVoiceTTS = function(utterance, lang, voice) { var events = {}; try { SDK.pauseSpeechRecognition(); if (voice == null || voice == "") { voice = "US English Female"; } if (utterance.onend != null) { events.onend = utterance.onend; } if (SDK.recognitionActive) { events.onend = function() { SDK.startSpeechRecognition(); if (utterance.onend != null) { utterance.onend(); } } } if (utterance.onstart != null) { events.onstart = utterance.onstart; } responsiveVoice.speak(utterance.text, voice, events); } catch (error) { console.log(error); } } /** * Use the Bing Speech API. */ SDK.bingSpeechTTS = function(utterance, lang, voice, apiKey, apiEndpoint, baseRoot) { try { if(utterance==null || utterance.text=="") { return; } SDK.pauseSpeechRecognition(); if (voice == null || voice == "") { voice = "en-US, JessaRUS"; } var url = SDK.rest + '/form-speak?&text='; url = url + encodeURIComponent(utterance.text); if (SDK.applicationId != null) { url = url + '&application=' + SDK.applicationId; } if (SDK.speechInstance != null) { url = url + '&instance=' + SDK.speechInstance; } if(SDK.speechType == "avatar") { url = url + '&embeddedAvatar=true'; } if (apiKey != null) { url = url + '&apiKey=' + apiKey; } if (apiEndpoint != null) { url = url + '&apiEndpoint=' + apiEndpoint; } url = url + '&voice=' + encodeURIComponent(voice); url = url + '&provider=bing'; var request = new XMLHttpRequest(); var self = this; request.onreadystatechange = function() { if (request.readyState != 4) return; if (request.status != 200) { if('bingApiKeyTr' in window) { SDK.showError("Invalid API Key or API Endpoint"); } console.log('Error: Bing Speech web request failed: ' + request.statusText); } var audio = SDK.play(SDK.url + "/" + request.responseText, null, baseRoot); audio.onplay = utterance.onstart; audio.onended = utterance.onend; } request.open('GET', url, true); request.send(); } catch (error) { console.log(error); } } /** * Use the QQ Speech API. */ SDK.qqSpeechTTS = function(utterance, lang, voice, baseRoot) { try { SDK.pauseSpeechRecognition(); var url = SDK.rest + '/form-speak?&text='; url = url + encodeURIComponent(utterance.text); if (SDK.applicationId != null) { url = url + '&application=' + SDK.applicationId; } if (SDK.speechInstance != null) { url = url + '&instance=' + SDK.speechInstance; } if(SDK.speechType == "avatar") { url = url + '&embeddedAvatar=true'; } if (voice != null) { url = url + '&voice=' + voice; } url = url + '&provider=qq'; var request = new XMLHttpRequest(); var self = this; request.onreadystatechange = function() { if (request.readyState != 4) return; if (request.status != 200) { console.log('Error: QQ Speech web request failed.'); return; } var audio = SDK.play(SDK.url + "/" + request.responseText, null, baseRoot); audio.onplay = utterance.onstart; audio.onended = utterance.onend; } request.open('GET', url, true); request.send(); } catch (error) { console.log(error); } } /** * Use the Google Speech API. */ SDK.googleSpeechTTS = function(utterance, voice, apiKey, baseRoot) { try { SDK.pauseSpeechRecognition(); var url = SDK.rest + '/form-speak?&text='; url = url + encodeURIComponent(utterance.text); if (SDK.applicationId != null) { url = url + '&application=' + SDK.applicationId; } if (SDK.speechInstance != null) { url = url + '&instance=' + SDK.speechInstance; } if(SDK.speechType == "avatar") { url = url + '&embeddedAvatar=true'; } if (voice != null) { url = url + '&voice=' + voice; } if (apiKey != null) { url = url + '&apiKey=' + apiKey; } url = url + '&provider=google'; var request = new XMLHttpRequest(); var self = this; request.onreadystatechange = function() { if (request.readyState != 4) return; if (request.status != 200) { SDK.showError("Invalid API key or voice."); console.log('Error: Google Speech web request failed.'); return; } var audio = SDK.play(SDK.url + "/" + request.responseText, null, baseRoot); audio.onplay = utterance.onstart; audio.onended = utterance.onend; } request.open('GET', url, true); request.send(); } catch (error) { console.log(error); } } /** * Speak the native utterance first setting the voice and language. */ SDK.nativeTTS = function(utterance, lang, voice, apiKey, apiEndpoint, baseRoot) { if (baseRoot == null) { baseRoot = document; } if (SDK.speechRate != null) { utterance.rate = SDK.speechRate; } if (SDK.speechPitch != null) { utterance.pitch = SDK.speechPitch; } if (SDK.responsiveVoice) { SDK.responsiveVoiceTTS(utterance, lang, voice); return; } else if (SDK.bingSpeech) { SDK.bingSpeechTTS(utterance, lang, voice, apiKey, apiEndpoint, baseRoot); return; } else if (SDK.qqSpeech) { SDK.qqSpeechTTS(utterance, lang, voice, baseRoot); return; } else if (SDK.googleSpeech){ SDK.googleSpeechTTS(utterance, voice, apiKey, baseRoot); return; } if (lang == null) { lang = SDK.lang; } SDK.pauseSpeechRecognition(); if (SDK.recognitionActive) { utterance.addEventListener("end", function() { SDK.startSpeechRecognition(); }); } speechSynthesis.cancel(); if (lang == null && voice == null) { // Events don't always get fired unless this is done... setTimeout(function() { speechSynthesis.speak(utterance); }, 100); return; } var voices = speechSynthesis.getVoices(); var foundVoice = null; var foundLang = null; var spoken = false; if (voices.length == 0) { speechSynthesis.onvoiceschanged = function() { if (spoken) { return; } voices = speechSynthesis.getVoices(); for (i = 0; i < voices.length; i++) { if (voice != null && (voice.length != 0) && voices[i].name.toLowerCase().indexOf(voice.toLowerCase()) != -1) { if (foundVoice == null || voices[i].name == voice) { foundVoice = voices[i]; } } else if (lang != null && (lang.length != 0) && voices[i].lang.toLowerCase().indexOf(lang.toLowerCase()) != -1) { if (foundLang == null || voices[i].lang == lang) { foundLang = voices[i]; } } } if (foundVoice != null) { utterance.voice = foundVoice; } else if (foundLang != null) { utterance.voice = foundLang; } spoken = true; setTimeout(function() { speechSynthesis.speak(utterance); },100); }; } else { for (i = 0; i < voices.length; i++) { if (voice != null && (voice.length != 0) && voices[i].name.toLowerCase().indexOf(voice.toLowerCase()) != -1) { if (foundVoice == null || voices[i].name == voice) { foundVoice = voices[i]; } } else if (lang != null && (lang.length != 0) && voices[i].lang.toLowerCase().indexOf(lang.toLowerCase()) != -1) { if (foundLang == null || voices[i].lang == lang) { foundLang = voices[i]; } } } if (foundVoice != null) { utterance.voice = foundVoice; } else if (foundLang != null) { utterance.voice = foundLang; } setTimeout(function() { speechSynthesis.speak(utterance); },100); } } /** * Allow text to be translated into another language is the interface elements. */ SDK.translator = null; SDK.translate = function(text) { if (SDK.translator == null) { SDK.translator = SDK.translators[SDK.lang]; if (SDK.translator == null) { SDK.translator = {}; } } var translated = SDK.translator[text]; if (translated != null) { return translated; } return text } SDK.translators = { "pt" : { "Yes" : "Sim", "No" : "Não", "Name" : "Nome", "Email" : "O email", "Phone" : "Telemóvel", "Connect" : "Ligar", "Speech" : "Discurso", "Enter name" : "Insira o nome", "Chat Log" : "Registro de bate-papo", "Choose Language" : "Escolha o seu idioma", "Enter valid email" : "Digite e-mail válido", "Ping server" : "Servidor Ping", "Flag user" : "Flag user", "Whisper user" : "Usuário Whisper", "Request private" : "Pedido privado", "Clear log" : "Log clara", "Accept private" : "Aceite privado", "Send image" : "Enviar imagem", "Send file" : "Enviar arquivo", "Email Chat Log" : "Registro de bate-papo por email", "Chime" : "Chime", "Exit chat" : "Sair do bate-papo", "Text to speech" : "Texto para fala", "Speech recognition" : "Reconhecimento de fala", "Speech Recognition" : "Reconhecimento de fala", "Quit private channel" : "Saia do canal privado", "Quit private or channel" : "Sair privado ou canal", "Would you like a copy of the chat log sent to your email?" : "Gostaria de uma cópia do registro de bate-papo enviado para o seu e-mail?" }, "fr" : { "Yes" : "Oui", "No" : "Non", "Name" : "Prénom", "Email" : "Email", "Phone" : "Téléphone", "Connect" : "Relier", "Speech" : "Discours", "Enter name" : "Entrez le nom", "Chat Log" : "Journal de chat", "Choose Language" : "Choisir la langue", "Enter valid email" : "Entrez une adresse email valide", "Ping server" : "Serveur ping", "Flag user" : "Utilisateur du drapeau", "Whisper user" : "Whisper utilisateur", "Request private" : "Demander privé", "Clear log" : "Effacer le journal", "Accept private" : "Accepter privé", "Send image" : "Envoyer une image", "Send file" : "Envoyer le fichier", "Email Chat Log" : "Journal de messagerie électronique", "Chime" : "Carillon", "Exit chat" : "Quitter le chat", "Text to speech" : "Texte pour parler", "Speech recognition" : "Reconnaissance vocale", "Speech Recognition" : "Reconnaissance vocale", "Quit private channel" : "Quitter la chaîne privée", "Quit private or channel" : "Quitter privé ou canal", "Would you like a copy of the chat log sent to your email?" : "Souhaitez-vous recevoir une copie du journal de chat envoyé à votre adresse e-mail?" }, "es" : { "Yes" : "Sí", "No" : "No", "Name" : "Nombre", "Email" : "Email", "Phone" : "Teléfono", "Connect" : "Conectar", "Speech" : "Discurso", "Enter name" : "Ingrese el nombre", "Chat Log" : "Registro de chat", "Choose Language" : "Elija el idioma", "Enter valid email" : "Ingrese un correo electrónico válido", "Ping server" : "Servidor Ping", "Flag user" : "Usuario de bandera", "Whisper user" : "Usuario de Whisper", "Request private" : "Solicitud privada", "Clear log" : "Borrar registro", "Accept private" : "Aceptar privado", "Send image" : "Enviar imagen", "Send file" : "Enviar archivo", "Email Chat Log" : "Registro de chat de correo electrónico", "Chime" : "Campaneo", "Exit chat" : "Salir de chat", "Text to speech" : "Texto a voz", "Speech recognition" : "Reconocimiento de voz", "Speech Recognition" : "Reconocimiento de voz", "Quit private channel" : "Salir del canal privado", "Quit private or channel" : "Salir de privado o canal", "Would you like a copy of the chat log sent to your email?" : "Desea enviar un mensaje a su dirección de correo electrónico?" }, "de" : { "Yes" : "Ja", "No" : "Nein", "Name" : "Name", "Email": "Email", "Phone" : "Telefon", "Connect" : "Verbinden", "Speech" : "Rede", "Enter name" : "Name eingeben", "Chat Log" : "Chat Protokoll", "Choose Language" : "Wähle eine Sprache", "Enter valid email" : "Geben Sie gültige E-Mail-Adresse ein", "Ping server" : "Ping-Server", "Flag user" : "Benutzer kennzeichnen", "Whisper user" : "Flüstern Benutzer", "Request private" : "Privat anfragen", "Clear log" : "Protokoll löschen", "Accept private" : "Akzeptiere privat", "Send image" : "Bild senden", "Send file" : "Datei senden", "Email Chat Log" : "E-Mail-Chatprotokoll", "Chime" : "Glockenspiel", "Exit chat" : "Chat beenden", "Text to speech" : "Text zu Sprache", "Speech recognition" : "Spracherkennung", "Speech Recognition" : "Spracherkennung", "Quit private channel" : "Beenden Sie den privaten Kanal", "Quit private or channel" : "Beenden Sie private oder Kanal", "Would you like a copy of the chat log sent to your email?" : "Möchten Sie eine Kopie des Chat-Protokolls an Ihre E-Mail-Adresse senden?" }, "zh" : { "Yes" : "是", "No" : "沒有", "Name" : "名稱", "Email" : "電子郵件", "Phone" : "電話", "Connect" : "連", "Speech" : "言語", "Enter name" : "輸入名字", "Chat Log" : "聊天記錄", "Choose Language" : "選擇語言", "Enter valid email" : "輸入有效的郵件", "Ping server" : "叮噹服務器", "Flag user" : "標記用戶", "Whisper user" : "耳語用戶", "Request private" : "請求私人", "Clear log" : "清除日誌", "Accept private" : "接受私人", "Send image" : "發送圖像", "Send file" : "發送文件", "Email Chat Log" : "電子郵件聊天日誌", "Chime" : "鐘", "Exit chat" : "退出聊天", "Text to speech" : "文字轉語音", "Speech recognition" : "語音識別", "Speech Recognition" : "語音識別", "Quit private channel" : "退出私人頻道", "Quit private or channel" : "退出私人或頻道", "Would you like a copy of the chat log sent to your email?" : "你想要發送到你的電子郵件的聊天記錄的副本嗎?" }, "ja" : { "Yes" : "はい", "No" : "いいえ", "Name" : "名", "Email" : "Eメール", "Phone" : "電話", "Connect" : "接続する", "Disconnect" : "切断する", "Speech" : "スピーチ", "Enter name" : "名前を入力", "Chat Log" : "チャットログ", "Clear log" : "ログをクリアする", "Choose Language" : "言語を選択する", "Enter valid email" : "有効なメールアドレスを入力", "Ping server" : "リングサーバー", "Flag user" : "ユーザーにフラグを設定する", "Whisper user" : "ささやくユーザー", "Request private" : "プライベートをリクエストする", "Accept private" : "プライベートを受け入れる", "Send image" : "画像を送る", "Send file" : "ファイルを送信", "Upload image" : "画像をアップロードする", "Upload file" : "ファイルをアップロードする", "Email Chat Log" : "メールチャットログ", "Chime" : "チャイム", "Exit chat" : "チャットを終了", "Text to speech" : "スピーチテキスト", "Speech recognition" : "音声認識", "Speech Recognition" : "音声認識", "Quit private channel" : "プライベートチャンネルを終了する", "Quit private or channel" : "プライベートまたはチャンネルを終了する", "Would you like a copy of the chat log sent to your email?" : "チャットログのコピーをメールに送信しますか?" }, "ar" : { "Yes" : "نعم فعلا", "No" : "لا", "Name" : "اسم", "Email" : "البريد الإلكتروني", "Phone" : "", "Connect" : "هاتف", "Disconnect" : "قطع الاتصال", "Speech" : "خطاب", "Enter name" : "أدخل الاسم", "Chat Log" : "سجل الدردشة", "Clear log" : "سجل نظيف", "Choose Language" : "اختر اللغة", "Enter valid email" : "أدخل بريد إلكتروني صالحا", "Ping server" : "خادم بينغ", "Flag user" : "مستخدم العلم", "Whisper user" : "الهمس المستخدم", "Request private" : "طلب خاص", "Clear Log" : "سجل نظيف", "Accept private" : "قبول خاص", "Send image" : "إرسال صورة", "Send file" : "إرسال ملف", "Email Chat Log" : "سجل الدردشة عبر البريد الإلكتروني", "Chime" : "قرع الأجراس", "Exit chat" : "الخروج من الدردشة", "Text to speech" : "النص إلى الكلام", "Speech Recognition" : "التعرف على الكلام", "Quit private channel" : "إنهاء القناة الخاصة", "Quit private or channel": "إنهاء خاص أو قناة", "Would you like a copy of the chat log sent to your email?" : "هل تريد إرسال نسخة من سجل الدردشة إلى بريدك الإلكتروني؟" }, "ru" : { "Yes" : "Да", "No" : "Hет", "Name" : "Имя", "Email": "Эл. почта", "Phone" : "Телефон", "Connect" : "Cоединить", "Disconnect" : "Отключить", "Speech" : "Pечь", "Enter name" : "Введите имя", "Chat Log" : "Журнал чата", "Choose Language" : "Выберите язык", "Enter valid email" : "Введите действующий адрес электронной почты", "Ping server" : "Пинг сервер", "Flag user" : "Oтметить пользователя", "Whisper user" : "Прошептать пользователю", "Request private" : "Частный запрос", "Clear log" : "Очистить журнал", "Clear Log" : "Очистить журнал", "Accept private" : "Принять конфиденциальнo", "Send image" : "Отправить изображение", "Send file" : "Отправить файл", "Email Chat Log" : "Oтправить журнал чата по электронной почте", "Chime" : "Звонок", "Exit chat" : "Выход из чата", "Text to speech" : "Текст в речь", "Speech Recognition" : "Распознавание речи", "Quit private channel" : "Выйти из частного канала", "Quit private or channel": "Выйти из частного канала", "Would you like a copy of the chat log sent to your email?" : "Oтправить копю журнала чата на ваш адрес электронной почты?" } } /** * Detect Chrome browser. */ SDK.isChrome = function() { var agent = navigator.userAgent.toLowerCase(); return agent.indexOf('chrome') != -1 && agent.indexOf('edg') == -1; } /** * Detect Firefox browser. */ SDK.isFirefox = function() { return navigator.userAgent.toLowerCase().indexOf('firefox') != -1; } /** * Detect Safari browser. */ SDK.isSafari = function() { var agent = navigator.userAgent.toLowerCase(); return agent.indexOf('safari') != -1 && agent.indexOf('edg') == -1; } /** * Detect Internet Explorer browser. */ SDK.isMSIE = function() { var agent = navigator.userAgent.toLowerCase(); return agent.indexOf('msie') != -1 || agent.indexOf('trident') != -1; } /** * Detect Edge browser. */ SDK.isEdge = function() { return navigator.userAgent.toLowerCase().indexOf('edg') != -1; } /** * Detect mobile browser. */ SDK.isMobile = function() { if (navigator.userAgent.match(/Android/i) || navigator.userAgent.match(/webOS/i) || navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i) || navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/BlackBerry/i) || navigator.userAgent.match(/Windows Phone/i)) { return true; } else { return false; } } /** * Detect iPhone OS. */ SDK.isIPhone = function() { if (navigator.userAgent.match(/iPhone/i)) { return true; } return false; } /** * Detect Mac OS. */ SDK.isMac = function() { return navigator.platform.toLowerCase().indexOf('mac') != -1; } SDK.hd = false; SDK.format = (SDK.isChrome() || SDK.isFirefox()) ? "webm" : "mp4"; // // Safari displays HTML5 video very poorly on iPhone. // if (SDK.isSafari() && SDK.isIPhone()) { // SDK.format = "img"; // } /** * Insert the text into the input field. */ SDK.insertAtCaret = function(element, text) { if (document.selection) { element.focus(); var sel = document.selection.createRange(); sel.text = text; element.focus(); } else if (element.selectionStart || element.selectionStart == 0) { var startPos = element.selectionStart; var endPos = element.selectionEnd; var scrollTop = element.scrollTop; element.value = element.value.substring(0, startPos) + text + element.value.substring(endPos, element.value.length); element.focus(); element.selectionStart = startPos + text.length; element.selectionEnd = startPos + text.length; element.scrollTop = scrollTop; } else { element.value += text; element.focus(); } } /** * Get the document body, and create one if missing. */ SDK.body = function() { var body = document.body || document.getElementsByTagName('body')[0]; if (body == null) { body = document.createElement("body"); document.body = body; } return body; } SDK.appendChild = function(baseRoot, child) { if (baseRoot == document) { baseRoot = SDK.body(); } baseRoot.appendChild(child); } /** * Fix innerHTML for IE and Safari. */ SDK.innerHTML = function(element) { var html = element.innerHTML; if (html == null) { var serializer = new XMLSerializer(); html = ""; for (var index = 0; index < element.childNodes.length; index++) { html = html + serializer.serializeToString(element.childNodes[index]); } if (html.indexOf(""") != -1) { html = html.replace(/"/g, '"'); } } var index = html.indexOf("<"); var index2 = html.indexOf(">") if (index != -1 && index2 > index) { html = html.replace(/</g, "<"); html = html.replace(/>/g, ">"); } if (html.indexOf("&") != -1) { html = html.replace(/&/g, "&"); } return html; } /** * Strip HTML tags from text. * Return plain text. */ SDK.stripTags = function(html) { var element = document.createElement("p"); element.innerHTML = html; SDK.removeTags(element); return element.innerText || element.textContent; } SDK.removeTags = function(node) { if (node.className == 'nospeech' || node.tagName == 'SCRIPT' || node.tagName == 'SELECT' || node.tagName == 'BUTTON' || node.tagName == 'OPTION') { node.parentNode.removeChild(node); } else { var index = 0; var childNodes = node.childNodes; var children = []; while (index < childNodes.length) { children[index] = childNodes[index]; index++; } var index = 0; while (index < children.length) { SDK.removeTags(children[index]); index++; } } return node; } /** * Replace reserved HTML character with their HTML escape codes. */ SDK.escapeHTML = function(html) { return html.replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } SDK.unescapeHTML = function(html) { return html.replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, "\"") .replace(/'/g, "'") .replace(/"/g, "\""); } /** * Replace URL and email references in the text with HTML links. */ SDK.linkURLs = function(text) { var http = text.indexOf("http") != -1; var www = text.indexOf("www.") != -1; var email = text.indexOf("@") != -1; if (!http && !www && !email) { return text; } if (text.indexOf("<") != -1 && text.indexOf(">") != -1) { return text; } if (http) { var regex = /\b(?:https?|ftp|file):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim; text = text.replace(regex, function(url, b, c) { var lower = url.toLowerCase(); if (lower.indexOf(".png") != -1 || lower.indexOf(".jpg") != -1 || lower.indexOf(".jpeg") != -1 || lower.indexOf(".gif") != -1) { return ''; } else if (lower.indexOf(".mp4") != -1 || lower.indexOf(".webm") != -1 || lower.indexOf(".ogg") != -1) { return ''; } else if (lower.indexOf(".wav") != -1 || lower.indexOf(".mp3") != -1) { return ''; } else { return '' + url + ''; } }); } else if (www) { var regex = /((www\.)[^\s]+)/gim; text = text.replace(regex, function(url, b, c) { return '' + url + ''; }); } // http://, https://, ftp:// //var urlPattern = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim; // www. // var wwwPattern = /(^|[^\/])(www\.[\S]+(\b|$))/gim; // name@domain.com if (email) { var emailPattern = /(([a-zA-Z0-9_\-\.]+)@[a-zA-Z_]+?(?:\.[a-zA-Z]{2,6}))+/gim; text = text.replace(emailPattern, '$1'); } return text; } /** * Enable speech recognition if supported by the browser, and insert the voice to text to the input field. * Optionally call click() on the button. */ SDK.registerSpeechRecognition = function(input, button) { if (SDK.recognition == null) { if ('webkitSpeechRecognition' in window) { SDK.recognition = new webkitSpeechRecognition(); if (SDK.lang != null) { SDK.recognition.lang = SDK.lang; } SDK.recognition.continuous = true; SDK.recognition.onresult = function (event) { for (var i = event.resultIndex; i < event.results.length; ++i) { if (event.results[i].isFinal) { SDK.insertAtCaret(input, event.results[i][0].transcript); } } if (button != null && button.click != null) { button.click(); } else if (button != null) { button(); } }; } else { return; } } } SDK.startSpeechRecognition = function() { if (SDK.recognition != null) { if (SDK.lang != null) { SDK.recognition.lang = SDK.lang; } SDK.recognition.start(); SDK.recognitionActive = true; } } SDK.pauseSpeechRecognition = function() { if (SDK.recognition != null) { SDK.recognition.stop(); } } SDK.stopSpeechRecognition = function() { if (SDK.recognition != null) { SDK.recognition.stop(); SDK.recognitionActive = false; } } SDK.popupwindow = function(url, title, w, h) { var wleft = (screen.width)-w-100; var wtop = (screen.height)-h-200; 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='+wtop+', left='+wleft); return false; } SDK.dataURLToBlob = function(dataURL) { var marker = ';base64,'; if (dataURL.indexOf(marker) == -1) { var parts = dataURL.split(','); var contentType = parts[0].split(':')[1]; var raw = parts[1]; return new Blob([raw], {type: contentType}); } var parts = dataURL.split(marker); var contentType = parts[0].split(':')[1]; var raw = window.atob(parts[1]); var rawLength = raw.length; var blobarray = new Uint8Array(rawLength); for (var i = 0; i < rawLength; ++i) { blobarray[i] = raw.charCodeAt(i); } return new Blob([blobarray], {type: contentType}); } SDK.isAlphaNumeric = function(text) { var regex = /[`!@#$%^&*()_\+\-=\[\]{};':"\\|,.~<>\/?]+/; return !regex.test(text); } SDK.uploadImage = function(fileInput, url, width, height, properties, onFinish) { if (window.File && window.FileReader && window.FileList && window.Blob) { // Copy over form properties. if (properties == null) { properties = {}; } if (fileInput.form != null) { var data = new FormData(fileInput.form); var entries = data.entries(); var next = entries.next(); while (!next.done) { var key = next.value[0]; var value = next.value[1]; if (key != 'file') { if (properties[key] == null) { properties[key] = value; } } next = entries.next(); } } var files = fileInput.files; for (var i = 0; i < files.length; i++) { SDK.resizeAndUploadImage(files[i], url, width, height, properties, ((i == (files.length - 1) ? onFinish : null))) } return false; } else { alert('The File APIs are not fully supported in this browser.'); return false; } } SDK.resizeAndUploadImage = function(file, url, width, height, properties, onFinish) { var reader = new FileReader(); reader.onloadend = function() { var tempImg = new Image(); tempImg.src = reader.result; tempImg.onload = function() { var MAX_WIDTH = width; var MAX_HEIGHT = height; if (width == null) { MAX_WIDTH = tempImg.width; } if (height == null) { MAX_HEIGHT = tempImg.height; } var tempW = tempImg.width; var tempH = tempImg.height; if (tempW > MAX_WIDTH) { tempH *= MAX_WIDTH / tempW; tempW = MAX_WIDTH; } if (tempH > MAX_HEIGHT) { tempW *= MAX_HEIGHT / tempH; tempH = MAX_HEIGHT; } var canvas = document.createElement('canvas'); canvas.width = tempW; canvas.height = tempH; var ctx = canvas.getContext("2d"); ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.drawImage(this, 0, 0, tempW, tempH); var dataUrl = canvas.toDataURL('image/jpeg'); var blob = SDK.dataURLToBlob(dataUrl); var formData = new FormData(); if (properties != null) { for (property in properties) { formData.append(property, properties[property]); } } formData.append('file', blob, file.name); var request = new XMLHttpRequest(); request.onreadystatechange = function() { if (request.readyState != 4) { return; } if (onFinish != null) { onFinish(); } } request.open("POST", url); request.send(formData); } } reader.readAsDataURL(file); } /** * Open a JQuery error message dialog. */ SDK.showError = function(message, title) { if (title == null) { title = "Error"; } $("").html(message).dialog({ title: title, resizable: false, modal: true, buttons: [ { text: "OK", click: function() { $(this).dialog("close"); }, class: "okbutton" } ] }); } /** * Open a JQuery confirm dialog. */ SDK.showConfirm = function(message, title, onYes, onNo) { if (title == null) { title = "Confirm"; } $("").html(message).dialog({ title: title, resizable: false, modal: true, buttons: { "Yes": function() { onYes(); $(this).dialog("close"); }, "No": function() { onNo(); $(this).dialog("close"); } } }); } /** * Evaluate any script tags in the node's descendants. * This is required when innerHtml contains script nodes as they are not evaluated. */ SDK.evalScripts = function(node) { if (node.tagName == 'SCRIPT') { var script = document.createElement("script"); script.text = node.innerHTML; for (var index = node.attributes.length-1; index >= 0; i--) { script.setAttribute(node.attributes[index].name, node.attributes[index].value); } node.parentNode.replaceChild(script, node); } else { var index = 0; var children = node.childNodes; while (index < children.length) { SDK.evalScripts(children[index]); index++; } } return node; } /** * Remove any script tags from the node. */ SDK.removeScripts = function(node) { if (node.tagName == 'SCRIPT') { node.parentNode.removeChild(node); } else { var index = 0; var children = node.childNodes; while (index < children.length) { SDK.removeScripts(children[index]); index++; } } return node; } /** * Add a stylesheet link to the page. */ SDK.addStylesheet = function(fileName) { var head = document.head; var link = document.createElement('link'); link.type = 'text/css'; link.rel = 'stylesheet'; link.href = fileName; head.appendChild(link); } /** * Add a style tag to the page. */ SDK.addStyle = function(css) { var style = document.createElement('style'); style.type = 'text/css'; if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } SDK.body().appendChild(style); } /** * Graphics upload dialog and shared repositry browser. * This provides a generic media upload dialog with many features: *" + "\n" + " |
" + "\n" + " |
paragraph for last chat message * - console -