blob: cd88dbf92f89d115d051e9cec77fbed6e1b737c2 [file] [log] [blame]
aviau039001d2016-09-29 16:39:05 -04001<html>
aviau039001d2016-09-29 16:39:05 -04002<!-- Empty head might be needed for setSenderImage -->
3<head>
Frederic Guimontd8343e62016-11-01 23:31:25 -04004 <meta name="viewport" content="width=device-width, initial-scale=1" />
Frédéric Guimont13778a62016-11-02 21:21:21 -04005 <meta charset=“utf-8”>
aviau039001d2016-09-29 16:39:05 -04006</head>
7
8<body>
Hugo Lefeuvreedad8832018-05-14 16:36:06 -04009 <div class="navbar-wrapper">
10 <div id="navbar">
11 <div id="backButton" class="nav-button non-action-button nav-left" onmouseover="addBackButtonHoverProperty()" onclick="backToWelcomeView()" title="Hide chat view">
12 <svg class="svgicon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
13 <path d="M11.67 3.87L9.9 2.1 0 12l9.9 9.9 1.77-1.77L3.54 12z"/>
14 <path fill="none" d="M0 0h24v24H0z"/>
15 </svg>
16 </div>
17 <div id="nav-contactid" class="nav-left">
18 <div id="nav-contactid-alias"></div>
19 <div id="nav-contactid-bestId"></div>
20 </div>
21 <div style="display:none" class="deactivated nav-button action-button nav-right" onclick="moreOptions()" title="Options">
22 <svg class="svgicon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
23 <path d="M0 0h24v24H0z" fill="none"/>
24 <path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
25 </svg>
26 </div>
27 <div id="callButtons"> <!-- callButtons block allows more efficient hiding of placeCallButton and placeAudioCallButton -->
28 <div id="placeCallButton" class="nav-button action-button nav-right" onclick="placeCall()" title="Place video call">
29 <svg class="svgicon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
30 <path d="M0 0h24v24H0z" fill="none"/>
31 <path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/>
32 </svg>
33 </div>
34 <div id="placeAudioCallButton" class="nav-button action-button nav-right" onclick="placeAudioCall()" title="Place audio call">
35 <svg class="svgicon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
36 <path fill="none" d="M0 0h24v24H0z"/>
37 <path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"/>
38 </svg>
39 </div>
40 </div>
41 <div id="addToConversationsButton" class="nav-button action-button nav-right" onclick="addToConversations()" title="Add to conversations">
42 <svg class="svgicon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
43 <path d="M0 0h24v24H0z" fill="none"/>
44 <path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
45 </svg>
46 </div>
47 <div id="addBannedContactButton" class="nav-button action-critical-button nav-right" onclick="addBannedContact()" title="Unban banned contact">
48 <svg class="svgicon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
49 <path fill="none" d="M0 0h24v24H0V0z"/>
50 <circle cx="15" cy="8" r="4"/>
51 <path d="M23 20v-2c0-2.3-4.1-3.7-6.9-3.9l6 5.9h.9zm-11.6-5.5C9.2 15.1 7 16.3 7 18v2h9.9l4 4 1.3-1.3-21-20.9L0 3.1l4 4V10H1v2h3v3h2v-3h2.9l2.5 2.5zM6 10v-.9l.9.9H6z"/>
52 </svg>
53 </div>
54 </div>
55 <div id="invitation">
56 <div id="text">
57 </div>
58 <div id="actions">
59 <div id="accept-btn" class="invitation-button button-green" onclick="acceptInvitation()" >Accept</div>
60 <div id="refuse-btn" class="invitation-button button-red" onclick="refuseInvitation()" >Refuse</div>
61 <div id="block-btn" class="invitation-button button-red" onclick="blockConversation()" >Block</div>
62 </div>
63 </div>
64 </div>
Adrien Beraud8e25afb2017-04-19 01:38:57 +020065 <div id="container">
66 <div id="messages"></div>
AmarOkb4253242017-07-13 11:21:39 -040067 <div id="sendMessage">
Hugo Lefeuvreedad8832018-05-14 16:36:06 -040068 <div class="nav-button action-button" onclick="sendFile()" title="Send File">
Hugo Lefeuvre8127fe12018-05-23 10:53:12 -040069 <svg class="svgicon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
70 <path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z"/>
71 <path d="M0 0h24v24H0z" fill="none"/>
72 </svg>
AmarOkb4253242017-07-13 11:21:39 -040073 </div>
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -040074 <textarea id="message" autofocus placeholder="Type a message" onkeyup="grow_text_area()" onkeydown="process_messagebar_keydown()" rows="1" disabled="false"></textarea>
Hugo Lefeuvreedad8832018-05-14 16:36:06 -040075 <div class="nav-button action-button" onclick="sendMessage()" title="Send">
Hugo Lefeuvre8127fe12018-05-23 10:53:12 -040076 <svg class="svgicon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
77 <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
78 <path d="M0 0h24v24H0z" fill="none"/>
79 </svg>
Guillaume Roguez5b137be2018-02-21 10:44:58 -050080 </div>
AmarOkb4253242017-07-13 11:21:39 -040081 </div>
Adrien Beraud8e25afb2017-04-19 01:38:57 +020082 </div>
aviau039001d2016-09-29 16:39:05 -040083</body>
84
aviau039001d2016-09-29 16:39:05 -040085<script>
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -040086"use strict"
AmarOk6286ad42017-07-14 12:11:08 -040087
Hugo Lefeuvreedad8832018-05-14 16:36:06 -040088/* Constants used at several places*/
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -040089const messageBarPlaceHolder = "Type a message"
90const avatar_size = 35
91var raf = window.requestAnimationFrame || window.webkitRequestAnimationFrame
92var displayLinksEnabled = false
93var historyBufferIndex = 0 // When showing a large amount of interactions, this counter store the interaction's index to show
94var historyBuffer = [] // Before showing a large amount of interactions, this array is used as a buffer.
95var hoverBackButtonAllowed = true
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -040096
97/* We retrieve refs to the most used navbar and message bar elements for efficiency purposes */
98/* NOTE: always use getElementById when possible, way more efficient */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -040099const backButton = document.getElementById("backButton")
100const aliasField = document.getElementById("nav-contactid-alias")
101const bestIdField = document.getElementById("nav-contactid-bestId")
102const idField = document.getElementById("nav-contactid")
103const messageBar = document.getElementById("sendMessage")
104const messageBarInput = document.getElementById("message")
105const messages = document.getElementById("messages")
106const addToConvButton = document.getElementById("addToConversationsButton")
107const invitation = document.getElementById("invitation")
108const navbar = document.getElementById("navbar")
109const invitationText = document.getElementById("text")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400110
111/* States: allows us to avoid re-doing something if it isn't meaningful */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400112var isBanned = false
113var isTemporary = false
114var hasInvitation = false
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400115
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400116/**
117 * Update common frame between conversations.
118 *
119 * Whenever the current conversation is switched, information from the navbar
120 * and message bar have to be updated to match new contact. This function
121 * handles most of the required work (except the showing/hiding the invitation,
122 * which is handled by showInvitation()).
123 *
124 * @param banned whether contact is banned or not
125 * @param temporary whether contact is temporary or not
126 * @param alias
127 * @param bestId
128 */
129/* exported update_chatview_frame */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400130function update_chatview_frame(banned, temporary, alias, bestid) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400131 navbar.style.display = "none"
AmarOk6286ad42017-07-14 12:11:08 -0400132
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400133 hoverBackButtonAllowed = true
AmarOk6286ad42017-07-14 12:11:08 -0400134
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400135 aliasField.innerHTML = (alias ? alias : bestid)
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400136
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400137 if(alias) {
138 bestIdField.innerHTML = bestid
139 idField.classList.remove("oneEntry")
140 } else {
141 idField.classList.add("oneEntry")
142 }
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400143
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400144 if (isBanned !== banned) {
145 isBanned = banned
146 hideMessageBar(banned)
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400147
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400148 if(banned) {
149 // contact is banned. update navbar and states
150 navbar.classList.add("onBannedState")
151 } else {
152 navbar.classList.remove("onBannedState")
153 }
154 } else if (isTemporary !== temporary) {
155 isTemporary = temporary
156 if (temporary) {
157 addToConvButton.style.display = "flex"
158 messageBarInput.placeholder = "Note: an interaction will create a new contact."
159 } else {
160 addToConvButton.style.display = ""
161 messageBarInput.placeholder = messageBarPlaceHolder
162 }
163 }
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400164
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400165 navbar.style.display = ""
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400166}
167
168/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400169 * Hide or show invitation.
170 *
171 * Invitation is hidden if no contactAlias/invalid alias is passed.
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400172 * Otherwise, invitation div is updated.
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400173 *
174 * @param contactAlias
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400175 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400176/* exported showInvitation */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400177function showInvitation(contactAlias) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400178 if (!contactAlias) {
179 if (hasInvitation) {
180 hasInvitation = false
181 invitation.style.visibility = ""
182 }
183 } else {
184 hasInvitation = true
185 invitationText.innerHTML = "<h1>" + contactAlias + " sends you an invitation</h1>"
186 + "Do you want to add them to the conversations list?<br>"
187 + "Note: you can automatically accept this invitation by sending a message."
188 invitation.style.visibility = "visible"
189 }
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400190}
191
192/**
193 * Hide or show navbar, and update body top padding accordingly.
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400194 *
195 * @param isVisible whether navbar should be displayed or not
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400196 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400197/* exported displayNavbar */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400198function displayNavbar(isVisible)
199{
200 if (isVisible) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400201 navbar.classList.remove("hiddenState")
202 document.documentElement.style.setProperty("--navbar-size", undefined)
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400203 } else {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400204 navbar.classList.add("hiddenState")
205 document.documentElement.style.setProperty("--navbar-size", "0")
AmarOk6286ad42017-07-14 12:11:08 -0400206 }
207}
208
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400209/**
210 * Hide or show message bar, and update body bottom padding accordingly.
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400211 *
212 * @param isHidden whether message bar should be displayed or not
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400213 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400214/* exported hideMessageBar */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400215function hideMessageBar(isHidden) {
216 if (isHidden) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400217 messageBar.classList.add("hiddenState")
218 document.body.style.setProperty("--messagebar-size", "0")
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400219 } else {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400220 messageBar.classList.remove("hiddenState")
221 document.body.style.removeProperty("--messagebar-size")
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400222 }
223}
224
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400225/* exported setDisplayLinks */
226function setDisplayLinks(display) {
227 displayLinksEnabled = display
228}
229
230/**
231 * This event handler dynamically resizes the message bar depending on the amount of
232 * text entered, while adjusting the body paddings so that that the message bar doesn't
233 * overlap messages when it grows.
234 */
235/* exported grow_text_area */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400236function grow_text_area() {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400237 var is_at_bottom = messages.scrollTop === (messages.scrollHeight - messages.offsetHeight)
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400238
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400239 var old_height = window.getComputedStyle(messageBar).height
240 messageBarInput.style.height = "auto" /* <-- necessary, no clue why */
241 messageBarInput.style.height = messageBarInput.scrollHeight + "px"
242 var new_height = window.getComputedStyle(messageBar).height
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400243
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400244 var msgbar_size = window.getComputedStyle(document.body).getPropertyValue("--messagebar-size")
245 var total_size = parseInt(msgbar_size) + parseInt(new_height) - parseInt(old_height)
246
247 document.body.style.setProperty("--messagebar-size", total_size.toString() + "px")
248
249 if (is_at_bottom) {
250 messages.scrollTop = messages.scrollHeight
251 }
252}
253
254/**
255 * This event handler processes keydown events in the navbar. When pressed key is
256 * the enter key, send the message unless shift or control was pressed too.
257 *
258 * @param key the pressed key
259 */
260/* exported process_messagebar_keydown */
261function process_messagebar_keydown(key) {
262 key = key || event
263 var map = {}
264 map[key.keyCode] = key.type == "keydown"
265 if (key.ctrlKey || key.shiftKey) {
266 return true
267 }
268 if (map[13]) {
269 sendMessage()
270 key.preventDefault()
271 }
272 return true
273}
274
275/**
276 * Disable or enable textarea.
277 *
278 * @param isDisabled whether message bar should be enabled or disabled
279 */
280/* exported disableSendMessage */
281function disableSendMessage(isDisabled)
282{
283 messageBarInput.disabled = isDisabled
284}
285
286/**
287 * This event handler adds the hover property back to the "back to welcome view"
288 * button.
289 *
290 * This is a hack. It needs some explanations.
291 *
292 * Problem: Whenever the "back to welcome view" button is clicked, the webview
293 * freezes and the GTK ring welcome view is displayed. While the freeze
294 * itself is perfectly fine (probably necessary for good performances), this
295 * is a big problem for us when the user opens a chatview again: Since the
296 * chatview was freezed, the back button has «remembered» the hover state and
297 * still displays the blue background for a small instant. This is a very bad
298 * looking artefact.
299 *
300 * In order to counter this problem, we introduced the following evil mechanism:
301 * Whenever a user clicks on the "back to welcome view" button, the hover
302 * property is disabled. The hover property stays disabled until the user calls
303 * this event handler by hover-ing the button.
304 */
305/* exported addBackButtonHoverProperty */
306function addBackButtonHoverProperty()
307{
308 if(hoverBackButtonAllowed) {
309 backButton.classList.add("non-action-button")
310 }
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400311}
312
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400313/*
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400314 * Update timestamps messages.
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400315 */
316function updateView() {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400317 updateTimestamps()
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400318}
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400319
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400320setInterval(updateView, 60000)
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400321
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400322window.onresize = function() {
323 updateTimestamps()
324}
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400325
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400326/* exported addBannedContact */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400327function addBannedContact()
328{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400329 window.prompt("UNBLOCK")
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400330}
331
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400332/* exported addToConversations */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400333function addToConversations()
334{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400335 window.prompt("ADD_TO_CONVERSATIONS")
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400336}
337
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400338/* exported placeCall */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400339function placeCall()
340{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400341 window.prompt("PLACE_CALL")
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400342}
343
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400344/* exported placeAudioCall */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400345function placeAudioCall()
346{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400347 window.prompt("PLACE_AUDIO_CALL")
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400348}
349
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400350/* exported backToWelcomeView */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400351function backToWelcomeView()
352{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400353 backButton.classList.remove("non-action-button")
354 hoverBackButtonAllowed = false
355 window.prompt("CLOSE_CHATVIEW")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400356}
aviau039001d2016-09-29 16:39:05 -0400357
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400358/**
359 * Transform a date to a string group like "1 hour ago".
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400360 *
361 * @param date
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400362 */
363function formatDate(date) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400364 const seconds = Math.floor((new Date() - date) / 1000)
365 var interval = Math.floor(seconds / (3600 * 24))
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400366 if (interval > 5) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400367 return date.toLocaleDateString()
Sébastien Blin70dc0b72017-07-31 16:24:41 -0400368 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400369 if (interval > 1) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400370 return interval + " days ago"
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400371 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400372 if (interval === 1) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400373 return interval + " day ago"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400374 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400375 interval = Math.floor(seconds / 3600)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400376 if (interval > 1) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400377 return interval + " hours ago"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400378 }
379 if (interval === 1) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400380 return interval + " hour ago"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400381 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400382 interval = Math.floor(seconds / 60)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400383 if (interval > 1) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400384 return interval + " minutes ago"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400385 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400386 return "just now"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400387}
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400388
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400389/**
390 * Send #sendMessage #message value
391 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400392function sendMessage()
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400393{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400394 var message = messageBarInput.value
395 if (message.length > 0) {
396 messageBarInput.value = ""
397 window.prompt("SEND:" + message)
398 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400399}
Sébastien Blinf4f90282017-10-03 14:07:16 -0400400
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400401/* exported acceptInvitation */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400402function acceptInvitation()
403{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400404 window.prompt("ACCEPT")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400405}
Sébastien Blinf4f90282017-10-03 14:07:16 -0400406
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400407/* exported refuseInvitation */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400408function refuseInvitation()
409{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400410 window.prompt("REFUSE")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400411}
Sébastien Blinf4f90282017-10-03 14:07:16 -0400412
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400413/* exported blockConversation */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400414function blockConversation()
415{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400416 window.prompt("BLOCK")
417}
418
419/* exported sendFile */
420function sendFile()
421{
422 window.prompt("SEND_FILE")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400423}
Sébastien Blinf4f90282017-10-03 14:07:16 -0400424
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400425/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400426 * Clear all messages.
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400427 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400428/* exported clearMessages */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400429function clearMessages()
430{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400431 messages.innerHTML = ""
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400432}
433
434/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400435 * Convert text to HTML.
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400436 */
437function escapeHtml(html)
438{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400439 var text = document.createTextNode(html)
440 var div = document.createElement("div")
441 div.appendChild(text)
442 return div.innerHTML
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400443}
444
445
446/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400447 * Get the youtube video id from a URL.
448 * @param url
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400449 */
450function youtube_id(url) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400451 const regExp = /^.*(youtu\.be\/|v\/|\/u\/w|embed\/|watch\?v=|&v=)([^#&?]*).*/
452 const match = url.match(regExp)
453 return (match && match[2].length == 11) ? match[2] : null
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400454}
455
456/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400457 * Returns HTML message from the message text, cleaned and linkified.
458 * @param message_text
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400459 */
460function getMessageHtml(message_text)
461{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400462 const escaped_message = escapeHtml(message_text)
463 var linkified_message = linkifyHtml(escaped_message, {}) // eslint-disable-line no-undef
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400464
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400465 const textPart = document.createElement("pre")
466 textPart.innerHTML = linkified_message
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400467
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400468 return textPart.outerHTML
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400469}
470
471/**
472 * Returns the message status, formatted for display
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400473 * @param message_delivery_status
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400474 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400475/* exported getMessageDeliveryStatusText */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400476function getMessageDeliveryStatusText(message_delivery_status)
477{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400478 var formatted_delivery_status = message_delivery_status
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400479
480 switch(message_delivery_status)
aviau039001d2016-09-29 16:39:05 -0400481 {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400482 case "sending":
483 case "ongoing":
484 formatted_delivery_status = "Sending<svg overflow='visible' viewBox='0 -2 16 14' height='16px' width='16px'><circle class='status_circle anim-first' cx='4' cy='12' r='1'/><circle class='status_circle anim-second' cx='8' cy='12' r='1'/><circle class='status_circle anim-third' cx='12' cy='12' r='1'/></svg>"
485 break
486 case "failure":
487 formatted_delivery_status = "Failure <svg overflow='visible' viewBox='0 -2 16 14' height='16px' width='16px'><path class='status-x x-first' stroke='#AA0000' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' fill='none' d='M4,4 L12,12'/><path class='status-x x-second' stroke='#AA0000' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' fill='none' d='M12,4 L4,12'/></svg>"
488 break
489 case "sent":
490 case "finished":
491 case "unknown":
492 case "read":
493 formatted_delivery_status = ""
494 break
495 default:
496 break
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400497 }
498
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400499 return formatted_delivery_status
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400500}
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400501
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400502/**
503 * Returns the message date, formatted for display
504 */
505function getMessageTimestampText(message_timestamp, custom_format)
506{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400507 const date = new Date(1000 * message_timestamp)
508 if(custom_format) {
509 return formatDate(date)
510 } else {
511 return date.toLocaleString()
512 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400513}
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400514
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400515/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400516 * Merge timestamps if they are from the same group. For instance, several "1 hour ago"
517 * timestamps will be merged into a single one.
518 *
519 * @param baseNode
520 * @param endIndex
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400521 */
522function cleanPreviousTimestamps (baseNode, endIndex) {
523 // Remove previous elements with the same formatted timestamp
524 for (var c = endIndex - 1 ; c >= 0 ; --c) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400525 const child = messages.children[c]
526 if (child.className.indexOf("timestamp") !== -1) {
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400527 if (child.className === baseNode.className &&
528 child.innerHTML === baseNode.innerHTML) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400529 messages.removeChild(child)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400530 } else {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400531 break // A different timestamp is met, we can stop here.
Sébastien Blin1e2dd352018-04-13 11:34:53 -0400532 }
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400533 }
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400534 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400535}
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400536
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400537/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400538 * Update timestamps.
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400539 */
540function updateTimestamps() {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400541 const timestamps = messages.getElementsByClassName(".timestamp")
542 const is_image_out = messages.querySelector(".message_out .sender_image")
543 const is_image_in = messages.querySelector(".message_in .sender_image")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400544 if (timestamps) {
545 for ( var c = 0 ; c < messages.children.length ; ++c) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400546 const child = messages.children[c]
547 if (child.className.indexOf("timestamp") !== -1) {
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400548 // Update timestamp
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400549 child.innerHTML = getMessageTimestampText(child.getAttribute("message_timestamp"), true)
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400550
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400551 var desktop_margin = "25%"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400552 const height = document.body.clientHeight
553 const width = document.body.clientWidth
554 if (width <= 1920 || height <= 1080) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400555 desktop_margin = "15%"
Adrien Beraud8e25afb2017-04-19 01:38:57 +0200556 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400557 if (width <= 1000 || height <= 480) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400558 desktop_margin = "0px"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400559 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400560 if (child.className.indexOf("timestamp_out") !== -1) {
561 const avatar_px = is_image_out ? (is_image_out.offsetHeight === avatar_size ? "60px" : "20px") : "20px"
562 child.style.paddingRight = `calc(${desktop_margin} + ${avatar_px})`
563 } else if (child.className.indexOf("timestamp_in") !== -1) {
564 const avatar_px = is_image_in ? (is_image_in.offsetHeight === avatar_size ? "60px" : "20px") : "20px"
565 child.style.paddingLeft = `calc(${desktop_margin} + ${avatar_px})`
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400566 }
567 // Remove previous elements with the same formatted timestamp
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400568 cleanPreviousTimestamps(child, c)
Adrien Beraud8e25afb2017-04-19 01:38:57 +0200569 }
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500570 }
aviau039001d2016-09-29 16:39:05 -0400571 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400572}
aviau039001d2016-09-29 16:39:05 -0400573
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400574/**
575 * Convert a value in filesize
576 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400577function humanFileSize(bytes) {
578 var thresh = 1024
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400579 if(Math.abs(bytes) < thresh) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400580 return bytes + " B"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400581 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400582 var units = ["kB","MB","GB","TB","PB","EB","ZB","YB"]
583 var u = -1
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400584 do {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400585 bytes /= thresh
586 ++u
587 } while(Math.abs(bytes) >= thresh && u < units.length - 1)
588 return bytes.toFixed(1)+" "+units[u]
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400589}
590
591/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400592 * Change the value of the progress bar.
593 *
594 * @param progress_bar
595 * @param message_object
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400596 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400597function updateProgressBar(progress_bar, message_object) {
598 var delivery_status = message_object["delivery_status"]
599 if ("progress" in message_object && !isErrorStatus(delivery_status) && message_object["progress"] !== 100) {
600 var progress_percent = (100 * message_object["progress"] / message_object["totalSize"])
601 if (progress_percent !== 100)
602 progress_bar.childNodes[0].setAttribute("style", "width: " + progress_percent + "%")
603 else
604 progress_bar.setAttribute("style", "display: none")
605 } else
606 progress_bar.setAttribute("style", "display: none")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400607}
608
609/**
610 * Check if a status is an error status
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400611 * @param
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400612 */
613function isErrorStatus(status) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400614 return (status === "failure"
615 || status === "awaiting peer timeout"
616 || status === "canceled"
617 || status === "unjoinable peer")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400618}
619
620/**
621 * Build a new file interaction
622 * @param message_id
623 */
624function fileInteraction(message_id) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400625 var message_wrapper = document.createElement("div")
626 message_wrapper.setAttribute("class", "message_wrapper")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400627
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400628 /* A file interaction contains buttons at the left for status information
629 or accept/refuse actions. The text is bold and clickable. */
630 var left_buttons = document.createElement("div")
631 left_buttons.setAttribute("class", "left_buttons")
632 message_wrapper.appendChild(left_buttons)
633 var text_div = document.createElement("div")
634 text_div.setAttribute("class", "text")
635
636 text_div.addEventListener("click", function () {
637 // ask ring to open the file
638 const filename = document.querySelector("#message_" + message_id + " .full").innerText
639 window.prompt("OPEN_FILE:" + filename)
640 })
641
642 var full_div = document.createElement("div")
643 full_div.setAttribute("class", "full")
644 full_div.style = "visibility: hidden; display: none;"
645 var message_text = document.createElement("div")
646 message_text.setAttribute("class", "filename")
647
648 // And some informations like size or error message.
649 var informations_div = document.createElement("div")
650 informations_div.setAttribute("class", "informations")
651 text_div.appendChild(message_text)
652 text_div.appendChild(full_div)
653 text_div.appendChild(informations_div)
654 message_wrapper.appendChild(text_div)
655
656 // And finally, a progress bar
657 var message_transfer_progress_bar = document.createElement("span")
658 message_transfer_progress_bar.setAttribute("class", "message_progress_bar")
659 var message_transfer_progress_completion = document.createElement("span")
660 message_transfer_progress_bar.appendChild(message_transfer_progress_completion)
661 message_wrapper.appendChild(message_transfer_progress_bar)
662 return message_wrapper
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400663}
664
665/**
666 * Update a file interaction (icons + filename + status + progress bar)
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400667 *
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400668 * @param message_div the message to update
669 * @param message_object new informations
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400670 * @param forceTypeToFile
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400671 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400672function updateFileInteraction(message_div, message_object, forceTypeToFile = false) {
673 if (!message_div.querySelector(".informations")) return // media
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400674
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400675 var acceptSvg = "<svg height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z\"/></svg>",
676 refuseSvg = "<svg height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/><path d=\"M0 0h24v24H0z\" fill=\"none\"/></svg>",
677 fileSvg = "<svg fill=\"#000000\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z\"/><path d=\"M0 0h24v24H0z\" fill=\"none\"/></svg>",
678 warningSvg = "<svg fill=\"#000000\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/></svg>"
679 var message_delivery_status = message_object["delivery_status"]
680 var message_direction = message_object["direction"]
681 var message_id = message_object["id"]
682 var message_text = message_object["text"]
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400683
684
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400685 if (isImage(message_text) && message_delivery_status === "finished" && displayLinksEnabled && !forceTypeToFile) {
686 // Replace the old wrapper by the downloaded image
687 if (message_div.querySelector(".message_wrapper")) {
688 var wrapper = message_div.querySelector(".message_wrapper")
689 wrapper.parentNode.removeChild(wrapper)
690 }
691 message_div.append(mediaInteraction(message_id, message_text))
692 message_div.querySelector("img").id = message_id
693 message_div.querySelector("img").msg_obj = message_object
694 message_div.querySelector("img").onerror = function() {
695 message_div = document.getElementById("#message_" + this.id)
696 var wrapper = message_div.querySelector(".message_wrapper")
697 if (wrapper) {
698 wrapper.parentNode.removeChild(wrapper)
699 }
700 message_div.append(fileInteraction(message_id))
701 updateFileInteraction(message_div, this.msg_obj, true)
702 }
703 return
704 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400705
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400706 // Set informations text
707 var informations_div = message_div.querySelector(".informations")
708 var informations_txt = getMessageTimestampText(message_object["timestamp"], true)
709 if (message_object["totalSize"] && message_object["progress"]) {
710 if (message_delivery_status === "finished") {
711 informations_txt += " - " + humanFileSize(message_object["totalSize"])
712 } else {
713 informations_txt += " - " + humanFileSize(message_object["progress"])
714 + " / " + humanFileSize(message_object["totalSize"])
715 }
716 }
717 informations_txt += " - " + message_delivery_status
718 informations_div.innerText = informations_txt
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400719
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400720 // Update flat buttons
721 var left_buttons = message_div.querySelector(".left_buttons")
722 left_buttons.innerHTML = ""
723 if (message_delivery_status === "awaiting peer" || message_delivery_status === "awaiting host" || message_delivery_status.indexOf("ongoing") === 0) {
724 if (message_direction === "in" && message_delivery_status.indexOf("ongoing") !== 0) {
725 // add buttons to accept or refuse a call.
726 var accept_button = document.createElement("div")
727 accept_button.innerHTML = acceptSvg
728 accept_button.setAttribute("title", "Accept")
729 accept_button.setAttribute("class", "flat-button accept")
730 accept_button.onclick = function() {
731 window.prompt("ACCEPT_FILE:" + message_id)
732 }
733 left_buttons.appendChild(accept_button)
734 }
735 var refuse_button = document.createElement("div")
736 refuse_button.innerHTML = refuseSvg
737 refuse_button.setAttribute("title", "Refuse")
738 refuse_button.setAttribute("class", "flat-button refuse")
739 refuse_button.onclick = function() {
740 window.prompt("REFUSE_FILE:" + message_id)
741 }
742 left_buttons.appendChild(refuse_button)
743 } else {
744 var status_button = document.createElement("div")
745 var statusFile = fileSvg
746 if (isErrorStatus(message_delivery_status))
747 statusFile = warningSvg
748 status_button.innerHTML = statusFile
749 status_button.setAttribute("class", "flat-button")
750 left_buttons.appendChild(status_button)
751 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400752
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400753 message_div.querySelector(".full").innerText = message_text
754 message_div.querySelector(".filename").innerText = message_text.split("/").pop()
755 message_div.querySelector(".filename").innerText = message_text.split("/").pop()
756 updateProgressBar(message_div.querySelector(".message_progress_bar"), message_object)
757 updateProgressBar(message_div.querySelector(".message_progress_bar"), message_object)
758}
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400759
760/**
761 * Return if a file is an image
762 * @param file
763 */
764function isImage(file) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400765 return file.toLowerCase().match(/\.(jpeg|jpg|gif|png)$/) !== null
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400766}
767
768/**
769 * Return if a file is a youtube video
770 * @param file
771 */
772function isVideo(file) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400773 const availableProtocols = ["http:", "https:"]
774 const videoHostname = ["youtube.com", "www.youtube.com", "youtu.be"]
775 const urlParser = document.createElement("a")
776 urlParser.href = file
777 return (availableProtocols.includes(urlParser.protocol) && videoHostname.includes(urlParser.hostname))
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400778}
779
780/**
781 * Try to show an image or a video link (youtube for now)
782 * @param message_id
783 * @param link to show
784 * @param ytid if it's a youtube video
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400785 * @param
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400786 */
787function mediaInteraction(message_id, link, ytid, noerror) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400788 /* TODO promise?
789 Try to display images. */
790 const media_wrapper = document.createElement("div")
791 media_wrapper.setAttribute("class", "media_wrapper")
792 const linkElt = document.createElement("a")
793 linkElt.href = link
794 linkElt.style = "text-decoration: none; border:none;"
795 const imageElt = document.createElement("img")
796 /* Note, here, we don't check the size of the image.
797 in the future, we can check the content-type and content-length with a request
798 and maybe disable svg */
799 if (noerror)
800 imageElt.setAttribute("onerror", "this.style.display='none'")
801 if (ytid) {
802 imageElt.src = `http://img.youtube.com/vi/${ytid}/0.jpg`
803 } else {
804 imageElt.src = link
805 }
806 linkElt.appendChild(imageElt)
807 if (ytid) {
808 const containerElt = document.createElement("div")
809 containerElt.setAttribute("class", "containerVideo")
810 const playDiv = document.createElement("div")
811 playDiv.setAttribute("class", "playVideo")
812 playDiv.innerHTML = "<svg fill=\"#ffffff\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\
813 <path d=\"M8 5v14l11-7z\"/>\
814 <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\
815 </svg>"
816 linkElt.appendChild(playDiv)
817 containerElt.appendChild(linkElt)
818 media_wrapper.appendChild(containerElt)
819 } else {
820 media_wrapper.appendChild(linkElt)
821 }
822 return media_wrapper
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400823}
824
825/**
826 * Build a new text interaction
827 * @param message_id
828 * @param htmlText the DOM to show
829 */
830function textInteraction(message_id, htmlText) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400831 const message_wrapper = document.createElement("div")
832 message_wrapper.setAttribute("class", "message_wrapper")
833 var message_text = document.createElement("span")
834 message_text.setAttribute("class", "message_text")
835 message_text.innerHTML = htmlText
836 message_wrapper.appendChild(message_text)
837 // TODO STATUS
838 return message_wrapper
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400839}
840
841/**
842 * Update a text interaction (text)
843 * @param message_div the message to update
844 * @param delivery_status the status of the message
845 * @TODO retry button
846 */
847function updateTextInteraction(message_div, delivery_status) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400848 if (!message_div.querySelector(".message_text")) return // media
849 var sending = message_div.querySelector(".sending")
850 switch(delivery_status)
851 {
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400852 case "ongoing":
853 case "sending":
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400854 if (!sending) {
855 sending = document.createElement("div")
856 sending.setAttribute("class", "sending")
857 sending.innerHTML = "<svg overflow=\"hidden\" viewBox=\"0 -2 16 14\" height=\"16px\" width=\"16px\"><circle class=\"status_circle anim-first\" cx=\"4\" cy=\"12\" r=\"1\"/><circle class=\"status_circle anim-second\" cx=\"8\" cy=\"12\" r=\"1\"/><circle class=\"status_circle anim-third\" cx=\"12\" cy=\"12\" r=\"1\"/></svg>"
858 // add sending animation to message;
859 message_div.appendChild(sending)
860 }
861 message_div.querySelector(".message_text").style = "color: #888"
862 break
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400863 case "failure":
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400864 // change text color to red
865 message_div.querySelector(".message_wrapper").style = "background-color: #f3a6a6"
866 message_div.querySelector(".message_text").style = "color: #000"
867 var failure_div = message_div.querySelector(".failure")
868 if (!failure_div) {
869 failure_div = document.createElement("div")
870 failure_div.setAttribute("class", "failure")
871 failure_div.innerHTML = "<svg overflow=\"visible\" viewBox=\"0 -2 16 14\" height=\"16px\" width=\"16px\"><path class=\"status-x x-first\" stroke=\"#AA0000\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"3\" fill=\"none\" d=\"M4,4 L12,12\"/><path class=\"status-x x-second\" stroke=\"#AA0000\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"3\" fill=\"none\" d=\"M12,4 L4,12\"/></svg>"
872 // add failure animation to message
873 message_div.appendChild(failure_div)
874 }
875 if (sending) sending.style.display = "none"
876 break
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400877 case "sent":
878 case "finished":
879 case "unknown":
880 case "read":
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400881 // change text color to black
882 message_div.querySelector(".message_text").style = "color: #000"
883 if (sending) sending.style.display = "none"
884 break
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400885 default:
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400886 break
887 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400888}
889
890/**
891 * Build a new interaction (call or contact)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400892 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400893function actionInteraction() {
894 var message_wrapper = document.createElement("div")
895 message_wrapper.setAttribute("class", "message_wrapper")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400896
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400897 // An file interaction contains buttons at the left of the interaction
898 // for the status or accept/refuse
899 var left_buttons = document.createElement("div")
900 left_buttons.setAttribute("class", "left_buttons")
901 message_wrapper.appendChild(left_buttons)
902 // Also contains a bold clickable text
903 var text_div = document.createElement("div")
904 text_div.setAttribute("class", "text")
905 message_wrapper.appendChild(text_div)
906 return message_wrapper
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400907}
908
909/**
910 * Update a call interaction (icon + text)
911 * @param message_div the message to update
912 * @param message_object new informations
913 */
914function updateCallInteraction(message_div, message_object) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400915 const outgoingCall = "<svg fill=\"#219d55\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5z\"/></svg>"
916 const callMissed = "<svg fill=\"#dc2719\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M19.59 7L12 14.59 6.41 9H11V7H3v8h2v-4.59l7 7 9-9z\"/></svg>"
917 const outgoingMissed = "<svg fill=\"#dc2719\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><defs><path d=\"M24 24H0V0h24v24z\" id=\"a\"/></defs><clipPath id=\"b\"><use overflow=\"visible\" xlink:href=\"#a\"/></clipPath><path clip-path=\"url(#b)\" d=\"M3 8.41l9 9 7-7V15h2V7h-8v2h4.59L12 14.59 4.41 7 3 8.41z\"/></svg>"
918 const callReceived = "<svg fill=\"#219d55\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M20 5.41L18.59 4 7 15.59V9H5v10h10v-2H8.41z\"/></svg>"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400919
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400920 const message_text = message_object["text"]
921 const message_direction = (message_text.toLowerCase().indexOf("incoming") !== -1) ? "in" : "out"
922 const missed = message_text.indexOf("Missed") !== -1
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400923
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400924 message_div.querySelector(".text").innerText = message_text.substring(2)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400925
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400926 var left_buttons = message_div.querySelector(".left_buttons")
927 left_buttons.innerHTML = ""
928 var status_button = document.createElement("div")
929 var statusFile = ""
930 if (missed)
931 statusFile = (message_direction === "in") ? callMissed : outgoingMissed
932 else
933 statusFile = (message_direction === "in") ? callReceived : outgoingCall
934 status_button.innerHTML = statusFile
935 status_button.setAttribute("class", "flat-button")
936 left_buttons.appendChild(status_button)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400937}
938
939/**
940 * Update a contact interaction (icon + text)
941 * @param message_div the message to update
942 * @param message_object new informations
943 */
944function updateContactInteraction(message_div, message_object) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400945 const message_text = message_object["text"]
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400946
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400947 message_div.querySelector(".text").innerText = message_text
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400948
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400949 var left_buttons = message_div.querySelector(".left_buttons")
950 left_buttons.innerHTML = ""
951 var status_button = document.createElement("div")
952 status_button.innerHTML = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\
953<path d=\"M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z\"/>\
954<path d=\"M0 0h24v24H0z\" fill=\"none\"/></svg>"
955 status_button.setAttribute("class", "flat-button")
956 left_buttons.appendChild(status_button)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400957}
958
959/**
960 * Add a message to the conversation.
961 * @param message_object to treat
962 * @param new_message if it's a new message or if we need to update
963 * @param insert_after if we want the message at the end or the top of the conversation
964 */
965function addOrUpdateMessage(message_object, new_message, insert_after = true) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400966 const message_id = message_object["id"]
967 const message_type = message_object["type"]
968 const message_text = message_object["text"]
969 const message_direction = message_object["direction"]
970 const message_timestamp = message_object["timestamp"]
971 const delivery_status = message_object["delivery_status"]
972 const message_sender_contact_method = message_object["sender_contact_method"]
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400973
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400974 var message_div = document.getElementById("message_" + message_id)
975 var type = ""
976 if (new_message) {
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400977 // Build new message
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400978 var classes = [
979 "message",
980 `message_${message_direction}`,
981 `message_type_${message_type}`
982 ]
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400983
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400984 message_div = document.createElement("div")
985 message_div.setAttribute("id", `message_${message_id}`)
986 message_div.setAttribute("class", classes.join(" "))
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400987
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400988 // Build message for each types.
989 // Add sender images if necessary (like if the interaction doesn't take the whole width)
990 const need_sender = (message_type === "data_transfer" || message_type === "text")
991 if (need_sender) {
992 var message_sender_image = document.createElement("span")
993 message_sender_image.setAttribute("class", `sender_image sender_image_${message_sender_contact_method}`)
994 message_div.appendChild(message_sender_image)
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500995 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400996
997 // Build main content
998 if (message_type === "data_transfer") {
999 if (isImage(message_text) && delivery_status === "finished" && displayLinksEnabled) {
1000 message_div.append(mediaInteraction(message_id, message_text, null, false))
1001 message_div.querySelector("img").id = message_id
1002 message_div.querySelector("img").msg_obj = message_object
1003 message_div.querySelector("img").onerror = function() {
1004 message_div = document.getElementById("#message_" + this.id)
1005 /* FIXME in many cases, message_div is null here and this code doesn't do anything */
1006 if (message_div.querySelector(".message_wrapper")) {
1007 var wrapper = message_div.querySelector(".message_wrapper")
1008 wrapper.parentNode.removeChild(wrapper)
1009 }
1010 message_div.append(fileInteraction(message_id))
1011 updateFileInteraction(message_div, this.msg_obj, true)
1012 }
1013 } else {
1014 message_div.append(fileInteraction(message_id))
1015 }
1016 } else if (message_type === "text") {
1017 // TODO add the possibility to update messages (remove and rebuild)
1018 const htmlText = getMessageHtml(message_text)
1019 if (displayLinksEnabled) {
1020 const parser = new DOMParser()
1021 const DOMMsg = parser.parseFromString(htmlText, "text/xml")
1022 const links = DOMMsg.querySelectorAll("a")
1023 if (DOMMsg.childNodes.length && links.length) {
1024 var isTextToShow = (DOMMsg.childNodes[0].childNodes.length > 1)
1025 const ytid = (isVideo(message_text))? youtube_id(message_text) : ""
1026 if (!isTextToShow && (ytid || isImage(message_text))) {
1027 type = "media"
1028 message_div.append(mediaInteraction(message_id, message_text, ytid))
1029 }
1030 }
1031 }
1032 if (type !== "media") {
1033 type = "text"
1034 message_div.append(textInteraction(message_id, htmlText))
1035 }
1036 } else if (message_type === "call" || message_type === "contact") {
1037 message_div.append(actionInteraction())
1038 } else {
1039 const temp = document.createElement("div")
1040 temp.innerText = message_type
1041 message_div.appendChild(temp)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001042 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001043
1044 // Get timestamp to add
1045 const formattedTimestamp = getMessageTimestampText(message_timestamp, true)
1046 // Create the timestamp object
1047 const date_elt = document.createElement("div")
1048 date_elt.innerText = formattedTimestamp
1049 var typeIsCallOrContact = (message_type === "call" || message_type === "contact")
1050 var timestamp_div_classes = ["timestamp", typeIsCallOrContact ? "timestamp_action" : `timestamp_${message_direction}`]
1051 date_elt.setAttribute("class", timestamp_div_classes.join(" "))
1052 date_elt.setAttribute("message_timestamp", message_timestamp)
1053
1054 // Remove last timestamp if it's the same<h6></h6>
1055 if (messages.getElementsByClassName(".timestamp"))
1056 cleanPreviousTimestamps(date_elt, messages.children.length)
1057
1058 // Add message and the new timestamp
1059 if (insert_after) {
1060 if (message_type === "call" || message_type === "contact") {
1061 message_div.querySelector(".message_wrapper").appendChild(date_elt)
1062 messages.appendChild(message_div)
1063 } else {
1064 messages.appendChild(message_div)
1065 messages.appendChild(date_elt)
1066 }
1067 } else {
1068 if (message_type === "call" || message_type === "contact") {
1069 message_div.querySelector(".message_wrapper").appendChild(date_elt)
1070 messages.insertBefore(message_div, messages.firstChild)
1071 } else {
1072 messages.insertBefore(date_elt, messages.firstChild)
1073 messages.insertBefore(message_div, messages.firstChild)
1074 }
1075 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001076 }
1077
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001078 // Update informations if needed
1079 if (message_type === "data_transfer")
1080 updateFileInteraction(message_div, message_object)
1081 if (message_type === "text" && message_direction === "out")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001082 // Modify sent status if necessary
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001083 updateTextInteraction(message_div, delivery_status)
1084 if (message_type === "call")
1085 updateCallInteraction(message_div, message_object)
1086 if (message_type === "contact")
1087 updateContactInteraction(message_div, message_object)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001088
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001089 // Clean timestamps
1090 updateTimestamps()
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001091}
1092
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001093/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001094 * Add a message to the buffer.
1095 *
1096 * @param message_object message to be added
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001097 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001098/* exported addMessage */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001099function addMessage(message_object)
1100{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001101 var atEnd = messages.scrollTop >= messages.scrollHeight - messages.clientHeight - 5
1102 addOrUpdateMessage(message_object, true)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001103 if (atEnd) {
1104 var startTime = Date.now(),
1105 durTime = 250.,
1106 scrollStartHeight = messages.scrollHeight,
1107 scrollStart = messages.scrollTop,
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001108 scrollDiff = scrollStartHeight - messages.clientHeight - scrollStart
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001109 function loop() {
1110 var time = Date.now() - startTime,
1111 scrollHeight = messages.scrollHeight,
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001112 diff = messages.scrollTop - scrollStart // If user scrolls up (diff > 0).
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001113
1114 if (time >= durTime || scrollHeight != scrollStartHeight || diff < 0) {
1115 if (diff >= 0) { // User scrolled up, don't autoscroll.
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001116 messages.scrollTop = scrollHeight
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001117 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001118 return false
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001119 } else {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001120 messages.scrollTop = scrollStart + (scrollDiff * (time/durTime))
1121 raf(loop)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001122 }
1123 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001124
1125
1126 raf(loop) // Start the animation loop
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001127 }
1128}
1129
1130/**
Hugo Lefeuvreedad8832018-05-14 16:36:06 -04001131 * Show the history in reverse order
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001132 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001133function printHistoryPart() {
1134 if(historyBuffer.length == 0 || historyBufferIndex === historyBuffer.length) {
1135 // nothing to print
1136 return
1137 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001138
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001139 var previousScrollHeightMinusTop = messages.scrollHeight - messages.scrollTop
Hugo Lefeuvreedad8832018-05-14 16:36:06 -04001140
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001141 // Show 10 messages
1142 for (var i = 0; i < 10; ++i) {
1143 addOrUpdateMessage(historyBuffer[historyBuffer.length - 1 - historyBufferIndex], true, false)
1144 historyBufferIndex ++
Hugo Lefeuvreedad8832018-05-14 16:36:06 -04001145
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001146 if (historyBufferIndex === historyBuffer.length)
1147 break
1148 }
Hugo Lefeuvreedad8832018-05-14 16:36:06 -04001149
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001150 // Replace the scrollbar to the wanted position
1151 messages.scrollTop = messages.scrollHeight - previousScrollHeightMinusTop
1152
1153 // Make a short pause to minimize ressources consumption
1154 // show quickly the first hundred messages
1155 if (historyBufferIndex !== historyBuffer.length)
1156 setTimeout(printHistoryPart, (historyBufferIndex > 100) ? 100 : 1)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001157}
1158
1159/**
1160 * Show the whole history in the chatview.
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001161 * @param messages_array array containing history to be printed
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001162 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001163/* exported printHistory */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001164function printHistory(messages_array)
1165{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001166 historyBuffer = messages_array
1167 historyBufferIndex = 0
1168 setTimeout(printHistoryPart, 0)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001169}
1170
1171/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001172 * Update a message that was previously added with addMessage
1173 * @param message_object message to be updated
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001174 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001175/* exported updateMessage */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001176function updateMessage(message_object)
1177{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001178 addOrUpdateMessage(message_object, false)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001179}
1180
1181/**
1182 * Sets the image for a given sender
1183 * set_sender_image object should contain the following keys:
1184 * - sender: the name of the sender
1185 * - sender_image: base64 png encoding of the sender image
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001186 *
1187 * @param set_sender_image_object sender image object as previously described
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001188 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001189/* exported setSenderImage */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001190function setSenderImage(set_sender_image_object)
1191{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001192 var sender_contact_method = set_sender_image_object["sender_contact_method"],
1193 sender_image = set_sender_image_object["sender_image"],
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001194 sender_image_id = "sender_image_" + sender_contact_method,
1195 currentSenderImage = document.getElementById("#" + sender_image_id), // Remove the currently set sender image
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001196 style
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001197
1198 if (currentSenderImage) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001199 currentSenderImage.parentNode.removeChild(currentSenderImage)
Guillaume Roguez5b137be2018-02-21 10:44:58 -05001200 }
1201
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001202 // Create a new style element
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001203 style = document.createElement("style")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001204
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001205 style.type = "text/css"
1206 style.id = sender_image_id
1207 style.innerHTML = "." + sender_image_id + " {content: url(data:image/png;base64," + sender_image + ");height: 35px;width: 35px;}"
1208 document.head.appendChild(style)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001209}
1210
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001211/* exported clearSenderImages */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001212function clearSenderImages()
1213{
1214 var styles = document.head.querySelectorAll("style"),
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001215 i = styles.length
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001216
1217 while (i--){
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001218 document.head.removeChild(styles[i])
aviau039001d2016-09-29 16:39:05 -04001219 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001220}
aviau039001d2016-09-29 16:39:05 -04001221
aviau039001d2016-09-29 16:39:05 -04001222</script>
aviau039001d2016-09-29 16:39:05 -04001223</html>