blob: fec9979c5a14c173c94fc7802b37877fd52d5163 [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
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -040091// scrollDetectionThresh represents the number of pixels a user can scroll
92// without disabling the automatic go-back-to-bottom when a new message is
93// received
94const scrollDetectionThresh = 200
95
96/* Buffers */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -040097var historyBufferIndex = 0 // When showing a large amount of interactions, this counter store the interaction's index to show
98var historyBuffer = [] // Before showing a large amount of interactions, this array is used as a buffer.
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -040099
100/* We retrieve refs to the most used navbar and message bar elements for efficiency purposes */
101/* NOTE: always use getElementById when possible, way more efficient */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400102const backButton = document.getElementById("backButton")
103const aliasField = document.getElementById("nav-contactid-alias")
104const bestIdField = document.getElementById("nav-contactid-bestId")
105const idField = document.getElementById("nav-contactid")
106const messageBar = document.getElementById("sendMessage")
107const messageBarInput = document.getElementById("message")
108const messages = document.getElementById("messages")
109const addToConvButton = document.getElementById("addToConversationsButton")
110const invitation = document.getElementById("invitation")
111const navbar = document.getElementById("navbar")
112const invitationText = document.getElementById("text")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400113
114/* States: allows us to avoid re-doing something if it isn't meaningful */
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400115var displayLinksEnabled = false
116var hoverBackButtonAllowed = true
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400117var hasInvitation = false
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400118var isTemporary = false
119var isBanned = false
120
121/**
122 * Generic wrapper. Execute passed function keeping scroll position identical.
123 *
124 * @param func function to execute
125 * @param args parameters as array
126 */
127function exec_keeping_scroll_position(func, args) {
128 var atEnd = messages.scrollTop >= messages.scrollHeight - messages.clientHeight - scrollDetectionThresh
129 func(...args)
130 if (atEnd) {
131 messages.scrollTop = messages.scrollHeight
132 }
133}
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400134
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400135/**
136 * Update common frame between conversations.
137 *
138 * Whenever the current conversation is switched, information from the navbar
139 * and message bar have to be updated to match new contact. This function
140 * handles most of the required work (except the showing/hiding the invitation,
141 * which is handled by showInvitation()).
142 *
143 * @param banned whether contact is banned or not
144 * @param temporary whether contact is temporary or not
145 * @param alias
146 * @param bestId
147 */
148/* exported update_chatview_frame */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400149function update_chatview_frame(banned, temporary, alias, bestid) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400150 navbar.style.display = "none"
AmarOk6286ad42017-07-14 12:11:08 -0400151
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400152 hoverBackButtonAllowed = true
AmarOk6286ad42017-07-14 12:11:08 -0400153
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400154 aliasField.innerHTML = (alias ? alias : bestid)
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400155
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400156 if(alias) {
157 bestIdField.innerHTML = bestid
158 idField.classList.remove("oneEntry")
159 } else {
160 idField.classList.add("oneEntry")
161 }
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400162
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400163 if (isBanned !== banned) {
164 isBanned = banned
165 hideMessageBar(banned)
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400166
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400167 if(banned) {
168 // contact is banned. update navbar and states
169 navbar.classList.add("onBannedState")
170 } else {
171 navbar.classList.remove("onBannedState")
172 }
173 } else if (isTemporary !== temporary) {
174 isTemporary = temporary
175 if (temporary) {
176 addToConvButton.style.display = "flex"
177 messageBarInput.placeholder = "Note: an interaction will create a new contact."
178 } else {
179 addToConvButton.style.display = ""
180 messageBarInput.placeholder = messageBarPlaceHolder
181 }
182 }
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400183
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400184 navbar.style.display = ""
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400185}
186
187/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400188 * Hide or show invitation.
189 *
190 * Invitation is hidden if no contactAlias/invalid alias is passed.
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400191 * Otherwise, invitation div is updated.
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400192 *
193 * @param contactAlias
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400194 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400195/* exported showInvitation */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400196function showInvitation(contactAlias) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400197 if (!contactAlias) {
198 if (hasInvitation) {
199 hasInvitation = false
200 invitation.style.visibility = ""
201 }
202 } else {
203 hasInvitation = true
204 invitationText.innerHTML = "<h1>" + contactAlias + " sends you an invitation</h1>"
205 + "Do you want to add them to the conversations list?<br>"
206 + "Note: you can automatically accept this invitation by sending a message."
207 invitation.style.visibility = "visible"
208 }
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400209}
210
211/**
212 * Hide or show navbar, and update body top padding accordingly.
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400213 *
214 * @param isVisible whether navbar should be displayed or not
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400215 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400216/* exported displayNavbar */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400217function displayNavbar(isVisible)
218{
219 if (isVisible) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400220 navbar.classList.remove("hiddenState")
221 document.documentElement.style.setProperty("--navbar-size", undefined)
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400222 } else {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400223 navbar.classList.add("hiddenState")
224 document.documentElement.style.setProperty("--navbar-size", "0")
AmarOk6286ad42017-07-14 12:11:08 -0400225 }
226}
227
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400228/**
229 * Hide or show message bar, and update body bottom padding accordingly.
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400230 *
231 * @param isHidden whether message bar should be displayed or not
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400232 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400233/* exported hideMessageBar */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400234function hideMessageBar(isHidden) {
235 if (isHidden) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400236 messageBar.classList.add("hiddenState")
237 document.body.style.setProperty("--messagebar-size", "0")
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400238 } else {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400239 messageBar.classList.remove("hiddenState")
240 document.body.style.removeProperty("--messagebar-size")
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400241 }
242}
243
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400244/* exported setDisplayLinks */
245function setDisplayLinks(display) {
246 displayLinksEnabled = display
247}
248
249/**
250 * This event handler dynamically resizes the message bar depending on the amount of
251 * text entered, while adjusting the body paddings so that that the message bar doesn't
252 * overlap messages when it grows.
253 */
254/* exported grow_text_area */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400255function grow_text_area() {
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400256 exec_keeping_scroll_position(function(){
257 var old_height = window.getComputedStyle(messageBar).height
258 messageBarInput.style.height = "auto" /* <-- necessary, no clue why */
259 messageBarInput.style.height = messageBarInput.scrollHeight + "px"
260 var new_height = window.getComputedStyle(messageBar).height
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400261
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400262 var msgbar_size = window.getComputedStyle(document.body).getPropertyValue("--messagebar-size")
263 var total_size = parseInt(msgbar_size) + parseInt(new_height) - parseInt(old_height)
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400264
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400265 document.body.style.setProperty("--messagebar-size", total_size.toString() + "px")
266 }, [])
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400267}
268
269/**
270 * This event handler processes keydown events in the navbar. When pressed key is
271 * the enter key, send the message unless shift or control was pressed too.
272 *
273 * @param key the pressed key
274 */
275/* exported process_messagebar_keydown */
276function process_messagebar_keydown(key) {
277 key = key || event
278 var map = {}
279 map[key.keyCode] = key.type == "keydown"
280 if (key.ctrlKey || key.shiftKey) {
281 return true
282 }
283 if (map[13]) {
284 sendMessage()
285 key.preventDefault()
286 }
287 return true
288}
289
290/**
291 * Disable or enable textarea.
292 *
293 * @param isDisabled whether message bar should be enabled or disabled
294 */
295/* exported disableSendMessage */
296function disableSendMessage(isDisabled)
297{
298 messageBarInput.disabled = isDisabled
299}
300
301/**
302 * This event handler adds the hover property back to the "back to welcome view"
303 * button.
304 *
305 * This is a hack. It needs some explanations.
306 *
307 * Problem: Whenever the "back to welcome view" button is clicked, the webview
308 * freezes and the GTK ring welcome view is displayed. While the freeze
309 * itself is perfectly fine (probably necessary for good performances), this
310 * is a big problem for us when the user opens a chatview again: Since the
311 * chatview was freezed, the back button has «remembered» the hover state and
312 * still displays the blue background for a small instant. This is a very bad
313 * looking artefact.
314 *
315 * In order to counter this problem, we introduced the following evil mechanism:
316 * Whenever a user clicks on the "back to welcome view" button, the hover
317 * property is disabled. The hover property stays disabled until the user calls
318 * this event handler by hover-ing the button.
319 */
320/* exported addBackButtonHoverProperty */
321function addBackButtonHoverProperty()
322{
323 if(hoverBackButtonAllowed) {
324 backButton.classList.add("non-action-button")
325 }
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400326}
327
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400328/*
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400329 * Update timestamps messages.
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400330 */
331function updateView() {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400332 updateTimestamps()
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400333}
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400334
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400335setInterval(updateView, 60000)
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400336
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400337window.onresize = function() {
338 updateTimestamps()
339}
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400340
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400341/* exported addBannedContact */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400342function addBannedContact()
343{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400344 window.prompt("UNBLOCK")
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400345}
346
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400347/* exported addToConversations */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400348function addToConversations()
349{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400350 window.prompt("ADD_TO_CONVERSATIONS")
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400351}
352
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400353/* exported placeCall */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400354function placeCall()
355{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400356 window.prompt("PLACE_CALL")
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400357}
358
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400359/* exported placeAudioCall */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400360function placeAudioCall()
361{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400362 window.prompt("PLACE_AUDIO_CALL")
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400363}
364
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400365/* exported backToWelcomeView */
Hugo Lefeuvreedad8832018-05-14 16:36:06 -0400366function backToWelcomeView()
367{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400368 backButton.classList.remove("non-action-button")
369 hoverBackButtonAllowed = false
370 window.prompt("CLOSE_CHATVIEW")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400371}
aviau039001d2016-09-29 16:39:05 -0400372
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400373/**
374 * Transform a date to a string group like "1 hour ago".
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400375 *
376 * @param date
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400377 */
378function formatDate(date) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400379 const seconds = Math.floor((new Date() - date) / 1000)
380 var interval = Math.floor(seconds / (3600 * 24))
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400381 if (interval > 5) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400382 return date.toLocaleDateString()
Sébastien Blin70dc0b72017-07-31 16:24:41 -0400383 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400384 if (interval > 1) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400385 return interval + " days ago"
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400386 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400387 if (interval === 1) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400388 return interval + " day ago"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400389 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400390 interval = Math.floor(seconds / 3600)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400391 if (interval > 1) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400392 return interval + " hours ago"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400393 }
394 if (interval === 1) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400395 return interval + " hour ago"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400396 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400397 interval = Math.floor(seconds / 60)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400398 if (interval > 1) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400399 return interval + " minutes ago"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400400 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400401 return "just now"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400402}
Sébastien Blinc6f3a262017-07-28 16:12:24 -0400403
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400404/**
405 * Send #sendMessage #message value
406 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400407function sendMessage()
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400408{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400409 var message = messageBarInput.value
410 if (message.length > 0) {
411 messageBarInput.value = ""
412 window.prompt("SEND:" + message)
413 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400414}
Sébastien Blinf4f90282017-10-03 14:07:16 -0400415
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400416/* exported acceptInvitation */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400417function acceptInvitation()
418{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400419 window.prompt("ACCEPT")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400420}
Sébastien Blinf4f90282017-10-03 14:07:16 -0400421
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400422/* exported refuseInvitation */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400423function refuseInvitation()
424{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400425 window.prompt("REFUSE")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400426}
Sébastien Blinf4f90282017-10-03 14:07:16 -0400427
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400428/* exported blockConversation */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400429function blockConversation()
430{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400431 window.prompt("BLOCK")
432}
433
434/* exported sendFile */
435function sendFile()
436{
437 window.prompt("SEND_FILE")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400438}
Sébastien Blinf4f90282017-10-03 14:07:16 -0400439
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400440/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400441 * Clear all messages.
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400442 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400443/* exported clearMessages */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400444function clearMessages()
445{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400446 messages.innerHTML = ""
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400447}
448
449/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400450 * Convert text to HTML.
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400451 */
452function escapeHtml(html)
453{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400454 var text = document.createTextNode(html)
455 var div = document.createElement("div")
456 div.appendChild(text)
457 return div.innerHTML
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400458}
459
460
461/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400462 * Get the youtube video id from a URL.
463 * @param url
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400464 */
465function youtube_id(url) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400466 const regExp = /^.*(youtu\.be\/|v\/|\/u\/w|embed\/|watch\?v=|&v=)([^#&?]*).*/
467 const match = url.match(regExp)
468 return (match && match[2].length == 11) ? match[2] : null
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400469}
470
471/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400472 * Returns HTML message from the message text, cleaned and linkified.
473 * @param message_text
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400474 */
475function getMessageHtml(message_text)
476{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400477 const escaped_message = escapeHtml(message_text)
478 var linkified_message = linkifyHtml(escaped_message, {}) // eslint-disable-line no-undef
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400479
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400480 const textPart = document.createElement("pre")
481 textPart.innerHTML = linkified_message
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400482
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400483 return textPart.outerHTML
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400484}
485
486/**
487 * Returns the message status, formatted for display
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400488 * @param message_delivery_status
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400489 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400490/* exported getMessageDeliveryStatusText */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400491function getMessageDeliveryStatusText(message_delivery_status)
492{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400493 var formatted_delivery_status = message_delivery_status
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400494
495 switch(message_delivery_status)
aviau039001d2016-09-29 16:39:05 -0400496 {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400497 case "sending":
498 case "ongoing":
499 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>"
500 break
501 case "failure":
502 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>"
503 break
504 case "sent":
505 case "finished":
506 case "unknown":
507 case "read":
508 formatted_delivery_status = ""
509 break
510 default:
511 break
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400512 }
513
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400514 return formatted_delivery_status
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400515}
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400516
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400517/**
518 * Returns the message date, formatted for display
519 */
520function getMessageTimestampText(message_timestamp, custom_format)
521{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400522 const date = new Date(1000 * message_timestamp)
523 if(custom_format) {
524 return formatDate(date)
525 } else {
526 return date.toLocaleString()
527 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400528}
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400529
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400530/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400531 * Merge timestamps if they are from the same group. For instance, several "1 hour ago"
532 * timestamps will be merged into a single one.
533 *
534 * @param baseNode
535 * @param endIndex
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400536 */
537function cleanPreviousTimestamps (baseNode, endIndex) {
538 // Remove previous elements with the same formatted timestamp
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400539 exec_keeping_scroll_position(function() {
540 for (var c = endIndex - 1 ; c >= 0 ; --c) {
541 const child = messages.children[c]
542 if (child.className.indexOf("timestamp") !== -1) {
543 if (child.className === baseNode.className &&
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400544 child.innerHTML === baseNode.innerHTML) {
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400545 messages.removeChild(child)
546 } else {
547 break // A different timestamp is met, we can stop here.
548 }
Sébastien Blin1e2dd352018-04-13 11:34:53 -0400549 }
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400550 }
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400551 }, [])
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400552}
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400553
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400554/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400555 * Update timestamps.
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400556 */
557function updateTimestamps() {
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400558 exec_keeping_scroll_position(function() {
559 const timestamps = messages.getElementsByClassName(".timestamp")
560 const is_image_out = messages.querySelector(".message_out .sender_image")
561 const is_image_in = messages.querySelector(".message_in .sender_image")
562 if (timestamps) {
563 for ( var c = 0 ; c < messages.children.length ; ++c) {
564 const child = messages.children[c]
565 if (child.className.indexOf("timestamp") !== -1) {
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400566 // Update timestamp
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400567 child.innerHTML = getMessageTimestampText(child.getAttribute("message_timestamp"), true)
Sébastien Blin82d0d2d2018-03-14 17:21:42 -0400568
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400569 var desktop_margin = "25%"
570 const height = document.body.clientHeight
571 const width = document.body.clientWidth
572 if (width <= 1920 || height <= 1080) {
573 desktop_margin = "15%"
574 }
575 if (width <= 1000 || height <= 480) {
576 desktop_margin = "0px"
577 }
578 if (child.className.indexOf("timestamp_out") !== -1) {
579 const avatar_px = is_image_out ? (is_image_out.offsetHeight === avatar_size ? "60px" : "20px") : "20px"
580 child.style.paddingRight = `calc(${desktop_margin} + ${avatar_px})`
581 } else if (child.className.indexOf("timestamp_in") !== -1) {
582 const avatar_px = is_image_in ? (is_image_in.offsetHeight === avatar_size ? "60px" : "20px") : "20px"
583 child.style.paddingLeft = `calc(${desktop_margin} + ${avatar_px})`
584 }
585 // Remove previous elements with the same formatted timestamp
586 cleanPreviousTimestamps(child, c)
Adrien Beraud8e25afb2017-04-19 01:38:57 +0200587 }
588 }
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -0500589 }
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400590 }, [])
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400591}
aviau039001d2016-09-29 16:39:05 -0400592
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400593/**
594 * Convert a value in filesize
595 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400596function humanFileSize(bytes) {
597 var thresh = 1024
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400598 if(Math.abs(bytes) < thresh) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400599 return bytes + " B"
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400600 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400601 var units = ["kB","MB","GB","TB","PB","EB","ZB","YB"]
602 var u = -1
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400603 do {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400604 bytes /= thresh
605 ++u
606 } while(Math.abs(bytes) >= thresh && u < units.length - 1)
607 return bytes.toFixed(1)+" "+units[u]
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400608}
609
610/**
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400611 * Change the value of the progress bar.
612 *
613 * @param progress_bar
614 * @param message_object
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400615 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400616function updateProgressBar(progress_bar, message_object) {
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400617 exec_keeping_scroll_position(function() {
618 var delivery_status = message_object["delivery_status"]
619 if ("progress" in message_object && !isErrorStatus(delivery_status) && message_object["progress"] !== 100) {
620 var progress_percent = (100 * message_object["progress"] / message_object["totalSize"])
621 if (progress_percent !== 100)
622 progress_bar.childNodes[0].setAttribute("style", "width: " + progress_percent + "%")
623 else
624 progress_bar.setAttribute("style", "display: none")
625 } else
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400626 progress_bar.setAttribute("style", "display: none")
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400627 }, [])
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400628}
629
630/**
631 * Check if a status is an error status
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400632 * @param
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400633 */
634function isErrorStatus(status) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400635 return (status === "failure"
636 || status === "awaiting peer timeout"
637 || status === "canceled"
638 || status === "unjoinable peer")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400639}
640
641/**
642 * Build a new file interaction
643 * @param message_id
644 */
645function fileInteraction(message_id) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400646 var message_wrapper = document.createElement("div")
647 message_wrapper.setAttribute("class", "message_wrapper")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400648
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400649 /* A file interaction contains buttons at the left for status information
650 or accept/refuse actions. The text is bold and clickable. */
651 var left_buttons = document.createElement("div")
652 left_buttons.setAttribute("class", "left_buttons")
653 message_wrapper.appendChild(left_buttons)
654 var text_div = document.createElement("div")
655 text_div.setAttribute("class", "text")
656
657 text_div.addEventListener("click", function () {
658 // ask ring to open the file
659 const filename = document.querySelector("#message_" + message_id + " .full").innerText
660 window.prompt("OPEN_FILE:" + filename)
661 })
662
663 var full_div = document.createElement("div")
664 full_div.setAttribute("class", "full")
665 full_div.style = "visibility: hidden; display: none;"
666 var message_text = document.createElement("div")
667 message_text.setAttribute("class", "filename")
668
669 // And some informations like size or error message.
670 var informations_div = document.createElement("div")
671 informations_div.setAttribute("class", "informations")
672 text_div.appendChild(message_text)
673 text_div.appendChild(full_div)
674 text_div.appendChild(informations_div)
675 message_wrapper.appendChild(text_div)
676
677 // And finally, a progress bar
678 var message_transfer_progress_bar = document.createElement("span")
679 message_transfer_progress_bar.setAttribute("class", "message_progress_bar")
680 var message_transfer_progress_completion = document.createElement("span")
681 message_transfer_progress_bar.appendChild(message_transfer_progress_completion)
682 message_wrapper.appendChild(message_transfer_progress_bar)
683 return message_wrapper
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400684}
685
686/**
687 * Update a file interaction (icons + filename + status + progress bar)
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400688 *
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400689 * @param message_div the message to update
690 * @param message_object new informations
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400691 * @param forceTypeToFile
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400692 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400693function updateFileInteraction(message_div, message_object, forceTypeToFile = false) {
694 if (!message_div.querySelector(".informations")) return // media
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400695
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400696 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>",
697 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>",
698 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>",
699 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>"
700 var message_delivery_status = message_object["delivery_status"]
701 var message_direction = message_object["direction"]
702 var message_id = message_object["id"]
703 var message_text = message_object["text"]
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400704
705
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400706 if (isImage(message_text) && message_delivery_status === "finished" && displayLinksEnabled && !forceTypeToFile) {
707 // Replace the old wrapper by the downloaded image
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400708 exec_keeping_scroll_position(function() {
709 if (message_div.querySelector(".message_wrapper")) {
710 var wrapper = message_div.querySelector(".message_wrapper")
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400711 wrapper.parentNode.removeChild(wrapper)
712 }
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400713 message_div.append(mediaInteraction(message_id, message_text))
714 message_div.querySelector("img").id = message_id
715 message_div.querySelector("img").msg_obj = message_object
716 message_div.querySelector("img").onerror = function() {
717 message_div = document.getElementById("#message_" + this.id)
718 var wrapper = message_div.querySelector(".message_wrapper")
719 if (wrapper) {
720 wrapper.parentNode.removeChild(wrapper)
721 }
722 message_div.append(fileInteraction(message_id))
723 updateFileInteraction(message_div, this.msg_obj, true)
724 }
725 }, [])
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400726 return
727 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400728
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400729 // Set informations text
730 var informations_div = message_div.querySelector(".informations")
731 var informations_txt = getMessageTimestampText(message_object["timestamp"], true)
732 if (message_object["totalSize"] && message_object["progress"]) {
733 if (message_delivery_status === "finished") {
734 informations_txt += " - " + humanFileSize(message_object["totalSize"])
735 } else {
736 informations_txt += " - " + humanFileSize(message_object["progress"])
737 + " / " + humanFileSize(message_object["totalSize"])
738 }
739 }
740 informations_txt += " - " + message_delivery_status
741 informations_div.innerText = informations_txt
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400742
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400743 // Update flat buttons
744 var left_buttons = message_div.querySelector(".left_buttons")
745 left_buttons.innerHTML = ""
746 if (message_delivery_status === "awaiting peer" || message_delivery_status === "awaiting host" || message_delivery_status.indexOf("ongoing") === 0) {
747 if (message_direction === "in" && message_delivery_status.indexOf("ongoing") !== 0) {
748 // add buttons to accept or refuse a call.
749 var accept_button = document.createElement("div")
750 accept_button.innerHTML = acceptSvg
751 accept_button.setAttribute("title", "Accept")
752 accept_button.setAttribute("class", "flat-button accept")
753 accept_button.onclick = function() {
754 window.prompt("ACCEPT_FILE:" + message_id)
755 }
756 left_buttons.appendChild(accept_button)
757 }
758 var refuse_button = document.createElement("div")
759 refuse_button.innerHTML = refuseSvg
760 refuse_button.setAttribute("title", "Refuse")
761 refuse_button.setAttribute("class", "flat-button refuse")
762 refuse_button.onclick = function() {
763 window.prompt("REFUSE_FILE:" + message_id)
764 }
765 left_buttons.appendChild(refuse_button)
766 } else {
767 var status_button = document.createElement("div")
768 var statusFile = fileSvg
769 if (isErrorStatus(message_delivery_status))
770 statusFile = warningSvg
771 status_button.innerHTML = statusFile
772 status_button.setAttribute("class", "flat-button")
773 left_buttons.appendChild(status_button)
774 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400775
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400776 message_div.querySelector(".full").innerText = message_text
777 message_div.querySelector(".filename").innerText = message_text.split("/").pop()
778 message_div.querySelector(".filename").innerText = message_text.split("/").pop()
779 updateProgressBar(message_div.querySelector(".message_progress_bar"), message_object)
780 updateProgressBar(message_div.querySelector(".message_progress_bar"), message_object)
781}
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400782
783/**
784 * Return if a file is an image
785 * @param file
786 */
787function isImage(file) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400788 return file.toLowerCase().match(/\.(jpeg|jpg|gif|png)$/) !== null
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400789}
790
791/**
792 * Return if a file is a youtube video
793 * @param file
794 */
795function isVideo(file) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400796 const availableProtocols = ["http:", "https:"]
797 const videoHostname = ["youtube.com", "www.youtube.com", "youtu.be"]
798 const urlParser = document.createElement("a")
799 urlParser.href = file
800 return (availableProtocols.includes(urlParser.protocol) && videoHostname.includes(urlParser.hostname))
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400801}
802
803/**
804 * Try to show an image or a video link (youtube for now)
805 * @param message_id
806 * @param link to show
807 * @param ytid if it's a youtube video
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -0400808 * @param noerror
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400809 */
810function mediaInteraction(message_id, link, ytid, noerror) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400811 /* TODO promise?
812 Try to display images. */
813 const media_wrapper = document.createElement("div")
814 media_wrapper.setAttribute("class", "media_wrapper")
815 const linkElt = document.createElement("a")
816 linkElt.href = link
817 linkElt.style = "text-decoration: none; border:none;"
818 const imageElt = document.createElement("img")
819 /* Note, here, we don't check the size of the image.
820 in the future, we can check the content-type and content-length with a request
821 and maybe disable svg */
822 if (noerror)
823 imageElt.setAttribute("onerror", "this.style.display='none'")
824 if (ytid) {
825 imageElt.src = `http://img.youtube.com/vi/${ytid}/0.jpg`
826 } else {
827 imageElt.src = link
828 }
829 linkElt.appendChild(imageElt)
830 if (ytid) {
831 const containerElt = document.createElement("div")
832 containerElt.setAttribute("class", "containerVideo")
833 const playDiv = document.createElement("div")
834 playDiv.setAttribute("class", "playVideo")
835 playDiv.innerHTML = "<svg fill=\"#ffffff\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\
836 <path d=\"M8 5v14l11-7z\"/>\
837 <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\
838 </svg>"
839 linkElt.appendChild(playDiv)
840 containerElt.appendChild(linkElt)
841 media_wrapper.appendChild(containerElt)
842 } else {
843 media_wrapper.appendChild(linkElt)
844 }
845 return media_wrapper
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400846}
847
848/**
849 * Build a new text interaction
850 * @param message_id
851 * @param htmlText the DOM to show
852 */
853function textInteraction(message_id, htmlText) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400854 const message_wrapper = document.createElement("div")
855 message_wrapper.setAttribute("class", "message_wrapper")
856 var message_text = document.createElement("span")
857 message_text.setAttribute("class", "message_text")
858 message_text.innerHTML = htmlText
859 message_wrapper.appendChild(message_text)
860 // TODO STATUS
861 return message_wrapper
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400862}
863
864/**
865 * Update a text interaction (text)
866 * @param message_div the message to update
867 * @param delivery_status the status of the message
868 * @TODO retry button
869 */
870function updateTextInteraction(message_div, delivery_status) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400871 if (!message_div.querySelector(".message_text")) return // media
872 var sending = message_div.querySelector(".sending")
873 switch(delivery_status)
874 {
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400875 case "ongoing":
876 case "sending":
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400877 if (!sending) {
878 sending = document.createElement("div")
879 sending.setAttribute("class", "sending")
880 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>"
881 // add sending animation to message;
882 message_div.appendChild(sending)
883 }
884 message_div.querySelector(".message_text").style = "color: #888"
885 break
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400886 case "failure":
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400887 // change text color to red
888 message_div.querySelector(".message_wrapper").style = "background-color: #f3a6a6"
889 message_div.querySelector(".message_text").style = "color: #000"
890 var failure_div = message_div.querySelector(".failure")
891 if (!failure_div) {
892 failure_div = document.createElement("div")
893 failure_div.setAttribute("class", "failure")
894 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>"
895 // add failure animation to message
896 message_div.appendChild(failure_div)
897 }
898 if (sending) sending.style.display = "none"
899 break
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400900 case "sent":
901 case "finished":
902 case "unknown":
903 case "read":
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400904 // change text color to black
905 message_div.querySelector(".message_text").style = "color: #000"
906 if (sending) sending.style.display = "none"
907 break
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400908 default:
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400909 break
910 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400911}
912
913/**
914 * Build a new interaction (call or contact)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400915 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400916function actionInteraction() {
917 var message_wrapper = document.createElement("div")
918 message_wrapper.setAttribute("class", "message_wrapper")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400919
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400920 // An file interaction contains buttons at the left of the interaction
921 // for the status or accept/refuse
922 var left_buttons = document.createElement("div")
923 left_buttons.setAttribute("class", "left_buttons")
924 message_wrapper.appendChild(left_buttons)
925 // Also contains a bold clickable text
926 var text_div = document.createElement("div")
927 text_div.setAttribute("class", "text")
928 message_wrapper.appendChild(text_div)
929 return message_wrapper
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400930}
931
932/**
933 * Update a call interaction (icon + text)
934 * @param message_div the message to update
935 * @param message_object new informations
936 */
937function updateCallInteraction(message_div, message_object) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400938 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>"
939 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>"
940 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>"
941 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 -0400942
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400943 const message_text = message_object["text"]
944 const message_direction = (message_text.toLowerCase().indexOf("incoming") !== -1) ? "in" : "out"
945 const missed = message_text.indexOf("Missed") !== -1
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400946
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400947 message_div.querySelector(".text").innerText = message_text.substring(2)
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 var statusFile = ""
953 if (missed)
954 statusFile = (message_direction === "in") ? callMissed : outgoingMissed
955 else
956 statusFile = (message_direction === "in") ? callReceived : outgoingCall
957 status_button.innerHTML = statusFile
958 status_button.setAttribute("class", "flat-button")
959 left_buttons.appendChild(status_button)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400960}
961
962/**
963 * Update a contact interaction (icon + text)
964 * @param message_div the message to update
965 * @param message_object new informations
966 */
967function updateContactInteraction(message_div, message_object) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400968 const message_text = message_object["text"]
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400969
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400970 message_div.querySelector(".text").innerText = message_text
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400971
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400972 var left_buttons = message_div.querySelector(".left_buttons")
973 left_buttons.innerHTML = ""
974 var status_button = document.createElement("div")
975 status_button.innerHTML = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\
976<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\"/>\
977<path d=\"M0 0h24v24H0z\" fill=\"none\"/></svg>"
978 status_button.setAttribute("class", "flat-button")
979 left_buttons.appendChild(status_button)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400980}
981
982/**
983 * Add a message to the conversation.
984 * @param message_object to treat
985 * @param new_message if it's a new message or if we need to update
986 * @param insert_after if we want the message at the end or the top of the conversation
987 */
988function addOrUpdateMessage(message_object, new_message, insert_after = true) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400989 const message_id = message_object["id"]
990 const message_type = message_object["type"]
991 const message_text = message_object["text"]
992 const message_direction = message_object["direction"]
993 const message_timestamp = message_object["timestamp"]
994 const delivery_status = message_object["delivery_status"]
995 const message_sender_contact_method = message_object["sender_contact_method"]
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -0400996
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -0400997 var message_div = document.getElementById("message_" + message_id)
998 var type = ""
999 if (new_message) {
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001000 // Build new message
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001001 var classes = [
1002 "message",
1003 `message_${message_direction}`,
1004 `message_type_${message_type}`
1005 ]
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001006
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001007 message_div = document.createElement("div")
1008 message_div.setAttribute("id", `message_${message_id}`)
1009 message_div.setAttribute("class", classes.join(" "))
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001010
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001011 // Build message for each types.
1012 // Add sender images if necessary (like if the interaction doesn't take the whole width)
1013 const need_sender = (message_type === "data_transfer" || message_type === "text")
1014 if (need_sender) {
1015 var message_sender_image = document.createElement("span")
1016 message_sender_image.setAttribute("class", `sender_image sender_image_${message_sender_contact_method}`)
1017 message_div.appendChild(message_sender_image)
Frédéric Guimont3e5f1b62016-11-22 11:41:38 -05001018 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001019
1020 // Build main content
1021 if (message_type === "data_transfer") {
1022 if (isImage(message_text) && delivery_status === "finished" && displayLinksEnabled) {
1023 message_div.append(mediaInteraction(message_id, message_text, null, false))
1024 message_div.querySelector("img").id = message_id
1025 message_div.querySelector("img").msg_obj = message_object
1026 message_div.querySelector("img").onerror = function() {
1027 message_div = document.getElementById("#message_" + this.id)
1028 /* FIXME in many cases, message_div is null here and this code doesn't do anything */
1029 if (message_div.querySelector(".message_wrapper")) {
1030 var wrapper = message_div.querySelector(".message_wrapper")
1031 wrapper.parentNode.removeChild(wrapper)
1032 }
1033 message_div.append(fileInteraction(message_id))
1034 updateFileInteraction(message_div, this.msg_obj, true)
1035 }
1036 } else {
1037 message_div.append(fileInteraction(message_id))
1038 }
1039 } else if (message_type === "text") {
1040 // TODO add the possibility to update messages (remove and rebuild)
1041 const htmlText = getMessageHtml(message_text)
1042 if (displayLinksEnabled) {
1043 const parser = new DOMParser()
1044 const DOMMsg = parser.parseFromString(htmlText, "text/xml")
1045 const links = DOMMsg.querySelectorAll("a")
1046 if (DOMMsg.childNodes.length && links.length) {
1047 var isTextToShow = (DOMMsg.childNodes[0].childNodes.length > 1)
1048 const ytid = (isVideo(message_text))? youtube_id(message_text) : ""
1049 if (!isTextToShow && (ytid || isImage(message_text))) {
1050 type = "media"
1051 message_div.append(mediaInteraction(message_id, message_text, ytid))
1052 }
1053 }
1054 }
1055 if (type !== "media") {
1056 type = "text"
1057 message_div.append(textInteraction(message_id, htmlText))
1058 }
1059 } else if (message_type === "call" || message_type === "contact") {
1060 message_div.append(actionInteraction())
1061 } else {
1062 const temp = document.createElement("div")
1063 temp.innerText = message_type
1064 message_div.appendChild(temp)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001065 }
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001066
1067 // Get timestamp to add
1068 const formattedTimestamp = getMessageTimestampText(message_timestamp, true)
1069 // Create the timestamp object
1070 const date_elt = document.createElement("div")
1071 date_elt.innerText = formattedTimestamp
1072 var typeIsCallOrContact = (message_type === "call" || message_type === "contact")
1073 var timestamp_div_classes = ["timestamp", typeIsCallOrContact ? "timestamp_action" : `timestamp_${message_direction}`]
1074 date_elt.setAttribute("class", timestamp_div_classes.join(" "))
1075 date_elt.setAttribute("message_timestamp", message_timestamp)
1076
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -04001077 // Remove last timestamp if it's the same
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001078 if (messages.getElementsByClassName(".timestamp"))
1079 cleanPreviousTimestamps(date_elt, messages.children.length)
1080
1081 // Add message and the new timestamp
1082 if (insert_after) {
1083 if (message_type === "call" || message_type === "contact") {
1084 message_div.querySelector(".message_wrapper").appendChild(date_elt)
1085 messages.appendChild(message_div)
1086 } else {
1087 messages.appendChild(message_div)
1088 messages.appendChild(date_elt)
1089 }
1090 } else {
1091 if (message_type === "call" || message_type === "contact") {
1092 message_div.querySelector(".message_wrapper").appendChild(date_elt)
1093 messages.insertBefore(message_div, messages.firstChild)
1094 } else {
1095 messages.insertBefore(date_elt, messages.firstChild)
1096 messages.insertBefore(message_div, messages.firstChild)
1097 }
1098 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001099 }
1100
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001101 // Update informations if needed
1102 if (message_type === "data_transfer")
1103 updateFileInteraction(message_div, message_object)
1104 if (message_type === "text" && message_direction === "out")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001105 // Modify sent status if necessary
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001106 updateTextInteraction(message_div, delivery_status)
1107 if (message_type === "call")
1108 updateCallInteraction(message_div, message_object)
1109 if (message_type === "contact")
1110 updateContactInteraction(message_div, message_object)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001111
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001112 // Clean timestamps
1113 updateTimestamps()
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001114}
1115
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001116/**
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -04001117 * Wrapper for addOrUpdateMessage.
1118 *
1119 * This function adds or updates a message and makes sure the scrollbar position
1120 * is refreshed correctly.
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001121 *
1122 * @param message_object message to be added
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001123 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001124/* exported addMessage */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001125function addMessage(message_object)
1126{
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -04001127 exec_keeping_scroll_position(addOrUpdateMessage, [message_object, true])
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001128}
1129
1130/**
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -04001131 * Update a message that was previously added with addMessage
1132 * @param message_object message to be updated
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001133 */
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -04001134/* exported updateMessage */
1135function updateMessage(message_object)
1136{
1137 exec_keeping_scroll_position(addOrUpdateMessage, [message_object, false])
1138}
1139
1140/**
1141 * This function displays the history in reverse order and makes sure the
1142 * scrollbar position is refreshed correctly.
1143 */
1144function printHistoryPart()
1145{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001146 if(historyBuffer.length == 0 || historyBufferIndex === historyBuffer.length) {
1147 // nothing to print
1148 return
1149 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001150
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -04001151 exec_keeping_scroll_position(function() {
1152 // Show 10 messages
1153 for (var i = 0; i < 10; ++i) {
1154 addOrUpdateMessage(historyBuffer[historyBuffer.length - 1 - historyBufferIndex], true, false)
1155 historyBufferIndex ++
Hugo Lefeuvreedad8832018-05-14 16:36:06 -04001156
Hugo Lefeuvre2722ab92018-05-31 17:43:06 -04001157 if (historyBufferIndex === historyBuffer.length)
1158 break
1159 }
1160 }, [])
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001161
1162 // Make a short pause to minimize ressources consumption
1163 // show quickly the first hundred messages
1164 if (historyBufferIndex !== historyBuffer.length)
1165 setTimeout(printHistoryPart, (historyBufferIndex > 100) ? 100 : 1)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001166}
1167
1168/**
1169 * Show the whole history in the chatview.
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001170 * @param messages_array array containing history to be printed
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001171 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001172/* exported printHistory */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001173function printHistory(messages_array)
1174{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001175 historyBuffer = messages_array
1176 historyBufferIndex = 0
1177 setTimeout(printHistoryPart, 0)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001178}
1179
1180/**
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001181 * Sets the image for a given sender
1182 * set_sender_image object should contain the following keys:
1183 * - sender: the name of the sender
1184 * - sender_image: base64 png encoding of the sender image
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001185 *
1186 * @param set_sender_image_object sender image object as previously described
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001187 */
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001188/* exported setSenderImage */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001189function setSenderImage(set_sender_image_object)
1190{
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001191 var sender_contact_method = set_sender_image_object["sender_contact_method"],
1192 sender_image = set_sender_image_object["sender_image"],
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001193 sender_image_id = "sender_image_" + sender_contact_method,
1194 currentSenderImage = document.getElementById("#" + sender_image_id), // Remove the currently set sender image
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001195 style
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001196
1197 if (currentSenderImage) {
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001198 currentSenderImage.parentNode.removeChild(currentSenderImage)
Guillaume Roguez5b137be2018-02-21 10:44:58 -05001199 }
1200
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001201 // Create a new style element
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001202 style = document.createElement("style")
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001203
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001204 style.type = "text/css"
1205 style.id = sender_image_id
1206 style.innerHTML = "." + sender_image_id + " {content: url(data:image/png;base64," + sender_image + ");height: 35px;width: 35px;}"
1207 document.head.appendChild(style)
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001208}
1209
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001210/* exported clearSenderImages */
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001211function clearSenderImages()
1212{
1213 var styles = document.head.querySelectorAll("style"),
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001214 i = styles.length
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001215
1216 while (i--){
Hugo Lefeuvre368c47c2018-05-29 15:50:07 -04001217 document.head.removeChild(styles[i])
aviau039001d2016-09-29 16:39:05 -04001218 }
Hugo Lefeuvre65a41d62018-05-23 10:12:24 -04001219}
aviau039001d2016-09-29 16:39:05 -04001220
aviau039001d2016-09-29 16:39:05 -04001221</script>
aviau039001d2016-09-29 16:39:05 -04001222</html>