/** * @return {Element!} **/ function createTypingDisplay() { var dom = document.createElement("span") ,dot1 = document.createElement("span") ,dot2 = document.createElement("span") ,dot3 = document.createElement("span"); dom.className = R.klass.typing.container; dot1.className = R.klass.typing.dot1; dot2.className = R.klass.typing.dot2; dot3.className = R.klass.typing.dot3; dot1.textContent = dot2.textContent = dot3.textContent = '.'; dom.appendChild(dot1); dom.appendChild(dot2); dom.appendChild(dot3); return dom; } /** * @param {Room} chan * @param {string|undefined} alternateChanName * @return {Element} **/ function createChanListItem(chan, alternateChanName) { var dom = document.createElement("li") ,link = document.createElement("a"); dom.id = "room_" +chan.id; link.href = '#' +chan.id; if (chan.isPrivate) { dom.className = R.klass.chatList.entry + " " +R.klass.chatList.typePrivate; dom.dataset["count"] = Object.keys((chan.users) || {}).length; } else { dom.className = R.klass.chatList.entry + " " +R.klass.chatList.typeChannel; } if (SELECTED_ROOM === chan) dom.classList.add(R.klass.selected); link.textContent = alternateChanName || chan.name; dom.appendChild(createTypingDisplay()); dom.appendChild(link); if (chan.lastMsg > chan.lastRead) { dom.classList.add(R.klass.unread); if (HIGHLIGHTED_CHANS.indexOf(chan) >= 0) dom.classList.add(R.klass.unreadHi); } return dom; } /** @type {function(string):Element} */ var createChanListHeader = (function() { var cache = {}; return function(title) { var dom = cache[title]; if (!dom) { dom = cache[title] = document.createElement("header"); dom.textContent = title; } return dom; }; })(); /** * @param {PrivateMessageRoom} ims * @param {string|undefined} alternateChanName * @return {Element} **/ function createImsListItem(ims, alternateChanName) { var dom = document.createElement("li") ,link = document.createElement("a"); dom.id = "room_" +ims.id; link.href = '#' +ims.id; dom.className = R.klass.chatList.entry + " " +R.klass.chatList.typeDirect +" " +R.klass.presenceIndicator; link.textContent = alternateChanName || ims.user.getName(); dom.appendChild(createTypingDisplay()); dom.appendChild(link); if (!ims.user.presence) dom.classList.add(R.klass.presenceAway); if (SELECTED_ROOM === ims) dom.classList.add(R.klass.selected); if (ims.lastMsg > ims.lastRead) { dom.classList.add(R.klass.unread); dom.classList.add(R.klass.unreadHi); // Always HI on private message } return dom; } /** * Try to resolve emoji from customized context * @param {string} emoji * @return {Element|string} **/ function tryGetCustomEmoji(emoji) { var loop = {}; if (SELECTED_CONTEXT) { var ctx = SELECTED_CONTEXT.getChatContext(); while (!loop[emoji]) { var emojisrc= ctx.emojis.data[emoji]; if (emojisrc) { if (emojisrc.substr(0, 6) == "alias:") { loop[emoji] = true; emoji = emojisrc.substr(6); } else { var dom = document.createElement("span"); dom.className = R.klass.emoji.custom +' ' +R.klass.emoji.emoji; dom.style.backgroundImage = "url('" +emojisrc +"')"; return dom; } } else { return emoji; // Emoji not found, fallback to std emoji } } } return emoji; //loop detected, return first emoji } function makeEmojiDom(emojiCode) { var emoji = tryGetCustomEmoji(emojiCode); if (typeof emoji === "string" && "makeEmoji" in window) emoji = window['makeEmoji'](emoji); return typeof emoji === "string" ? null : emoji; } /** * @param {string} chanId * @param {string} msgId * @param {string} reaction * @param {Array.} users * @return {Element|null} **/ function createReactionDom(chanId, msgId, reaction, users) { var emojiDom = makeEmojiDom(reaction); if (emojiDom) { var dom = document.createElement("li") ,a = document.createElement("a") ,emojiContainer = document.createElement("span") ,userList = document.createElement("span") ,userNames = []; for (var i =0, nbUser = users.length; i < nbUser; i++) { var user = DATA.context.getUser(users[i]); if (user) userNames.push(user.getName()); } userNames.sort(); userList.textContent = userNames.join(", "); emojiContainer.appendChild(emojiDom); emojiContainer.className = R.klass.emoji.small; a.href = "javascript:toggleReaction('" +chanId +"', '" +msgId +"', '" +reaction +"')"; a.appendChild(emojiContainer); a.appendChild(userList); dom.className = R.klass.msg.reactions.item; dom.appendChild(a); return dom; } else { console.warn("Reaction id not found: " +reaction); } return null; } /** * @param {Element} hover * @param {UiMessage|UiMeMessage|UiNoticeMessage} msg **/ function addHoverButtons(hover, msg) { var capacities = msg.context.getChatContext().capacities, isMe = DATA.context.isMe(msg.userId); if (capacities["replyToMsg"]) { var hoverReply = document.createElement("li"); hoverReply.className = R.klass.msg.hover.reply; hoverReply.style.backgroundImage = 'url("repl.svg")'; hover.appendChild(hoverReply); } if (capacities["reactMsg"]) { var hoverReaction = document.createElement("li"); hoverReaction.className = R.klass.msg.hover.reaction; hoverReaction.style.backgroundImage = 'url("smile.svg")'; hover.appendChild(hoverReaction); } if (isMe && capacities["editMsg"] || capacities["editOtherMsg"]) { var hoverEdit = document.createElement("li"); hoverEdit.className = R.klass.msg.hover.edit; hoverEdit.style.backgroundImage = 'url("edit.svg")'; hover.appendChild(hoverEdit); } if (capacities["starMsg"]) { hover.hoverStar = document.createElement("li") hover.hoverStar.className = R.klass.msg.hover.star; hover.appendChild(hover.hoverStar); } if (capacities["pinMsg"]) { var hoverPin = document.createElement("li") hoverPin.className = R.klass.msg.hover.pin; hover.appendChild(hoverPin); hoverPin.style.backgroundImage = 'url("pin.svg")'; } if (isMe && capacities["removeMsg"] || capacities["moderate"]) { var hoverRemove = document.createElement("li") hoverRemove.className = R.klass.msg.hover.remove; hoverRemove.style.backgroundImage = 'url("remove.svg")'; hover.appendChild(hoverRemove); } } /** * @param {UiMessage|UiMeMessage|UiNoticeMessage} msg * @return {Element} **/ function doCreateMessageDom(msg) { var channelId = msg.channelId; var dom = document.createElement("div") ,msgBlock = document.createElement("div") ,hoverReaction = document.createElement("li"); dom.hover = document.createElement("ul"); dom.attachments = document.createElement("ul"); dom.reactions = document.createElement("ul"); dom.ts = document.createElement("div"); dom.textDom = document.createElement("div"); dom.authorName = document.createElement("span"); dom.id = channelId +"_" +msg.id; dom.className = R.klass.msg.item; dom.ts.className = R.klass.msg.ts; dom.textDom.className = R.klass.msg.msg; dom.authorName.className = R.klass.msg.authorname; addHoverButtons(dom.hover, msg); dom.hover.className = R.klass.msg.hover.container; msgBlock.appendChild(dom.authorName); msgBlock.appendChild(dom.textDom); msgBlock.appendChild(dom.ts); msgBlock.appendChild(dom.attachments); dom.edited = document.createElement("div"); dom.edited.className = R.klass.msg.edited; msgBlock.appendChild(dom.edited); msgBlock.appendChild(dom.reactions); msgBlock.className = R.klass.msg.content; dom.attachments.className = R.klass.msg.attachment.list; dom.reactions.className = R.klass.msg.reactions.container; dom.appendChild(msgBlock); dom.appendChild(dom.hover); return dom; } /** * @param {Chatter} user * @param {string} userName * @return {Element} **/ function createMessageGroupDom(user, userName) { var dom = document.createElement("div") ,authorBlock = document.createElement("div") ,authorName = document.createElement("a") ,authorImg = document.createElement("img"); dom.authorImgWrapper = document.createElement("span") dom.authorImgWrapper.className = R.klass.msg.authorAvatarWrapper; authorImg.className = R.klass.msg.authorAvatar; authorName.className = R.klass.msg.authorname; authorName.href = "#" +user.id; if (user) { authorName.textContent = user.getName(); authorImg.src = user.getSmallIcon(); } else { authorName.textContent = userName || "?"; authorImg.src = ""; } dom.authorImgWrapper.appendChild(authorImg); authorBlock.appendChild(dom.authorImgWrapper); authorBlock.appendChild(authorName); authorBlock.className = R.klass.msg.author; dom.className = R.klass.msg.authorGroup; dom.appendChild(authorBlock); dom.content = document.createElement("div"); dom.content.className = R.klass.msg.authorMessages; dom.appendChild(dom.content); return dom; } /** * @param {string=} colorText * @return {string} **/ function getColor(colorText) { /** @const @type {Object.} */ var colorMap = { "good": "#2fa44f" ,"warning": "#de9e31" ,"danger": "#d50200" }; if (colorText) { if (colorText[0] === '#') return colorText; else if (colorMap[colorText]) return colorMap[colorText]; } return "#e3e4e6"; } /** * @param {string} channelId * @param {UiNoticeMessage|UiMessage} msg * @param {*} attachment * @param {number} attachmentIndex * @return {Element|null} **/ function createAttachmentDom(channelId, msg, attachment, attachmentIndex) { var rootDom = document.createElement("li") ,attachmentBlock = document.createElement("div") ,pretext = document.createElement("div") ,titleBlock = document.createElement("a") ,authorBlock = document.createElement("div") ,authorImg = document.createElement("img") ,authorName = document.createElement("a") ,textBlock = document.createElement("div") ,textDom = document.createElement("div") ,thumbImgDom = document.createElement("div") ,imgDom = document.createElement("img") ,footerBlock = document.createElement("div") ; rootDom.className = R.klass.msg.attachment.container; //Color attachmentBlock.style.borderColor = getColor(attachment["color"] || ""); attachmentBlock.className = R.klass.msg.attachment.block; //Pretext pretext.className = R.klass.msg.attachment.pretext; if (attachment["pretext"]) { pretext.innerHTML = msg.formatText(attachment["pretext"]); } else { pretext.classList.add(R.klass.hidden); } //Title titleBlock.target = "_blank"; if (attachment["title"]) { titleBlock.innerHTML = msg.formatText(attachment["title"]); if (attachment["title_link"]) { titleBlock.href = attachment["title_link"]; } titleBlock.className = R.klass.msg.attachment.title; } else { titleBlock.className = R.klass.hidden + " " +R.klass.msg.attachment.title; } //Author authorName.target = "_blank"; authorBlock.className = R.klass.msg.author; if (attachment["author_name"]) { authorName.innerHTML = msg.formatText(attachment["author_name"]); authorName.href = attachment["author_link"] || ""; authorName.className = R.klass.msg.authorname; authorImg.className = R.klass.msg.authorAvatar; if (attachment["author_icon"]) { authorImg.src = attachment["author_icon"]; authorBlock.appendChild(authorImg); } authorBlock.appendChild(authorName); } // Img (small one) thumbImgDom.className = R.klass.msg.attachment.thumbImg; if (attachment["thumb_url"]) { var img = document.createElement("img"); img.src = attachment["thumb_url"]; thumbImgDom.appendChild(img); attachmentBlock.classList.add(R.klass.msg.attachment.hasThumb); if (attachment["video_html"]) thumbImgDom.dataset["video"] = attachment["video_html"]; } else { thumbImgDom.classList.add(R.klass.hidden); } //Text textBlock.className = R.klass.msg.attachment.content; var textContent = msg.formatText(attachment["text"] || ""); textDom.className = R.klass.msg.attachment.text; if (textContent && textContent != "") { textDom.innerHTML = textContent; } else { textDom.classList.add(R.klass.hidden); } textBlock.appendChild(thumbImgDom); textBlock.appendChild(textDom); if (attachment["geo"]) { var geoTileDom = makeOSMTiles(attachment["geo"]); if (geoTileDom) textBlock.appendChild(geoTileDom); } //Img (the big one) imgDom.className = R.klass.msg.attachment.img; if (attachment["image_url"]) imgDom.src = attachment["image_url"]; else imgDom.classList.add(R.klass.hidden); //Footer footerBlock.className = R.klass.msg.attachment.footer; if (attachment["footer"]) { var footerText = document.createElement("span") footerText.className = R.klass.msg.attachment.footerText; footerText.innerHTML = msg.formatText(attachment["footer"]); if (attachment["footer_icon"]) { var footerIcon = document.createElement("img") footerIcon.src = attachment["footer_icon"]; footerIcon.className = R.klass.msg.attachment.footerIcon; footerBlock.appendChild(footerIcon); } footerBlock.appendChild(footerText); } //Ts if (attachment["ts"]) { var footerTs = document.createElement("span") footerTs.className = R.klass.msg.ts; footerTs.innerHTML = locale.formatDate(attachment["ts"]); footerBlock.appendChild(footerTs); } attachmentBlock.appendChild(titleBlock); attachmentBlock.appendChild(authorBlock); attachmentBlock.appendChild(textBlock); attachmentBlock.appendChild(imgDom); // Fields if (attachment["fields"] && attachment["fields"].length) { var fieldsContainer = document.createElement("ul"); attachmentBlock.appendChild(fieldsContainer); fieldsContainer.className = R.klass.msg.attachment.field.container; attachment["fields"].forEach(function(fieldData) { var fieldDom = createFieldDom(msg, fieldData["title"] || "", fieldData["value"] || "", !!fieldData["short"]); if (fieldDom) { fieldsContainer.appendChild(fieldDom); } }); } // Buttons if (attachment["actions"] && attachment["actions"].length) { var buttons; buttons = document.createElement("ul"); buttons.className = R.klass.msg.attachment.actions +' ' +R.klass.buttonContainer; attachmentBlock.appendChild(buttons); for (var i =0, nbAttachments = attachment["actions"].length; i < nbAttachments; i++) { var action = attachment["actions"][i]; if (action) { var button = createActionButtonDom(attachmentIndex, i, action); if (button) { buttons.appendChild(button); } } } } attachmentBlock.appendChild(footerBlock); rootDom.appendChild(pretext); rootDom.appendChild(attachmentBlock); return rootDom; } /** * @param {UiMessage|UiNoticeMessage} msg * @param {string} title * @param {string} text * @param {boolean} isShort * @return {Element} **/ function createFieldDom(msg, title, text, isShort) { var fieldDom = document.createElement("li") ,titleDom = document.createElement("div") ,textDom = document.createElement("div"); fieldDom.className = R.klass.msg.attachment.field.item; if (!isShort) { fieldDom.classList.add(R.klass.msg.attachment.field.longField); } titleDom.className = R.klass.msg.attachment.field.title; titleDom.textContent = title; textDom.className = R.klass.msg.attachment.field.text; textDom.innerHTML = msg.formatText(text); fieldDom.appendChild(titleDom); fieldDom.appendChild(textDom); return fieldDom; } /** * @param {number} attachmentIndex * @param {number} actionIndex * @param {*} action * @return {Element} **/ function createActionButtonDom(attachmentIndex, actionIndex, action) { var li = document.createElement("li") ,color = getColor(action["style"]); li.textContent = action["text"]; if (color !== getColor()) li.style.color = color; li.style.borderColor = color; li.dataset["attachmentIndex"] = attachmentIndex; li.dataset["actionIndex"] = actionIndex; li.className = R.klass.msg.attachment.actionItem +' ' +R.klass.button; return li; } /** * @param {Chatter} user * @return {Element} **/ function makeUserIsTypingDom(user) { var dom = document.createElement("li") ,userName = document.createElement("span"); userName.textContent = user.getName(); dom.appendChild(createTypingDisplay()); dom.appendChild(userName); return dom; } /** * @param {string} servicename * @return {Element} **/ function createSlashAutocompleteHeader(servicename) { var lh = document.createElement("lh"); lh.textContent = servicename; lh.className = R.klass.commands.header; return lh; } /** * @param {Command|{names: Array!, desc: string!, usage: string!,category: string!, exec: Function!}|string|{name: string, provider: string, emojiSpan: Element}} cmd * @return {Element} **/ function createSlashAutocompleteDom(cmd) { var li = document.createElement("li") ,name = document.createElement("span") name.className = R.klass.commands.name; if (typeof cmd === "string") { name.textContent = cmd; li.appendChild(name); } else if (cmd.emojiSpan) { name.textContent = cmd.name; li.appendChild(cmd.emojiSpan); li.appendChild(name); } else { var usage = document.createElement("span") ,desc = document.createElement("span"); name.textContent = cmd.name; usage.textContent = cmd.usage; desc.textContent = cmd.desc; usage.className = R.klass.commands.usage; desc.className = R.klass.commands.desc; li.appendChild(name); li.appendChild(usage); li.appendChild(desc); } li.dataset.input = name.textContent; li.className = R.klass.commands.item; return li; }