var /** @type {SlackMessage|null} */ REPLYING_TO = null ; /** * @param {SlackChan|SlackGroup} chan * @return {Element} **/ function createChanListItem(chan) { var dom = document.createElement("li"); dom.id = chan.id; if (chan.id[0] === 'D') dom.className = R.klass.chatList.entry + " " +R.klass.chatList.typeDirect; else if (chan.id[0] === 'G') dom.className = R.klass.chatList.entry + " " +R.klass.chatList.typeGroup; else if (chan.id[0] === 'C') dom.className = R.klass.chatList.entry + " " +R.klass.chatList.typeChannel; dom.textContent = chan.name; return dom; } /** * @param {SlackIms} ims * @return {Element} **/ function createImsListItem(ims) { var dom = document.createElement("li"); dom.id = ims.id; dom.className = R.klass.chatList.entry; dom.textContent = ims.user.name; return dom; } function onContextUpdated() { var chanListFram = document.createDocumentFragment(); var sortedChans = SLACK.context.self ? Object.keys(SLACK.context.self.channels) : []; sortedChans.sort(function(a, b) { if (a[0] !== b[0]) { return a[0] - b[0]; } return (SLACK.context.channels[a] || SLACK.context.groups[a]).name.localeCompare((SLACK.context.channels[b] || SLACK.context.groups[b]).name); }); sortedChans.forEach(function(chanId) { var chan = SLACK.context.channels[chanId] || SLACK.context.groups[chanId] ,chanListItem = createChanListItem(chan); if (chanListItem) { chanListFram.appendChild(chanListItem); } }); var sortedUsers = SLACK.context.users ? Object.keys(SLACK.context.users) : []; sortedUsers.sort(function(a, b) { return SLACK.context.users[a].name.localeCompare(SLACK.context.users[b].name); }); sortedUsers.forEach(function(userId) { var ims = SLACK.context.users[userId].ims ,imsListItem = createImsListItem(ims); if (imsListItem) { chanListFram.appendChild(imsListItem); } }); document.getElementById(R.id.chanList).textContent = ""; document.getElementById(R.id.chanList).appendChild(chanListFram); } function onNetworkStateUpdated(isNetworkWorking) { isNetworkWorking ? document.body.classList.remove(R.klass.noNetwork) : document.body.classList.add(R.klass.noNetwork); } function onRoomSelected() { var name = SELECTED_ROOM.name || (SELECTED_ROOM.user ? SELECTED_ROOM.user.name : undefined); if (!name) { var members = []; for (var i in SELECTED_ROOM.members) { members.push(SELECTED_ROOM.members[i].name); } name = members.join(", "); } var roomLi = document.getElementById(SELECTED_ROOM.id); document.getElementById(R.id.currentRoom.title).textContent = name; onRoomUpdated(); focusInput(); document.getElementById(R.id.message.file.formContainer).classList.add(R.klass.hidden); markRoomAsRead(SELECTED_ROOM); if (REPLYING_TO) { REPLYING_TO = null; onReplyingToUpdated(); } } function onReplyingToUpdated() { if (REPLYING_TO) { document.body.classList.add(R.klass.replyingTo); var domParent = document.getElementById(R.id.message.replyTo) ,closeLink = document.createElement("a"); closeLink.addEventListener("click", function() { REPLYING_TO = null; onReplyingToUpdated(); }); closeLink.className = R.klass.msg.replyTo.close; closeLink.textContent = 'x'; domParent.textContent = ""; domParent.appendChild(closeLink); domParent.appendChild(createMessageDom("reply_" +SELECTED_ROOM.id, REPLYING_TO)); } else { document.body.classList.remove(R.klass.replyingTo); } } /** * @param {string} channelId * @param {SlackMessage} msg * @return {Element} **/ function doCreateMessageDom(channelId, msg) { var dom = document.createElement("div") ,ts = document.createElement("div") ,text = document.createElement("div") ,author = document.createElement("div") ,authorImg = document.createElement("img") ,authorName = document.createElement("span") ,hover = document.createElement("ul") ,hoverReply = document.createElement("li") ,sender = msg.raw["user"] ? SLACK.context.users[msg.raw["user"]] : SLACK.context.bots[msg.raw["bot_id"]]; dom.id = channelId +"_" +msg.ts; dom.className = R.klass.msg.item; ts.className = R.klass.msg.ts; text.className = R.klass.msg.msg; author.className = R.klass.msg.author; authorImg.className = R.klass.msg.authorAvatar; authorName.className = R.klass.msg.authorname; hover.className = R.klass.msg.hover.container; hoverReply.className = R.klass.msg.hover.reply; ts.textContent = (new Date(msg.ts * 1000)).toLocaleTimeString(); var msgContents = msg.raw["text"] || ""; msgContents = msgContents .replace(new RegExp('<([@#]?)([^>]*)>', 'g'), function(_, type, entity) { var sub = entity.split('|'); if (type === '@') { if (!sub[1]) { var user = SLACK.context.getMember(sub[0]); sub[1] = user ? ('@' +user.name) : "Unknown member"; // TODO locale } else if ('@' !== sub[1][0]) { sub[1] = '@' +sub[1]; } sub[0] = '#' +sub[0]; sub[2] = R.klass.msg.link +' ' +R.klass.msg.linkuser; } else if (type === '#') { if (!sub[1]) { var chan = SLACK.context.getChannel(sub[0]); sub[1] = chan ? ('#' +chan.name) : "Unknown channel"; // TODO locale } else if ('#' !== sub[1][0]) { sub[1] = '#' +sub[1]; } sub[0] = '#' +sub[0]; sub[2] = R.klass.msg.link +' ' +R.klass.msg.linkchan; } else { if (!sub[1]) sub[1] = sub[0]; sub[2] = R.klass.msg.link; } return '' +sub[1] +''; }) .split(/\r?\n/g); ; for (var msgContentIndex=0, nbMsgContents = msgContents.length; msgContentIndex < nbMsgContents; msgContentIndex++) { var msgContent = msgContents[msgContentIndex] ,_msgContent = "" ,currentMods = {} ,quote = false ,i =0 ,msgLength = msgContent.length; var checkEnd = function(str, pos, c) { while (str[pos]) { if (str[pos] != ' ' && str[pos] != c && str[pos +1] == c) { return true; } pos++; } return false; } ,appendMod = function(mods) { if (!Object.keys(currentMods).length) return ""; return ''; }; // Skip trailing while (i < msgLength && (msgContent[i] === ' ' || msgContent[i] === '\t')) i++; if (msgContent.substr(i, 4) === '>') { quote = true; i += 4; } for (; i < msgLength; i++) { var c = msgContent[i]; if (!(currentMods[R.klass.msg.style.bold]) && c === '*' && msgContent[i +1] && checkEnd(msgContent, i, c)) { if (Object.keys(currentMods).length) _msgContent += ''; currentMods[R.klass.msg.style.bold] = true; _msgContent += appendMod(currentMods); } else if (!(currentMods[R.klass.msg.style.strike]) && c === '~' && msgContent[i +1] && checkEnd(msgContent, i, c)) { if (Object.keys(currentMods).length) _msgContent += ''; currentMods[R.klass.msg.style.strike] = true; _msgContent += appendMod(currentMods); } else if (!(currentMods[R.klass.msg.style.code]) && c === '`' && msgContent[i +1] && checkEnd(msgContent, i, c)) { if (Object.keys(currentMods).length) _msgContent += ''; currentMods[R.klass.msg.style.code] = true; _msgContent += appendMod(currentMods); } else if (!(currentMods[R.klass.msg.style.italic]) && c === '_' && msgContent[i +1] && checkEnd(msgContent, i, c)) { if (Object.keys(currentMods).length) _msgContent += ''; currentMods[R.klass.msg.style.italic] = true; _msgContent += appendMod(currentMods); } else { var finalFound = false; _msgContent += c; do { if ((currentMods[R.klass.msg.style.bold]) && c !== '*' && msgContent[i +1] === '*') { delete currentMods[R.klass.msg.style.bold]; finalFound = true; } else if ((currentMods[R.klass.msg.style.strike]) && c !== '~' && msgContent[i +1] === '~') { delete currentMods[R.klass.msg.style.strike]; finalFound = true; } else if ((currentMods[R.klass.msg.style.code]) && c !== '`' && msgContent[i +1] === '`') { delete currentMods[R.klass.msg.style.code]; finalFound = true; } else if ((currentMods[R.klass.msg.style.italic]) && c !== '_' && msgContent[i +1] === '_') { delete currentMods[R.klass.msg.style.italic]; finalFound = true; } else { break; } c = msgContent[++i]; } while (i < msgLength); if (finalFound) _msgContent += '' +appendMod(currentMods); } } if (currentMods) { // Should not append _msgContent += ''; } if (quote) msgContents[msgContentIndex] = '' +_msgContent +''; else msgContents[msgContentIndex] = _msgContent; } text.innerHTML = msgContents.join('
'); authorName.textContent = sender ? sender.name : (msg.raw["username"] || "?"); if (!sender && !msg.raw["username"]) text.textContent = msg.raw["subtype"] || JSON.stringify(msg.raw); authorImg.src = sender ? sender.icons.image_48 : ""; author.appendChild(authorImg); author.appendChild(authorName); hover.appendChild(hoverReply); dom.appendChild(author); dom.appendChild(text); dom.appendChild(ts); dom.appendChild(hover); return dom; } /** * @param {string} channelId * @param {SlackMessage} msg * @return {Element} **/ function doCreateMeMessageDom(channelId, msg) { var dom = doCreateMessageDom(channelId, msg); dom.classList.add(R.klass.msg.meMessage); return dom; } /** * @param {string} channelId * @param {SlackMessage} msg * @return {Element} **/ function createMessageDom(channelId, msg) { if (msg.subtype === "me_message") { return doCreateMeMessageDom(channelId, msg); } return doCreateMessageDom(channelId, msg); } function updateTitle() { var hasUnread = 0 ,hasHl = 0 ,title; for (var i in UNREAD_CHANS) { if (UNREAD_CHANS.hasOwnProperty(i)) { hasUnread += UNREAD_CHANS[i].unread; hasHl += UNREAD_CHANS[i].hl; } } if (hasHl) { title = "(!" +hasHl; } if (hasUnread) { title = (title ? (title+" - ") : "(") +hasUnread; } if (title) title += ") " +SLACK.context.team.name; else title = SLACK.context.team.name; document.title = title; } function onRoomUpdated() { var chatFrag = document.createDocumentFragment() ,currentRoomId = SELECTED_ROOM.id; document.getElementById(R.id.currentRoom.content).textContent = ""; if (SLACK.history[currentRoomId]) SLACK.history[currentRoomId].messages.forEach(function(msg) { if (msg.type === "message") { var dom = createMessageDom(currentRoomId, msg); chatFrag.appendChild(dom); } }); var content = document.getElementById(R.id.currentRoom.content); content.appendChild(chatFrag); //TODO scroll lock content.scrollTop = content.scrollHeight -content.clientHeight; } function onChanClick(e) { while (e.target !== e.currentTarget && e.target) { if (e.target.classList.contains(R.klass.chatList.entry)) { var room = (SLACK.context.channels[e.target.id] || SLACK.context.ims[e.target.id] || SLACK.context.groups[e.target.id]); if (room && room !== SELECTED_ROOM) { selectRoom(room); } return; } e.target = e.target.parentElement; } } function chatClickDelegate(e) { var target = e.target ,getMessageId = function(e, target) { target = target || e.target; while (target !== e.currentTarget && target) { if (target.classList.contains(R.klass.msg.item)) { return target.id; } target = target.parentElement; } }; while (target !== e.currentTarget && target) { if (target.classList.contains(R.klass.msg.hover.container)) { return; } else if (target.classList.contains(R.klass.msg.hover.reply)) { var messageId = getMessageId(e, target); if (messageId) { messageId = parseFloat(messageId.split("_")[1]); var history = SLACK.history[SELECTED_ROOM.id].messages; for (var i =0, histLen =history.length; i < histLen && history[i].ts <= messageId; i++) { if (history[i].ts === messageId) { if (REPLYING_TO !== history[i]) { REPLYING_TO = history[i]; onReplyingToUpdated(); } return; } } } return; } target = target.parentElement; } } function focusInput() { document.getElementById(R.id.message.input).focus(); } document.addEventListener('DOMContentLoaded', function() { document.getElementById(R.id.chatList).addEventListener("click", onChanClick); document.getElementById(R.id.currentRoom.content).addEventListener("click", chatClickDelegate); document.getElementById(R.id.message.file.cancel).addEventListener("click", function(e) { e.preventDefault(); document.getElementById(R.id.message.file.error).classList.add(R.klass.hidden); document.getElementById(R.id.message.file.formContainer).classList.add(R.klass.hidden); document.getElementById(R.id.message.file.fileInput).value = ""; return false; }); document.getElementById(R.id.message.file.form).addEventListener("submit", function(e) { e.preventDefault(); var fileInput = document.getElementById(R.id.message.file.fileInput) ,filename = fileInput.value; if (filename) { filename = filename.substr(filename.lastIndexOf('\\') +1); uploadFile(SELECTED_ROOM, filename, fileInput.files[0], function(errorMsg) { var error = document.getElementById(R.id.message.file.error); if (errorMsg) { error.textContent = errorMsg; error.classList.remove(R.klass.hidden); } else { error.classList.add(R.klass.hidden); document.getElementById(R.id.message.file.fileInput).value = ""; document.getElementById(R.id.message.file.formContainer).classList.add(R.klass.hidden); } }); } return false; }); document.getElementById(R.id.message.file.bt).addEventListener("click", function(e) { e.preventDefault(); if (SELECTED_ROOM) { document.getElementById(R.id.message.file.formContainer).classList.remove(R.klass.hidden); } return false; }); document.getElementById(R.id.message.form).addEventListener("submit", function(e) { e.preventDefault(); var input =document.getElementById(R.id.message.input); if (SELECTED_ROOM && input.value) { sendMsg(SELECTED_ROOM, input.value, REPLYING_TO); input.value = ""; if (REPLYING_TO) { REPLYING_TO = null; onReplyingToUpdated(); } } focusInput(); return false; }); window.addEventListener('blur', function() { window.hasFocus = false; }); window.addEventListener('focus', function() { window.hasFocus = true; if (SELECTED_ROOM) markRoomAsRead(SELECTED_ROOM); focusInput(); }); window.hasFocus = true; startPolling(); });