var /** * @type {number} next period to wait before next retry in case of failure, in seconds **/ NEXT_RETRY = 0 /** * @type {Room|null} **/ ,SELECTED_ROOM = null /** * @type {SimpleChatSystem|null} **/ ,SELECTED_CONTEXT = null /** @type {Message|null} */ ,REPLYING_TO = null /** @type {Message|null} */ ,EDITING = null ; function initHljs() { document.head.innerHTML += ''; document.body.innerHTML += ''; } /** * @param {Room} room * @param {function(boolean)} cb **/ function fetchHistory(room, cb) { var xhr = new XMLHttpRequest(); xhr.open('GET', 'api/hist?room=' +room.id, true); xhr.send(null); } function poll(callback) { var xhr = new XMLHttpRequest(); xhr.timeout = 1000 * 60 * 1; // 3 min timeout xhr.onreadystatechange = function(e) { if (xhr.readyState === 4) { if (xhr.status === 0) { if (NEXT_RETRY) { NEXT_RETRY = 0; onNetworkStateUpdated(true); } poll(callback); // retry on timeout return; } var resp = null ,success = Math.floor(xhr.status / 100) === 2; if (success) { if (NEXT_RETRY) { NEXT_RETRY = 0; onNetworkStateUpdated(true); } resp = xhr.response; try { resp = JSON.parse(/** @type {string} */ (resp)); } catch (e) { resp = null; } } else { if (NEXT_RETRY) { NEXT_RETRY += Math.floor((NEXT_RETRY || 5)/2); NEXT_RETRY = Math.min(60, NEXT_RETRY); } else { NEXT_RETRY = 5; onNetworkStateUpdated(false); } } callback(success, resp); } }; xhr.open('GET', 'api?v=' +DATA.lastServerVersion, true); xhr.send(null); } function outOfSync() { DATA.lastServerVersion = 0; } /** * @param {Room} room **/ function sendTyping(room) { var xhr = new XMLHttpRequest() ,url = 'api/typing?room=' +room.id; xhr.open('POST', url, true); xhr.send(null); } /** * @param {boolean} success * @param {*} response **/ function onPollResponse(success, response) { if (success) { if (response) { DATA.update(response); } startPolling(); } else { setTimeout(startPolling, NEXT_RETRY * 1000); } } function startPolling() { poll(onPollResponse); } /** * @param {Room} room **/ function selectRoom(room) { if (SELECTED_ROOM) unselectRoom(); document.getElementById("room_" +room.id).classList.add(R.klass.selected); document.body.classList.remove(R.klass.noRoomSelected); SELECTED_ROOM = room; SELECTED_CONTEXT = /** @type {SimpleChatSystem} */ (DATA.context.getChannelContext(room.id)); onRoomSelected(); createContextBackground(SELECTED_CONTEXT.getChatContext().team.id, SELECTED_CONTEXT.getChatContext().users, function(imgData) { document.getElementById(R.id.context).style.backgroundImage = 'url(' +imgData +')'; }); if (SELECTED_ROOM.lastMsg && !DATA.history[SELECTED_ROOM.id]) fetchHistory(SELECTED_ROOM, function(success) {}); } function unselectRoom() { document.getElementById("room_" +SELECTED_ROOM.id).classList.remove(R.klass.selected); } /** * @param {Room} chan * @param {string} filename * @param {File} file * @param {function(string?)} callback **/ function uploadFile(chan, filename, file, callback) { var fileReader = new FileReader() ,formData = new FormData() ,xhr = new XMLHttpRequest(); formData.append("file", file); formData.append("filename", filename); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 204) { callback(null); } else { callback(xhr.statusText); } } } xhr.open('POST', 'api/file?room=' +chan.id); xhr.send(formData); } /** * @param {string} payload * @param {string} serviceId * @param {(function((string|null)))=} callback **/ function sendCommand(payload, serviceId, callback) { var formData = new FormData() ,xhr = new XMLHttpRequest(); formData.append("payload", payload); formData.append("service_id", serviceId); if (callback) { xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 204) { callback(null); } else { callback(xhr.statusText); } } } } xhr.open('POST', "api/attachmentAction"); xhr.send(formData); } function getActionPayload(channelId, msg, attachment, action) { var payload = { "actions": [ action ] ,"attachment_id": attachment["id"] ,"callback_id": attachment["callback_id"] ,"channel_id": channelId ,"is_ephemeral": msg instanceof NoticeMessage ,"message_ts": msg["id"] }; return JSON.stringify(payload); } /** * @param {Room} chan * @param {Command!} cmd * @param {string} args **/ function doCommand(chan, cmd, args) { var xhr = new XMLHttpRequest() ,url = 'api/cmd?room=' +chan.id +"&cmd=" +encodeURIComponent(cmd.name.substr(1)) +"&args=" +encodeURIComponent(args); xhr.open('POST', url, true); xhr.send(null); } /** * @param {Room} chan * @param {string} msg * @param {Message|null=} replyTo **/ function sendMsg(chan, msg, replyTo) { var xhr = new XMLHttpRequest(); var url = 'api/msg?room=' +chan.id +"&text=" +encodeURIComponent(msg); if (replyTo) { var sender = DATA.context.getUser(replyTo.userId) ,footer = "Message"; if (chan.isPrivate) { footer = "Private message"; } else { footer = chan.name; } var attachment = { "fallback": replyTo.text ,"author_name": "<@" +sender.id +"|" +sender.name +">" ,"text": replyTo.text ,"footer": footer ,"ts": replyTo.ts }; url += "&attachments=" +encodeURIComponent(JSON.stringify([attachment])); } xhr.open('POST', url, true); xhr.send(null); } /** * @param {string} input * @param {boolean=} skipCommand * @return {boolean} true on recognized input **/ function onTextEntered(input, skipCommand) { var success = true; if (EDITING) { editMsg(SELECTED_ROOM, input, EDITING); return true; } if (input[0] === '/' && skipCommand !== true) { var endCmd = input.indexOf(' ') ,cmd = input.substr(0, endCmd === -1 ? undefined : endCmd) ,args = endCmd === -1 ? "" : input.substr(endCmd) ,ctx = SELECTED_CONTEXT ,cmdObject = ctx ? ctx.getChatContext().commands.data[cmd] : null; if (cmdObject) { doCommand(SELECTED_ROOM, cmdObject, args.trim()); return true; } return false; } sendMsg(SELECTED_ROOM, input, REPLYING_TO); return true; } /** * @param {Room} chan * @param {string} text * @param {Message|null=} msg **/ function editMsg(chan, text, msg) { var xhr = new XMLHttpRequest(); var url = 'api/msg?room=' +chan.id +"&ts=" +msg.id +"&text=" +encodeURIComponent(text); xhr.open('PUT', url, true); xhr.send(null); } /** * @param {Room} chan * @param {Message|null=} msg **/ function removeMsg(chan, msg) { var xhr = new XMLHttpRequest(); var url = 'api/msg?room=' +chan.id +"&ts=" +msg.id; xhr.open('DELETE', url, true); xhr.send(null); } /** * @param {Room} chan * @param {number} ts **/ function sendReadMArker(chan, ts) { var xhr = new XMLHttpRequest(); var url = 'api/markread?room=' +chan.id +"&ts=" +ts; xhr.open('POST', url, true); xhr.send(null); } /** * @param {string} channelId * @param {string} msgId * @param {string} reaction **/ function addReaction(channelId, msgId, reaction) { var xhr = new XMLHttpRequest(); var url = 'api/reaction?room=' +channelId +"&msg=" +msgId +"&reaction=" +encodeURIComponent(reaction); xhr.open('POST', url, true); xhr.send(null); } /** * @param {string} channelId * @param {string} msgId * @param {string} reaction **/ function removeReaction(channelId, msgId, reaction) { var xhr = new XMLHttpRequest(); var url = 'api/reaction?room=' +channelId +"&msg=" +msgId +"&reaction=" +encodeURIComponent(reaction); xhr.open('DELETE', url, true); xhr.send(null); }