(()=>{ url_domain =(data)=> { let a = document.createElement('a'); a.href = data; return a.hostname; }; for (let script of document.getElementsByTagName('script')) { if (script.src.indexOf('messenger')>=0) { let url = script.src; window.messangerSrc = url_domain(url); break; } } })(); class ChatMessenger { asyncLoadScripts (origin) { this.loadCSS(`${origin}/messenger/css/messenger.css?cache=` + Date.now()); this.loadCSS(`//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css`); console.log('origin', `${origin}js/mediasoup3/easy-mediasoup.bundle.min.js`); let files = [ `//cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.slim.js`, '//ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js', '//cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js', `${origin}js/mediasoup3/easy-mediasoup.bundle.min.js`, `${origin}js/mediasoup3/mediasSoup3.js`, `https://cdn.jsdelivr.net/npm/lsx-emojipicker@1.1.2/jquery.lsxemojipicker.min.js`, `https://cdn.jsdelivr.net/npm/hark@1.2.3/hark.bundle.js` ]; if(!window.jQuery) { files.unshift('//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js'); } let checkStateAndCall = (path, callback) => { let _success = false; return function () { if (!_success && (!this.readyState || (this.readyState === 'complete'))) { _success = true; callback(); } }; }; let loadNext = () => { // chain element if (!files.length) { this.init(); return; } let path = files.shift(); let scriptElm = document.createElement('script'); scriptElm.type = 'text/javascript'; scriptElm.async = true; scriptElm.src = path; scriptElm.onload = scriptElm.onreadystatechange = checkStateAndCall(path, loadNext); // load next file in chain when let headElm = document.head || document.getElementsByTagName('head')[0]; headElm.appendChild(scriptElm); }; loadNext(); // start a chain }; ago(date) { let plurial; let seconds = Math.floor((new Date() - date) / 1000); let interval = Math.floor(seconds / 31536000); if (interval > 0) { if (interval>1) { return eval(this.traductions.yearsAgo); } else { return eval(this.traductions.yearAgo); } } interval = Math.floor(seconds / 2592000); if (interval > 0) { if (interval>1) { return eval(this.traductions.monthsAgo); } else { return eval(this.traductions.monthAgo); } } interval = Math.floor(seconds / 86400); if (interval > 0) { if (interval>1) { return eval(this.traductions.daysAgo); } else { return eval(this.traductions.dayAgo); } } interval = Math.floor(seconds / 3600); if (interval > 0) { if (interval>1) { return eval(this.traductions.hoursAgo); } else { return eval(this.traductions.hourAgo); } } interval = Math.floor(seconds / 60); if (interval > 0) { if (interval>1) { return eval(this.traductions.minutesAgo); } else { return eval(this.traductions.minuteAgo); } } return 'now'; }; isEmpty (obj){ return Object.keys(obj).length === 0 && obj.constructor === Object; }; getDateAgo (date) { let res = new Date(date).getTime(); return ago(res); }; loadCSS (src) { let head = document.getElementsByTagName('HEAD')[0]; let link = document.createElement('link'); link.rel = 'stylesheet'; link.type = 'text/css'; link.href = src; head.appendChild(link); }; async getWebmasterid (url) { let res = await $.ajax({ url: this.ajax, type: 'POST', data: {a:'getWebmasterid', url:url} }); return parseInt(res); }; constructor(myUser = {}, notification = {}, callBackAddUser = false, callBackRemoveUser = false, traductions={}) { this.traductions = { yourMessage: "Your message", minuteAgo: "`${interval} min ago.`", minutesAgo: "`${interval} mins ago.`", hourAgo: "`${interval} hour ago.`", hoursAgo: "`${interval} hours ago.`", dayAgo: "`${interval} day ago.`", daysAgo: "`${interval} days ago.`", monthAgo: "`${interval} month ago.`", monthsAgo: "`${interval} months ago.`", yearAgo: "`${interval} year ago.`", yearsAgo: "`${interval} years ago.`", }; this.traductions = { ...traductions, ...this.traductions}; if (!window.messangerSrc) { window.messangerSrc = 'html5-chat.com'; } let origin = `//${window.messangerSrc}/`; if (myUser.ajax) { this.ajax = myUser.ajax; } else { this.ajax = origin + 'ajax.php'; } if (!myUser.el) { myUser.el = 'button[data-userid], div[data-userid]'; } if (!myUser.imagePath) { myUser.imagePath = ''; } this.myUser = myUser; this.users = new Map(); this.mutedUsers = new Map(); this.container = 'messengerContainer'; this.notification = notification; this.callBackAddUser = callBackAddUser; this.callBackRemoveUser = callBackRemoveUser; this.socket = {}; this.asyncLoadScripts(origin); }; pickRandomAvatar () { let rnd = Math.round(Math.random()*10) + 1; return `https://html5-chat.com/img/avatars/${rnd}.svg`; }; async getConfig (webmasterid) { let res = await $.ajax({ url: this.ajax, type: 'POST', data: {a:'getConfig', webmasterid:webmasterid} }); return JSON.parse(res); }; async getDefaultRoom (webmasterid) { let res = await $.ajax({ url: this.ajax, type: 'POST', data: {a:'getDefaultRoom', webmasterid:webmasterid} }); return JSON.parse(res); }; getMuted () { $.post(this.ajax, {a:'getMutedUsersAsArray', webmasterid:this.myUser.webmasterid, userid:this.myUser.id}, (jsonMuted)=> { if (jsonMuted) { jsonMuted = JSON.parse(jsonMuted); jsonMuted.forEach((muted) => { this.mutedUsers.set(muted.muteduserid, muted); let temp = `#messengerNotificatorContent div.messengerNotificatorUserItem[data-userid=${muted.muteduserid}]`; $(temp).addClass('muted'); }); } this.restoreWindows(); }); }; getUsersWhoSentMeMessages () { this.socket.emit('getUsersWhoSentMeMessages', (messages)=>{ if (!messages) return; messages = JSON.parse(messages); messages.forEach((message) => { let extra = JSON.parse(message.extras); let id = parseInt(message.fromid); let status = (this.users.get(id))?'messengerOnline':'messengerOffline'; let user = {id: id, username:extra.from, avatar:extra.avatar, status:status}; this.addUser(user); }); }); }; async promptForUsername (){ return new Promise((resolve)=>{ let template = `
`; jQuery('#messengerContainer').append(template); jQuery(document).on('keyup', '#messengerInput', (e) => { e.preventDefault(); e.stopImmediatePropagation(); let keyCode = e.keyCode || e.which; if (keyCode===13) { let username = jQuery(e.currentTarget).val(); if (username) { jQuery('#modalMessenger').remove(); resolve(username); } } }); }); }; filterUsers (username = '') { let filterOnline = $('#filterOnlineUsers').hasClass('onlineFilter'); jQuery('#messengerNotificatorContent .messengerNotificatorUserItem').show(); if (filterOnline) { let $els = $('div.messengerStatus.messengerOffline'); $els.each((index, element)=> { $(element).parent().hide(); }); } if (!username) return; let temp = `#messengerNotificatorContent .messengerNotificatorUserItem:not([data-username*='${username}'])`; jQuery(temp).hide(); console.log('filterOnline', filterOnline); }; addMessengerNotificator (){ let template = `
${this.traductions.users}
`; jQuery('#' + this.container).append(template); let $messengerNotificatorHeader = jQuery('#messengerNotificatorHeader'); $messengerNotificatorHeader.on('mouseup', ()=> { $('#messengerNotificator').toggleClass('messengerNotificatorMinimized'); }); jQuery('#messengerClearSearch').on('mouseup', ()=> { $('#messengerNotificatorSearch').val(''); this.filterUsers(); }); jQuery('#messengerNotificatorSearch').on('keyup', (e)=>{ e.preventDefault(); e.stopImmediatePropagation(); let search = jQuery(e.currentTarget).val(); this.filterUsers(search); }); jQuery('#filterOnlineUsers').on('mouseup', (e)=> { e.preventDefault(); e.stopImmediatePropagation(); jQuery(e.currentTarget).toggleClass('onlineFilter'); let search = jQuery(e.currentTarget).val(); this.filterUsers(search); }); jQuery('#messengerExit').on('mouseup', (e)=>{ localStorage.removeItem('user'); jQuery.each(localStorage, (key, data)=>{ if (typeof key === 'string' && key.indexOf('messenger_')===0) { localStorage.removeItem(key); } }); window.location = this.config.quitUrl; }); }; async init () { let el = `
`; if (!jQuery(`#${this.container}`).length) { jQuery('body').append(el); } if (this.isEmpty(this.notification)) { this.addMessengerNotificator(); } let localUser = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user')) : false; if (!this.myUser.avatar) { this.myUser.avatar = this.pickRandomAvatar(); } if (!this.myUser.username) { this.myUser.username = (localUser && localUser.username) ? localUser.username : await this.promptForUsername(); } if (!this.myUser.id) { this.myUser.id = (localUser && localUser.id)? localUser.id :Date.now(); } this.myUser.streamid = Date.now(); localStorage.setItem('user', JSON.stringify(this.myUser)); if (!this.myUser.webmasterid) { this.myUser.webmasterid = (localUser && localUser.webmasterid) ? localUser.webmasterid : await this.getWebmasterid(window.messangerSrc); } localStorage.setItem('user', JSON.stringify(this.myUser)); console.log('init..', this.myUser); jQuery('button[data-userid]').addClass('messengerOffline'); this.initEvents(); setInterval(()=>{ jQuery('#messengerContainer div.content div.messages span.timeAgo').each((index, element)=> { let date = jQuery(element).data('ago'); let ago = this.ago(date); jQuery(element).text(ago); }); }, 60000); this.config = await this.getConfig(this.myUser.webmasterid); this.myUser.room = await this.getDefaultRoom(this.myUser.webmasterid); this.connectToServer(); this.getMuted(); }; getChat(userid){ return jQuery(`#messengerContainer div.content[data-userid=${userid}]`); }; updateElements (user) { //console.log('updateElements', user); jQuery(`button[data-userid=${user.id}]`).removeClass('messengerOffline').removeClass('messengerOnline').addClass(user.status); jQuery(`#messengerContainer div.header`).removeClass('messengerOffline').removeClass('messengerOnline').addClass(user.status); }; updateUsersNumber () { jQuery('#messengerNotificatorCounter').text(this.users.size); }; connectToServer () { if (this.myUser.username.length>50) { return; } this.socket = io.connect(this.config.nodeMessenger, { 'force new connection': true, transports: ['websocket'], secure: true, query: this.myUser }); this.socket.on('connected', ()=> { console.log('Connected to server !'); this.socket.emit('enterRoom', this.myUser); //connectMS(this.config.webrtcServerUrl, this.myUser.id, this.config.VIDEO_CONSTRAINTS); this.updateNotification(); }); this.socket.on('call1to1', (user)=> { if (this.mutedUsers.has(user.id)) { return; } let $chat = this.getChat(user.id); let message = `${user.username} requested a video call.`; this.serverMessage($chat, message, 'serverMessage'); if (this.myUser.call1to1User) { return; } user.isCaller = true; this.myUser.call1to1User = user; this.displayCall1to1(user); }); this.socket.on('call1to1Cancelled', (user)=> { this.closeMyWebcam(); console.log(user); }); this.socket.on('call1to1Accepted', (user)=> { jQuery('div.messengerWebcamContainer div#MessengerPhotoCallerContainer').hide(); playStream(user.streamid, '#hisMessengerVideo'); jQuery('#hisMessengerVideo').show(); jQuery('#messengerMuteAudioBtn').removeClass('hiddenButton'); jQuery('#messengerToggleWebcamBtn').addClass('hiddenButton'); }); this.socket.on('getUsers', (usersInRoom)=> { //console.log('getUsers', usersInRoom); for (let userid in usersInRoom) { let user = usersInRoom[userid]; user.status = 'messengerOnline'; this.addUser(user); if (this.callBackAddUser) { this.callBackAddUser(user); } user.status = 'messengerOnline'; this.updateElements(user); } this.getUsersWhoSentMeMessages(); }); this.socket.on('receiveMessenger', (fromUser, toUser, message, extras)=> { if (this.mutedUsers.has(parseInt(fromUser.id))) { return; } this.appendMessage(fromUser, toUser, message, extras); this.playMP3(this.config.soundPrivateMessageReceived); }); this.socket.on('messengerWrites', (user)=> { let $e = jQuery(`#messengerContainer div.content[data-userid=${user.id}] span.isWrtiting`); if (!$e.hasClass('writesBlink')) { $e.addClass('writesBlink'); setTimeout(()=> { $e.removeClass('writesBlink'); }, 4500); } }); this.socket.on('addUser', (user)=> { if (this.callBackAddUser) { this.callBackAddUser(user); } user.status = 'messengerOnline'; this.updateElements(user); this.addUser(user); }); this.socket.on('removeUser', (user)=> { user.status = 'messengerOffline'; this.updateElements(user); if (this.callBackRemoveUser) { this.callBackRemoveUser(user); } this.removeUser(user); if (this.myUser.call1to1User && this.myUser.call1to1User.id === user.id) { this.closeMyWebcam(); } }); }; removeUser (user) { let el = `#messengerNotificatorContent div.messengerNotificatorUserItem[data-userid=${user.id}] div.messengerStatus`; jQuery(el).removeClass('messengerOnline').removeClass('messengerOffline').addClass('messengerOffline'); this.updateUsersNumber(); this.users.delete(parseInt(user.id)); }; addRandomUsers (num = 50) { for(let i=0;i `; if (user.gender && user.gender!=='undefined' && jQuery(`#genderChecboxContainer${user.gender}`).length===0) { jQuery('#genderFilterDiv').append(genderEl); } let eluser = `#messengerNotificatorContent div.messengerNotificatorUserItem[data-userid=${user.id}]`; $(eluser).remove(); let $messengerNotificatorContent = jQuery('#messengerNotificatorContent'); if (!$messengerNotificatorContent.length) return; let title = (this.myUser.id===user.id)?"That's you !" : user.username; let profileEl = (user.profile)?``:''; let gender = (user.gender)?user.gender:''; let el = `
${user.username}
${profileEl}
`; $messengerNotificatorContent.prepend(el); this.users.set(parseInt(user.id), user); this.updateUsersNumber(); this.updateFilterGenders(); }; messengerWindowDelete (userid) { localStorage.removeItem(`messenger_${userid}`); }; messengerWindowSave (user) { let $chat = this.getChat(user.id); let top = $chat.css('top'); let left = $chat.css('left'); let data = {username:user.username, left:left, top:top, avatar:user.avatar}; localStorage.setItem(`messenger_${user.id}`, JSON.stringify(data)); }; messengerWindowRestore (user) { let data = localStorage.getItem(`messenger_${user.id}`); if (data) { data = JSON.parse(data); let $chat = this.displayChat(user); $chat.css('left', data.left); $chat.css('top', data.top); } }; restoreWindows () { jQuery.each(localStorage, (key, data)=>{ if (typeof key === 'string') { let arr = key.split('messenger_'); if (arr.length > 1) { try { data = JSON.parse(data); let user = {id: arr[1], username: data.username, avatar:data.avatar}; this.messengerWindowRestore(user); } catch(e) { } } } }); }; sendMessenger (message, toUser, extras={}) { extras.time = Date.now(); extras.from = this.myUser.username; extras.avatar = this.myUser.avatar; this.socket.emit('sendMessenger', this.myUser, toUser, message, extras); }; call1to1HangOut () { this.socket.emit('call1to1Cancelled', this.myUser.call1to1User.id); this.closeMyWebcam(); }; closeMyWebcam () { unpublishOwnFeed('#myMessengerVideo'); jQuery('.messengerWebcamContainer').remove(); delete ChatHTML5.myUser.call1to1User; }; displayCall1to1 (userWhoCalls) { let classeWebcamBtn = (parseInt(this.myUser.id) === parseInt(userWhoCalls.id))?'hiddenButton':''; let template = `
${this.myUser.call1to1User.username}
${this.myUser.call1to1User.username}
`; jQuery('#messengerContainer').append(template); }; updateNotification () { this.socket.emit('getCountMessengerUnread', (messageCount)=>{ if (!messageCount) { messageCount = ''; } let html = `
${messageCount}
`; jQuery(this.notification.id).html(html); }); }; selectChat (user) { let $chat = this.getChat(user.id); if (!$chat.length) { $chat = this.displayChat(user); } else { $chat.find('input').focus(); } return $chat; }; serverMessage ($chat, message, classe='serverMessage'){ let now = Date.now(); let ago = this.ago(now); let el = `
${ago}
${message}
`; let $messages = $chat.find('.messages'); $messages.append(el); let objDiv = $messages[0]; if (objDiv) { objDiv.scrollTop = objDiv.scrollHeight; } }; appendMessage (fromUser, toUser, message, extras='') { //console.log('appendMessage', fromUser, toUser, message, extras); let classe = 'messageOther'; let $chat; if (fromUser.id === this.myUser.id) { $chat = this.getChat(toUser.id); classe = 'messageMine'; } else { let exists = this.getChat(fromUser.id).length; $chat = this.selectChat(fromUser); if (!exists){ return; } } if (!$chat.length) return; let ago = this.ago(extras.time); let el = `
${fromUser.username}${ago}
${message}
`; let $messages = $chat.find('.messages'); $messages.append(el); let objDiv = $messages[0]; if (objDiv) { objDiv.scrollTop = objDiv.scrollHeight; } }; updateFilterGenders() { jQuery('#genderFilterDiv input').each(function(i, el) { let genre = $(el).val(); let $checkBox = jQuery(`#genderChecbox${genre}`); if ($checkBox.prop('checked')) { $(`#messengerNotificatorContent div.${genre}`).show(); } else { $(`#messengerNotificatorContent div.${genre}`).hide(); } }); } displayChat (user) { user.id = parseInt(user.id); let headerClass = (this.users.get(user.id) && this.users.get(user.id).status ==='messengerOnline') ?'messengerOnline':'messengerOffline'; let mutedClass = (this.mutedUsers.get(user.id)) ?'muted':''; let username = (user.profile!=="undefined")?`${user.username}`:user.username; let template = `
${user.username} ${username} is writing
`; jQuery('#messengerContainer').append(template); jQuery(`div[data-userid=${user.id}]`).draggable({ containment: 'parent', stack: 'div', handle: 'div.header', stop: ( e, ui )=> { console.log(ui.position.left, ui.position.top); this.messengerWindowSave(user); } }); let $chat = this.getChat(user.id); this.messengerWindowSave(user); $chat.find('input').focus(); $chat.find('.messengerEmojiBtn').lsxEmojiPicker({ onSelect:(emoji)=>{ let input = $(event.currentTarget).parents('form.footer').find('input')[0]; const value = input.value; const start = input.selectionStart; const end = input.selectionEnd; let textToInsert = emoji.value.replace('&#','0'); input.value = value.slice(0, start) + String.fromCodePoint(textToInsert) + value.slice(end); // update cursor to be at the end of insertion input.selectionStart = input.selectionEnd = start + textToInsert.length; input.focus(); } }); // get History if (this.mutedUsers.has(parseInt(user.id))) { return false; } this.socket.emit('getMessengerMessages', user.id, (messages)=>{ messages = JSON.parse(messages); messages = messages.reverse(); messages.forEach((message) => { let extras = JSON.parse(message.extras); if (parseInt(message.fromid) === parseInt(this.myUser.id)) { let user = {id:message.toid, username:extras.from, avatar:extras.avatar}; this.appendMessage(this.myUser, user, message.message, extras); } else { let user = {id:message.fromid, username:extras.from, avatar:extras.avatar}; this.appendMessage(user, this.myUser, message.message, extras); } }) }); this.updateNotification(); return $chat; }; displayMenuMessages (messages) { if (!messages.length){ return; } let menuItems = ''; messages.forEach((message) => { let extra = JSON.parse(message.extras); let classeBall = (this.users.get(parseInt(message.fromid)))?'online':''; let menuItem = ` `; menuItems+=menuItem; }); let el = ` `; this.removeMenu(); let $notificationElementId = jQuery(this.notification.id); $notificationElementId.remove('.menu'); $notificationElementId.append(el); }; removeMenu () { jQuery(`${this.notification.id} .menuMessenger`).remove(); }; playMP3 (mp3file, loop=false) { if (!mp3file) { return; } try { let soundMP3 = new Audio(); if (soundMP3.paused) { soundMP3.src = mp3file; soundMP3.loop = loop; try { soundMP3.play().catch(function() { }); } catch(e) { console.log('error playsound', mp3file); } } } catch(e) { console.log('error playsound', mp3file); } }; initEvents () { let $messengerContainer = jQuery('#messengerContainer'); $messengerContainer.on('mousedown', 'button.closeBtn', (e) => { e.stopImmediatePropagation(); let $chat = jQuery(e.currentTarget).closest('.content'); let userid = $chat.data('userid'); this.messengerWindowDelete(userid); $chat.remove(); }); $messengerContainer.on('mouseup', 'button.clearMessengerBtn', (e) => { let message = `Are you sure you want to clear this chat ? `; let $chat = jQuery(e.currentTarget).closest('.content'); this.serverMessage($chat, message); }); $messengerContainer.on('mouseup', 'button[data-answer]', (e) => { e.stopImmediatePropagation(); let answer = ($(e.currentTarget).data('answer')==='yes'); let $chat = jQuery(e.currentTarget).closest('.content'); $(e.currentTarget).parent().parent().remove(); if (answer) { let userid = $chat.data('userid'); console.log('userid', userid); $chat.find('div.messages').empty(); this.socket.emit('deleteMessengerMessages', userid); } }); $messengerContainer.on('mouseup', 'button.muteUserBtn', (e) => { e.stopImmediatePropagation(); $(e.currentTarget).toggleClass('muted'); let $el = jQuery(e.currentTarget).closest('[data-userid]'); let muteduserid = $el.data('userid'); let username = $el.data('username'); let val = !this.mutedUsers.has(muteduserid); this.socket.emit('messengerMute', muteduserid, val); let $chat = this.getChat(muteduserid); let message; if (val) { message = sprintf(this.traductions['userHasBeenMutedBanned'], username); this.mutedUsers.set(muteduserid, {id:muteduserid, username:username}); } else { message = sprintf(this.traductions['userHasBeenUnmutedUnbanned'], username); this.mutedUsers.delete(muteduserid); } $(`#messengerNotificatorContent div.messengerNotificatorUserItem[data-userid=${muteduserid}]`).toggleClass('muted'); this.serverMessage($chat, message); }); $messengerContainer.on('mouseup', 'button.webcamBtn', (e) => { e.stopImmediatePropagation(); let id = jQuery(e.currentTarget).closest('[data-userid]').data('userid'); let user = this.users.get(parseInt(id)); if (!user) return; user.isCaller = false; this.myUser.call1to1User = user; this.displayCall1to1(this.myUser); publishOwnFeed('#myMessengerVideo', this.myUser.streamid); }); jQuery(document).on('change', '#genderFilterDiv input', (e)=> { this.updateFilterGenders(); }); jQuery(document).on('mouseup', ()=>{ this.removeMenu(); }); jQuery(document).on('mouseup', 'div.menuMessenger .menuMessengerItem', (e)=> { e.stopImmediatePropagation(); e.stopImmediatePropagation(); let $e = jQuery(e.currentTarget); let user = {id:$e.data('id'), username:$e.data('username'), avatar:$e.data('avatar'), profile:$e.data('profile')}; this.removeMenu(); this.displayChat(user); }); jQuery(document).on('getMyStreamId', ()=> { console.log('call1to1', this.myUser.call1to1User.id); if(this.myUser.call1to1User.isCaller) { this.socket.emit('call1to1Accepted', this.myUser.call1to1User.id); } else { this.socket.emit('call1to1', this.myUser.call1to1User.id); } }); $messengerContainer.on('keyup', 'form.footer input', (e) => { e.preventDefault(); e.stopImmediatePropagation(); let keyCode = e.keyCode || e.which; if (!this.myUser.isWriting) { console.log('isWriting !!!'); this.myUser.isWriting = true; let userid = jQuery(e.constructor).find('[data-userid]').data('userid'); this.socket.emit('messengerWrites', userid); setTimeout(()=> { this.myUser.isWriting = false; }, 5000); } if (keyCode === 13) { let message = jQuery(e.currentTarget).val(); if (!message) return; jQuery(e.currentTarget).val(''); let $content = jQuery(e.currentTarget).closest('.content'); let id = $content.data('userid'); let username = $content.data('username'); let avatar = $content.data('avatar'); let user = {id:id, username:username, avatar:avatar}; this.sendMessenger(message, user); } }); jQuery(document).on('mousedown', this.myUser.el, (e) => { e.stopImmediatePropagation(); let $e = jQuery(e.currentTarget); let userid = $e.data('userid'); let username = $e.data('username'); let avatar = $e.data('avatar'); let profile = $e.data('profile'); if (parseInt(userid)===parseInt(ChatHTML5.myUser.id)) return; let user = {id:userid, username:username, avatar:avatar, profile:profile}; this.selectChat(user); }); jQuery(document).on('mouseup', 'button#messengerHangoutBtn', () => { this.call1to1HangOut(); }); jQuery(document).on('mouseup', 'button#messengerMuteAudioBtn', (e) => { let $e = jQuery(e.currentTarget); $e.toggleClass('muted'); mute('#myMessengerVideo', $e.hasClass('muted')); }); jQuery(document).on('mouseup', 'button#messengerToggleWebcamBtn', () => { //this.socket.emit('call1to1Accepted', this.myUser.call1to1User.id); publishOwnFeed('#myMessengerVideo', this.myUser.streamid); }); jQuery(document).on('getMyStreamId', ()=> { ChatHTML5.myUser.webcam = true; //ChatHTML5.cameraStatus(false); }); let $notificationElementId = jQuery(this.notification.id); if ($notificationElementId.length) { jQuery(document).on('mouseup', this.notification.id, ()=>{ this.socket.emit('getMessengerUnread', (messages)=>{ messages = JSON.parse(messages); console.log('getMessengerMessages', messages); this.displayMenuMessages(messages); }); }) } }; }var ChatHTML5 = new ChatMessenger(, {}, false, false, {"hosting":{"hosting":"H\u00e9b\u00e9rger","Host html5 chat":"H\u00e9b\u00e9rger html5 chat","How to host html5 chat":"Comment h\u00e9berger un chat html5 sur votre serveur? ","Host Your chat":"Vous pouvez maintenant h\u00e9berger le chat <\/b> sur votre serveur
Nous pouvons installer la partie serveur multim\u00e9dia <\/b> (streaming) et les sockets < \/ b> (texte de discussion) sur votre serveur.
","Advantages":"Quels sont les avantages d'h\u00e9berger cela sur votre serveur?","you have 100% bandwidth of your server":"Vous avez 100% de bande passante de votre serveur","you have 100% CPU power of your server":"Vous avez 100% de puissance CPU de votre serveur","you have 100% CPU control of your server":"vous avez 100% de contr\u00f4le CPU de votre serveur","You are independent":"Vous \u00eates ind\u00e9pendant","FAQ":"Foire aux questions","You still got updates automatically":"Vous avez toujours des mises \u00e0 jour automatiquement puisque le chat est h\u00e9berg\u00e9 sur nos serveurs sauf la webcam et les sockets!","You still have to pay subscription":"Vous devez toujours payer l'abonnement car la partie de script de chat est toujours h\u00e9berg\u00e9e sur nos serveurs","Generally it takes 24-48 hours":"G\u00e9n\u00e9ralement, cela prend 24 \u00e0 48 heures, nous l'installons donc sur votre serveur","You need a dedicated server":"Vous avez besoin d'un serveur d\u00e9di\u00e9 avec un acc\u00e8s ROOT","We recommend 4 Giga RAM":"Nous recommandons 4 Giga RAM, 4 c\u0153urs et un acc\u00e8s root. Ce doit \u00eatre LINUX. Nous recommandons la distribution DEBIAN 9 (mais centos \/ ubuntu ... sont ok)","We need a ROOT access to your server":"Nous avons besoin d'un acc\u00e8s ROOT \u00e0 votre serveur","Of course you can still use your server for other tasks":"Bien s\u00fbr, vous pouvez toujours utiliser votre serveur pour d'autres t\u00e2ches comme l'h\u00e9bergement de vos sites","Price is":"Le prix est de 199 \u20ac. C'est juste un prix one shot. <\/b>","Other questions ?":"Autres questions?","Contact us by skype":"Contactez-nous par skype.","You can purchase here":"Vous pouvez acheter le service d'h\u00e9bergement ici. Une fois le paiement effectu\u00e9, nous vous contacterons pour obtenir toutes les informations sur les serveurs comme l'acc\u00e8s root.","oneTimePrice":"199 euros: UNE FOIS POUR TOUTES."},"Best webcam chat script":"Script de vid\u00e9o chat","Free version has not WEBRTC and uses Flash for webcam chat.":"La version gratuite n'a pas de webrtc <\/a> et utilise du Flash pour la vid\u00e9o webcam","Looking for 123flashchat, avchat or camfrog alternatives in HTML5\/webrtc ?":"A la recherche d'une alternative \u00e0 123flashchat, avchat ou camfrog en HTML5\/webrtc ?","I did not receive my email. Please send it again":"Je n'ai pas re\u00e7u d'email. renvoyez le moi svp.","roomList":"Liste des salles","privateOn":"Priv\u00e9 on","privateOff":"Priv\u00e9 off","camOn":"Cam on","camOff":"Cam off","soundOn":"sound On","soundOff":"sound Off","searchUsers":"chercher chatteur","online":"en ligne","chatWithSomeone":"chatter avec %s","chatInRoom":"chattez dans la salle %s","rooms":"Salons","name":"Nom","chatters":"chatters","action":"Action","join":"Rejoindre","close":"Fermer","quit":"Quitter","cleanChat":"Effacer chat","smileys":"Emoticones","quitChat":"Quitter le chat ?","login":"Entr\u00e9e","chooseAnUsername":"Choisir un pseudo","showMyWebcamToAnyone":"Montrer ma webcam \u00e0 tous","myWebcamIsPublic":"Webcam publique","myWebcamIsPrivate":"Webcam priv\u00e9e","showMyWebcamOnlyOnInvitation":"Show my webcam only on invitation","changeAvatar":"Changer avatar","statusOnline":"En ligne","statusOffline":"Absent","statusBusy":"Occup\u00e9","private":"Priv\u00e9","kickUser":"Ejecter","banUser":"Bannir un user","ban":"Bannir","chooseDurationIfTheBanInMinutes":"Choisir la dur\u00e9e du ban (en minutes)","explainWhyYouBan":"Expliquer le motif du bannissement","forgottenPassword":"Mot de passe oubli\u00e9 ?","password":"Mot de passe","usernameOrEmail":"Username ou email","enterYourUsernameOrEmail":"Entrez votre username ou email","enterYourPassword":"Entrez votre mot de passe","register":"Enregistrez vous","username":"Pseudo","email":"Email","confirmPassword":"Confirmer mot de passe","enterYourUsername":"Entrez votre username","enterYourEmail":"Entrez votre email","createNewRoom":"Cr\u00e9er une nouvelle room","enterNameOfRoom":"Entrez le nom de la room","enterWelcomeMessage":"Entrez le message de bienvenue","welcome":"Bienvenue","publicRoom":"Room publique","privateRoom":"Room priv\u00e9e","createRoom":"Cr\u00e9er room","enterPassword":"Entrez le mot de passe","passwordIncorrect":"Mot de passe incorrect !","youHaveBeenKicked":"You have been kicked !","unreadMessages":"Messages non lus","watchesMe":"Me regarde","badLoginOrPassword":"Mauvais login ou mot de passe","youHaveBeenBannedFromChat":"Vous avez \u00e9t\u00e9 banni du chat pendant %s<\/b> minutes.

Motif:%s","youHaveBeenKickedByUser":"Vous avez \u00e9t\u00e9 \u00e9ject\u00e9 par %s<\/b>","youAreNowWatchingUser":"Vous \u00eates en train de regarder %s","userHasClosedPrivateChat":"%s a ferm\u00e9 le chat priv\u00e9","requestAPrivateChat":"vous invite en discussion priv\u00e9e","accept":"Accepter","deny":"Refuser","mute":"Muter","requestsAVideoChat":"demande \u00e0 voir votre webcam.","youRequestedAPrivateChatWith":"Vous avez demand\u00e9 une discussion priv\u00e9e avec","privateWith":"priv\u00e9 avec","youEequestedWatchWebcamOf":"Vous avez demand\u00e9 \u00e0 voir la webcam de","webcamNumberMaximumReached":"Nombre maximale de webcam atteint","invalidImageType":"Invalid image type","invalidImageSize":"Taille image invalide","thisEmailIsAlreadyUsed":"Cette email est d\u00e9j\u00e0 utilis\u00e9e","thisUsernameIsTaken":"Ce pseudo est d\u00e9j\u00e0 utilis\u00e9","youCannotUseJunkEmailService":"Emails jettables interdits","checkYourEmailToConfirm":"Verifiez votre courrier %s pour confirmer","emailWithPasswordHasBeenSent":"Un email contenant le mot de passe vous a \u00e9t\u00e9 envoy\u00e9","emailNotFoundInDatabase":"Cette email ne figure pas dans la base","unmuteUser":"Ne plus ignorer %s","muteUser":"Ignorer %s","privateWithX":"Priv\u00e9 avec %s","kickUserX":"Ejecter %s","banUserX":"Bannir %s","youJustKickedX":"Vous avez \u00e9ject\u00e9 %s","toInviteForPrivateChatYouMustEnablePrivateChatsYourself":"Pour inviter en priv\u00e9, vous devez vous m\u00eame habiliter les priv\u00e9s. Habiliter les priv\u00e9s maintenant ?","passwordDoNotMatch":"Les mots de passe ne correspondent pas","invalidEmail":"Email invalide","invalidPassword":"Mot de passe non valide","invalidUsername":"Pseudo non valide","isWriting":"est en train d'\u00e9crire","developers":"D\u00e9veloppeurs","home":"Accueil","enterChat":"Chattez !","getChat":"Obtenir votre chat","purchaseChat":"Acheter chat","features":"Caract\u00e9ristiques","faq":"FAQ","contact":"Contact","feelFreeToContactUs":"N'h\u00e9sitez pas \u00e0 nous contacter","LookingForFreeHTML5VideoChat":"Vous cherchez un webcam\/video tchat HTML5 gratuit ?","tryOutHTML5FreeChat":"Essayez notre HTML5 video chat gratuit<\/strong>: en 1 click
obtenez le votre !<\/a>.","WeHaveTheMostCompleteVideoChat":"Nous avons le video chat le plus complet pour votre site, d\u00e9velopp\u00e9 en html5 et facile \u00e0 int\u00e9grer.","FlashOrWebrtc":"Choisissez WEBRTC pour votre webcam chat.
Chat Html5 int\u00e8gre maintenant la technologie webrtc pour un multi chat en temps r\u00e9el","tryHTML5Chat":"Essayez html5 webrtc chat","GetYourHtml5Chat":"Obtenez html5 chat","enterYourQuestionHere":"Votre question. Si vous avez un probl\u00e8me avec votre chat, veuillez indiquer l'url de votre chat.","WebcamVideoChatTotallyCustomized":"Webcam video chat totalement personnalis\u00e9 et gratuit.","GetYourOwnHTML5VideoChat":"Obtenez votre chat HTML5 pour votre site ou votre blog","HTML5chatGetYourOwnFreeHtml5Chat":"HTML5 chat, obtenez votre chat html5 webrtc gratuit. Script vid\u00e9o chat","Html5ChatAllowsYouToIntegrateQuicklyFreeHtml5Chat":"Chat HTML5 : vous permet d'obtenir votre propre chat WEBRTC pour votre site web ou blog gratuitement.","Moderate it and customize it as you want":"Mod\u00e9rez votre chat et customisez le","Easy to integrate, easy to admin":"Facile \u00e0 int\u00e9grer, facile \u00e0 administrer","Includes live webcam streaming":"Inclut live vid\u00e9o streaming","Private or public messages":"Messages priv\u00e9s ou publiques","Tabbed or windowed style":"Style : conf\u00e9rence, tabulations ou par fen\u00eatres","Webcam and chat on invitation":"Webcam et tchat sur invitation","based on nodejs sockets compatible with all browsers":"d\u00e9v\u00e9lopp\u00e9 en nodejs \/ sockets compatible avec tout navigateur","3 differents modes : tabbed mode, windowed mode or conference mode":"3 modes de fonctionnment: mode tabulation, fen\u00eatre ou mode conf\u00e9rence","Multi webcam support : stream and watch online users":"Support Multi webcams : Diffusez votre vid\u00e9o et regardez les autres","full admin panel":"Panneau admin complet","Look and feel as you want !":"Votre propre look&feel","manyChatsInHTML5chat":"html5-chat vous permet de choisir diff\u00e9rents modes de fonctionnement de votre chat et de le personnaliser \u00e0 l'infini: un chat multi utilisateurs, un chat roulette, une conf\u00e9rence, un chat1to1 ou bien encore un chat de style 'liveshow'. Vosu pouvez tout faire avec 1 seul produit","integrate to any existing website just with 1 line of code":"Int\u00e9grez le dans n'importe quel site web avec 1 seule ligne de code","auto login features to be integarted with your existing users database":"possibilit\u00e9 d'auto login pour utiliser votre propre base de donn\u00e9e","Here are some features of our video webcam chat":"Voici quelques caract\u00e9ristqiues de HTML5 chatr","Moderation features Inludes moderation features kickban":"Possibilit\u00e9 de mod\u00e9ration comme les fonctions de kick et bannissement","modern, pure GUI, easy to customize to your needs":"Aspect moderne: custimisez le \u00e0 vos envies !","More than 100 features included":"Plus de 100 fonctionnalit\u00e9s disponibles","Full user managment":"Administation compl\u00e8te de vos membres","Gender managment":"administration des genres","full screen webcam streaming":"diffusion en full screen de la webcam","and so much more...":"et tellement plus...","Register to get your own html5 chat":"Enregsitrez vous pour obtenir votre propre chat","Register to get your html5 chat: it is free. Webcam video chat included":"Enregistrez vous pour obentir votre chat en html5 : C'est gratuit. Webcam video chat inclus","Get your FREE video chat now !":"Obtenez le video chat GRATUIT maintenant !","You are 10 seconds from getting your own chat: just fill the form and you will yours":"Vous \u00eates \u00e0 10 secondes seulement pour obtenir votre propre video chat: remplissez ce formulaire et il est \u00e0 vous !","Enter your email here":"Entrez votre email","Choose your username":"Choisissez votre pseudo","Choose your password":"Votre mot de passe","Confirm your password":"Confirmez mot de passe","Get your HTML5 chat":"Obtenez votre HTML5 Chat","Your script will be sent to your email. So be sure, you spell it correctly.":"Votre script vous sera envoy\u00e9 par email.","Error: password do not match":"Erreur: Les mots de passe ne correspondent pas","Contact html5 chat":"Contactez l'\u00e9quipe de html5 chat","Questions about html5 chat ? Contact our chat team and get answer to your question":"Vous avez des questions \u00e0 propos du chat en HTML5, contactez nous !","Any question ? any recommendation ? any suggestion ?":"Questions ? Suggestions ? Recommendations ?","Please: fill this form: we will answer within 24 hours.":"Remplissez ce formualire: nous r\u00e9pondrons sous 24 heures","sendYourQuestion":"Envoyez votre question","Faq about the html5 chat":"Questions \/ r\u00e9ponses \u00e0 propos du chat html5","Here you can find the most common questions\/answers about the html5 chat":"Vous trouverez ici les questions\/r\u00e9ponses les plus fr\u00e9quentes \u00e0 propos du chat html5","If you cannot find the answer to your question, please use the ":"Si vous ne trouvez pas r\u00e9ponse \u00e0 votre question, utilisez: ","Purchase HTML5 chat":"Acheter le chat HTML5","Purchase now html5 chat to be embedded into your existing web site":"Acheter maintenant le chat HTML pour votre site","Free":"Gratuit","OneHundred free version":"100% version gratuite","Free chat":"Chat gratuit","4 max webcams":"4 webcams max","3 max rooms":"3 rooms max","50 users max":"50 utilisateurs max","Sign Up":"Obtenez le","Full":"Full","Full registered version":"Version 'full'","per year":"par an","per month":"par mois","No webcam limit":"Pas de limite webcam","Unlimited room":"Rooms illimit\u00e9es","No limitations, no ads":"Aucune limitation, pas de publicit\u00e9","Source code":"Code source","extended version":"Version \u00e9tendue","Inlcudes full source code":"Inclut code source","Allows you to run on your server":"Sur votre propre serveur","Support included":"Support inclus","Updates included for 12 months":"Maintenance incluse sur 12 mois","HTML5 demo chat":"D\u00e9monstration du chat en HTML5","Feature list of the html5 chat":"Caract\u00e9ristiques techniques du chat HTML5","Developers API HTML5 chat":"API HTML5 chat pour les d\u00e9veloppeurs","Here is an inexhaustive list of the html5 chat features":"Voici une liste non exhaustives de toutes les possibilit\u00e9s du chat HTML5","We are keeping add new features every week":"Nous ajoutons des nouvelles fonctionnalit\u00e9s toutes les semaines. Si vous pensez qu'il manque une fonctionnalit\u00e9 importante, faites le nous connat\u00eetre
avec le formulaire de suggestion<\/a>","here_are_some_features_of_our_video_webcam_chat":"Voici les caract\u00e9ristiques du webcam video chat:","our_html5_is_based_on_sockets":"Chat en html5 bas\u00e9 sur sockets","major_browser_compatibility":"Compatible tout navigateur","tab_mode_every_private_opens_in_a_new_private_tab":"Mode onglet: chaque nouveau chat s'ouvre dans un nouvel onglet","window_mode_every_chat_opens_in_a_new_draggable_window":"Mode fen\u00eatre: Chaque chat peut s'ouvrir dans une nouvelle fen\u00eatre","conference_mode_there_is_one_streamer_and_many_listeners_suitable_for_shows_e_learning_etc":"Mode conf\u00e9rence: 1 personne diffuse et d'uatres visionnent: parfait pour des shows, s\u00e9ance e-learning etc...","individual_draggable_webcam_support":"Webcam individuelles drag&drop","full_screen_webcam_support":"Mode Full screen webcam","fullAdminPanel":"full panneau pour administrer\/customizer votre tchat<\/a>","customized_look_n_feel_make_it_look_like_your_website":"Aspect visuel look&feel totalement personnalisable","easy_to_integrate_1_line_of_code_to_be_inserted":"Facile \u00e0 int\u00e9grer: 1 seule ligne de script \u00e0 copier\/coller","direct_link_to_your_chat":"Link direct vers votre chat","auto_login_feature_so_you_can_plugin_to_your_existing_database_users":"Auto connexion vers votre base de donn\u00e9e d'utilisateurs","full_moderation_features_inludes_moderation_features_kick_ban_warn":"Mod\u00e9ration des chats: permet de kicker \/ bannir ou avrtir un utilisateur","modern_pure_gui_easy_to_customize_to_your_needs":"Interface graphique simple et \u00e9pur\u00e9e. Facile \u00e0 customiez selon vos besoins","More_than_100_features_included_and_growing_every_day":"Plus de 100 options d\u00e9j\u00e0 incluses et.. nous en rajoutons plus tous les jours","full_user_managment_add_remove_edit_password":"Gestion d'utilisateurs complet: ajouter\/supprimer d\u00e9finissez leurs r\u00f4les","rooms_management_add_edit_remove_users_or_protect_rooms_with_passwords":"Gestion des rooms: editer\/ajouter\/supprimer les rooms et prot\u00e9gez les avec un mot de passe","gender_managment_customized_it_to_your_genders_or_roles":"Gestion de genres : d\u00e9finissez vos genres et leurs r\u00f4les","private_or_public_webcams_managements":"Diffuser sa webcam en priv\u00e9 ou en public","private_or_public_chats_on_invitation":"Chat priv\u00e9 ou public sur invitation","users_filtering_by_gender_by_username":"Filtrer les utilisateurs par genre et par pseudo","who_s_watching_me_feature":"'Qui me regarde': savoir quels utilisateurs me regardent","mute_an_user_feature":"Muter un utilisateur","and_so_much_more":"Et bien plus encore....","try_it_now_to_have_a_look_at_all_features_you_can_get":"Essayez le pour voir toutes les options disponibles !","answer":"R\u00e9ponse","what_is_html5_chat_for":"A quoi sert le chat HTML5 ?","html5_chat_allows_you_to_quickly_intergate_a_full_video_chat_to_your_website_or_blog":"HTML5 chat vous permet de rajouter un vid\u00e9o chat sans effort pour votre site ou votre blog.","is_html5_chat_free":"Est ce que le chat HTML5 est gratuit ?","yes_we_have_a_free_version_and_a_paid_version":"Oui: nous avons une version gratuite du chat et une version paynate du chat","what_is_the_difference_between_the_free_and_paid_version":"Quelles sont les diff\u00e9rences entre la version payante et la version gratuite ?","the_free_version_has_some_limited_features":"La version gratuit est limit\u00e9e comme la limitation de nombre de rooms par exemple, limitation de la bande passante, limitatioun de nombre de webcams et plus g\u00e9n\u00e9rallement moins d'options disponibles.
La version payante est illimit\u00e9e.","how_much_cost_the_paid_version":"Combien c\u00f4ute la version payante ?","just_check_the_prices_here":"La liste des prix est disponible ici","how_to_integrate_the_chat_to_my_existing_website":"Comment int\u00e9grer le chat html5 dans mon site existant ?","first_register":"
Enregistrez vous en premier !<\/a>
Vous recevrez par email votre ligne de script qu'il suffira de coller sur votre site\/blog.
Vous pouvez \u00e9galement cr\u00e9er un lien direct vers le chat. (le lien est fourni dans l'email \u00e9galement)","youCanAlsoEasilyIntegrateItIntoCMSsuchas":"Le script est d\u00e9j\u00e0 compatibles avec les CMS les plus connus comme: Wordpress, WordPress, Joomla, VLD Personals, Dating Site script, PhpFox, Boonex, WooWonder...
Plus d'information sur votre
console admin","how_can_i_administrate_my_chat":"Comment administrer mon tchat ?","you_can_administrate_your_html5_chat":"Vous l'administrez dans le panneau admin.<\/a> Vous devez fournir votre email et votre mot de passe panneau administration<\/a>","Technical questions about your html5 chat ? We are here to help you":"Des questions techniques sur votre chat html5 ?","Support is included in":"Le support et l'aide sont incluses dans la version payante du chat<\/a>","To integrate it into existing website":"Pour l'int\u00e9grer dans votre site existant, copiez\/collez le script re\u00e7u lors de votre enregistrement","script looks like":"Script ressemble \u00e0 ceci:","where id is your id script and uniqueKey is a token":"o\u00f9 id iest votre id de script et uniqueKey est une cha\u00eene al\u00e9atoire","Notice that you can also integrate a direct link to you chat":"Remarque: vous pouvez \u00e9galement int\u00e9grer un lien direct vers votre chat","if you most your data":"Donn\u00e9es perdues, alors vous pouvez les r\u00e9cup\u00e9rer dans votre panneau admin<\/a>.","How to change the dimension of the chat ?":"Comment changer les dimensions de mon tchat ?","chat uses iframe to be embedded into your site. The idea is so to included that iframe into your existing element (div) and it will take 100% of that element.":"Le tchat utilise une 'iframe' comme moyen d'int\u00e9gration. L'id\u00e9e est donc d'inclure cette iframe dans un \u00e9lement existant (div) et il prendra les dimensions de cet \u00e9lement !","I want the chat to be 640px width and 480px height: just create a div of 640px x 480px and embed the script inside !":"Si je veux que le chat fasse 640px de largeur et 480px de hauteur: Cr\u00e9ez donc une div de 640x480 et int\u00e9grer le script \u00e0 l'interieur de cette div.","Question: I have my website ready and I manage my users by myself. How to integrate it with my existing database ?":"Question: J'ai mon site d\u00e9j\u00e0 existant. Puis je g\u00e9rer mes propres utilisetauer avec ma propre base de donn\u00e9es ?","This is possible with HTML5 chat!":"C'est possible avec le chat HTML5","More questions ?":"Encore plus de questions ?","Please contact us !":"Contactez nous !","Quick tips how to integrate your video chat into your website":"Quelques astuces pour int\u00e9grer le chat dans votre site web","does_it_work_on_any_browser":"Est ce que le chat HTML5 fonctionne sur tout navigateur ?","webrtc_is_supported_on_firefox_and_chrome":"Oui \u00e0 condition d'avoir habilit\u00e9 le plugin Flash. Si vous cherchez un chat avec une technologie webrtc<\/b>, visitez le chat webrtc Html5 chat<\/a>","playerYoutube":"player youtube pour animer vos chats","quickPrivateMessage":"Mentionner","quickPrivateMessageTo":"Envoyer un priv\u00e9 \u00e0 %s","myWebcam":"Ma webcam","pushToTalk":"Push to Talk","youNeedToEnableYourWebcamToPushToTalk":"Vous devez habiliter votre webcam pour parler","youAreTalking":"Vous parlez...","userIsTalking":"%s parle