diff --git a/web/chatview.css b/web/chatview.css index 5a9cff68b7413c524564b947e51efe180e94684e..2e9fb6b8e48aa3d5448081f18da2d2e62bc2db2f 100644 --- a/web/chatview.css +++ b/web/chatview.css @@ -3,6 +3,54 @@ body { overflow: hidden; } +a:link { + text-decoration: none; + color: #0e649b; + transition: all 0.2s ease-in-out; + border-bottom: dotted 1px; +} + +a:hover { + border: 0; +} + +.message_out #cardWrapper { + display: inline-block; + text-align: right; + width: 100%; +} + +.message_out #cardWrapper a { + border: 0; +} + +.message_out #containerVideo { + float: right; +} + +#playVideo { + background-color: rgba(0, 0, 0, 0.4); + height: 50px; + width: 50px; + border-radius: 5px; + float: right; + position: absolute; + top: calc(50% - 25px); + left: calc(50% - 25px); + z-index: 3; +} + +#containerVideo { + max-width: 350px; + position: relative; +} + +#playVideo svg { + height: 40px; + width: 40px; + margin: 5px; +} + #container { position: relative; height: 100%; @@ -173,7 +221,7 @@ input[placeholder], [placeholder], *[placeholder] { .message_wrapper { transform: scale(0); - max-width: 50%; + max-width: 70%; margin: 10px 0; display: inline-block; padding: 10px; @@ -195,6 +243,15 @@ input[placeholder], [placeholder], *[placeholder] { overflow: hidden; } +#msg_content img { + max-width: 300px; + align-content: center; +} + +#msg_content img:first-of-type { + margin: 5px; +} + pre { font : inherit; font-family : inherit; diff --git a/web/chatview.html b/web/chatview.html index 649002048c1e4ffb4f023ae6902ffbc2356c5c0b..6cc74df75509175230d2d738533f4ed349e72e8e 100644 --- a/web/chatview.html +++ b/web/chatview.html @@ -135,18 +135,98 @@ ring.chatview = (function(){ return div.innerHTML; } + /** - * Returns HTML message from the message text. - * Cleaned and linkified. + * Get the youtube video id from a URI */ - function getMessageHtml(message_text) - { - var escaped_message = escapeHtml(message_text), - linkified_message = linkifyHtml(escaped_message, {}); + function youtube_id(url) { + const regExp = /^.*(youtu\.be\/|v\/|\/u\/w|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/; + const match = url.match(regExp); + return (match && match[2].length == 11) ? match[2] : null; + } + - return "<pre>" + linkified_message + "</pre>"; + /** + * Show images/videos from links in a message. + */ + function displayLinks(messageNode) + { + const finalMsg = document.createElement('div'); + finalMsg.setAttribute('id', 'msg_content'); + finalMsg.innerHTML = messageNode.outerHTML; + + const parser = new DOMParser(); + const DOMMsg = parser.parseFromString(messageNode.innerHTML, 'text/xml'); + const links = DOMMsg.querySelectorAll('a'); + + const availableProtocols = ['http:', 'https:']; + const videoHostname = ['youtube.com', 'www.youtube.com', 'youtu.be']; + + const cardElt = document.createElement('div'); + cardElt.setAttribute('id', 'cardWrapper'); + + if (links.length) { + const link = links[0].getAttribute('href'); + const urlParser = document.createElement('a'); + urlParser.href = link; + + // Parse videos + if (availableProtocols.includes(urlParser.protocol) && videoHostname.includes(urlParser.hostname)) { + // Youtube + const ytid = youtube_id(link) + if (ytid) { + const linkElt = document.createElement('a'); + linkElt.href = link; + const containerElt = document.createElement('div'); + containerElt.setAttribute('id', 'containerVideo'); + const imageElt = document.createElement('img'); + imageElt.src = `http://img.youtube.com/vi/${ytid}/0.jpg`; + const playDiv = document.createElement('div'); + playDiv.setAttribute('id', 'playVideo'); + playDiv.innerHTML = '<svg fill="#ffffff" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">\ + <path d="M8 5v14l11-7z"/>\ + <path d="M0 0h24v24H0z" fill="none"/>\ + </svg>' + linkElt.appendChild(imageElt); + linkElt.appendChild(playDiv); + containerElt.appendChild(linkElt); + cardElt.appendChild(containerElt); + finalMsg.appendChild(cardElt); + messageNode.outerHTML = finalMsg.outerHTML; + } + } else { + // Try to display images. + const linkElt = document.createElement('a'); + linkElt.href = link; + const imageElt = document.createElement('img'); + // Note, here, we don't check the size of the image. + // in the future, we can check the content-type and content-length with a request + // and maybe disable svg + imageElt.setAttribute("onerror", "this.style.display='none'"); + imageElt.src = link; + linkElt.appendChild(imageElt); + cardElt.appendChild(linkElt); + finalMsg.appendChild(cardElt); + messageNode.outerHTML = finalMsg.outerHTML; + } + } } + /** + * Returns HTML message from the message text. + * Cleaned and linkified. + */ + function getMessageHtml(message_text) + { + const escaped_message = escapeHtml(message_text), + linkified_message = linkifyHtml(escaped_message, {}); + + const textPart = document.createElement('pre'); + textPart.innerHTML = linkified_message; + + return textPart.outerHTML; + } + /** * Returns the message status, formatted for display */ @@ -363,6 +443,8 @@ ring.chatview = (function(){ // Set the variables chatview_message_text.innerHTML = getMessageHtml(message_text); + if (new_message) + displayLinks(chatview_message_text); chatview_message_sender.textContent = message_sender + ": "; chatview_message_delivery_status.innerHTML = getMessageDeliveryStatusText(message_delivery_status); chatview_message_timestamp.textContent = getMessageTimestampText(message_timestamp);