/* jshint sub: true */ /** * @constructor * @extends {RoomHistory} * @param {Room|string} room or roomId * @param {number} keepMessages number of messages to keep in memory * @param {Array|undefined} evts * @param {number|undefined} now **/ function UiRoomHistory(room, keepMessages, evts, now) { RoomHistory.call(this, room, keepMessages, 0, evts, now); } UiRoomHistory.prototype = Object.create(RoomHistory.prototype); UiRoomHistory.prototype.constructor = UiRoomHistory; UiRoomHistory.prototype.messageFactory = function(ev, ts) { if (ev["isMeMessage"] === true) return new UiMeMessage(this.id, ev, ts); if (ev["isNotice"] === true) return new UiNoticeMessage(this.id, ev, ts); return new UiMessage(this.id, ev, ts); }; UiRoomHistory.prototype.invalidateAllMessages = function() { this.messages.forEach(function(m) { m.invalidate(); }); }; /** @interface */ function IUiMessage() {} /** * @return {Element} **/ IUiMessage.prototype.getDom = function() {}; /** * @return {IUiMessage} **/ IUiMessage.prototype.removeDom = function() {}; /** * @return {IUiMessage} **/ IUiMessage.prototype.invalidate = function() {}; /** * @return {IUiMessage} **/ IUiMessage.prototype.createDom = function() {}; /** * @return {IUiMessage} **/ IUiMessage.prototype.updateDom = function() {}; /** * @return {Element} **/ IUiMessage.prototype.duplicateDom = function() {}; /** @const */ var AbstractUiMessage = (function() { var updateReactions = function(_this, channelId) { var reactionFrag = document.createDocumentFragment(); if (_this.reactions) { for (var reaction in _this.reactions) { var reac = createReactionDom(channelId, _this.id, reaction, _this.reactions[reaction]); if (reac) reactionFrag.appendChild(reac); } } _this.dom.reactions.textContent = ''; _this.dom.reactions.appendChild(reactionFrag); }, _linkFilter = function(msgContext, str) { var sep = str.indexOf('|'), link, text, style, classes, isInternal = false; if (sep === -1) { link = str; } else { link = str.substr(0, sep); text = str.substr(sep +1); } var newLink; if (link[0] === '@') { newLink = msgContext.context.getId() +'|' +link.substr(1); var user = DATA.context.getUser(newLink); if (user) { isInternal = true; link = '#' +user.privateRoom.id; text = '@' +user.getName(); style = "background-color:" +makeUserColor(user.getName()); classes = [ R.klass.msg.linkuser, R.klass.msg.link ]; } else { return null; } } else if (link[0] === '#') { newLink = msgContext.context.getId() +'|' +link.substr(1); var chan = DATA.context.getChannel(newLink); if (chan) { isInternal = true; link = '#' +newLink; text = '#' +chan.name; classes = [ R.klass.msg.linkchan, R.klass.msg.link ]; } else { return null; } } else { if (!link.match(/^(https?|mailto):\/\//i)) return null; classes = [ R.klass.msg.link ]; isInternal = false; } return { link: link, text: text || link, style: style, classes: classes, isInternal: isInternal }; }, _formatText = function(_this, text) { return formatText(text, { highlights: _this.context.self.prefs.highlights, emojiFormatFunction: function(emoji) { if (emoji[0] === ':' && emoji[emoji.length -1] === ':') emoji = emoji.substr(1, emoji.length -2); var emojiDom = makeEmojiDom(emoji); if (emojiDom) { var domParent = document.createElement("span"); domParent.className = R.klass.emoji.small; domParent.appendChild(emojiDom); return domParent.outerHTML; } return null; }, linkFilter: function(link) { return _linkFilter(_this, link); } }); }, updateAttachments = function(_this, channelId) { var attachmentFrag = document.createDocumentFragment(), containImages = false, hasAttachment = false; for (var i =0, nbAttachments = _this.attachments.length; i < nbAttachments; i++) { var attachment = _this.attachments[i]; if (attachment) { var domAttachment = createAttachmentDom(channelId, _this, attachment, i); if (domAttachment) { attachmentFrag.appendChild(domAttachment); containImages |= !!attachment["image_url"]; hasAttachment = true; } } } _this.dom.attachments.textContent = ''; _this.dom.attachments.appendChild(attachmentFrag); if (hasAttachment) { _this.dom.attachmentWrapper.classList.remove(R.klass.hidden); if (CONFIG.shouldExpandAttachment(containImages)) _this.dom.attachmentWrapper["open"] = true; else _this.dom.attachmentWrapper["open"] = false; } else { _this.dom.attachmentWrapper.classList.add(R.klass.hidden); } }, updateHover = function(_this, channelId) { if (_this.dom.hover.hoverStar) _this.dom.hover.hoverStar.style.backgroundImage = _this.starred ? 'url("star_full.png")' : 'url("star_empty.png")'; }, updateCommon = function(_this, sender) { _this.dom.ts.innerHTML = locale.formatDate(_this.ts); _this.dom.textDom.innerHTML = _formatText(_this, _this.text); _this.dom.authorName.textContent = sender ? sender.getName() : (_this.username || "?"); }; return { /** @type {Element|null} * dom: null, /** @type {boolean} * uiNeedRefresh: true, */ /** @param {IUiMessage} _this */ invalidate: function(_this) { _this.uiNeedRefresh = true; return _this; }, /** @param {UiMessage|UiMeMessage|UiNoticeMessage} _this */ removeDom: function(_this) { if (_this.dom && _this.dom.parentElement) { _this.dom.remove(); delete(_this.dom); } return _this; }, /** @param {UiMessage|UiMeMessage|UiNoticeMessage} _this */ getDom: function(_this) { if (!_this.dom) { _this.createDom().updateDom(); } else if (_this.uiNeedRefresh) { _this.uiNeedRefresh = false; _this.updateDom(); } return _this.dom; }, /** @param {UiMessage|UiMeMessage|UiNoticeMessage} _this */ updateDom: function(_this) { var sender = DATA.context.getUser(_this.userId); updateCommon(_this, sender); updateAttachments(_this, _this.channelId); updateReactions(_this, _this.channelId); updateHover(_this, _this.channelId); if (_this.edited) { _this.dom.edited.innerHTML = locale.edited(_this.edited); _this.dom.classList.add(R.klass.msg.editedStatus); } return _this; }, duplicateDom: function(_this) { return _this.getDom().cloneNode(true); }, formatText: function(_this, text) { return _formatText(_this, text); } }; })(); /** * @constructor * @implements {IUiMessage} * @extends {MeMessage} * @param {*} ev * @param {number} ts **/ function UiMeMessage(channelId, ev, ts) { // Extends AbstractUiMessage and MeMessage MeMessage.call(this, ev, ts); /** @const @type {ChatContext} */ this.context = DATA.context.getChannelContext(channelId).getChatContext(); /** @type {string} @const */ this.channelId = channelId; this.dom = AbstractUiMessage.dom; this.uiNeedRefresh = AbstractUiMessage.uiNeedRefresh; } UiMeMessage.prototype = Object.create(MeMessage.prototype); UiMeMessage.prototype.constructor = UiMeMessage; UiMeMessage.prototype.invalidate = function() { return AbstractUiMessage.invalidate(this); }; UiMeMessage.prototype.formatText = function(text) { return AbstractUiMessage.formatText(this, text); }; UiMeMessage.prototype.removeDom = function() { return AbstractUiMessage.removeDom(this); }; UiMeMessage.prototype.getDom = function() { return AbstractUiMessage.getDom(this); }; UiMeMessage.prototype.createDom = function() { this.dom = doCreateMessageDom(this); this.dom.classList.add(R.klass.msg.meMessage); return this; }; UiMeMessage.prototype.duplicateDom = function() { return AbstractUiMessage.duplicateDom(this); }; UiMeMessage.prototype.updateDom = function() { AbstractUiMessage.updateDom(this); return this; }; UiMeMessage.prototype.update = function(ev, ts) { MeMessage.prototype.update.call(this, ev, ts); this.invalidate(); }; /** * @constructor * @implements {IUiMessage} * @extends {Message} * @param {*} ev * @param {number} ts **/ function UiMessage(channelId, ev, ts) { // Extends AbstractUiMessage and Message Message.call(this, ev, ts); /** @const @type {ChatContext} */ this.context = DATA.context.getChannelContext(channelId).getChatContext(); /** @type {string} @const */ this.channelId = channelId; /** @type {Element} */ this.dom = AbstractUiMessage.dom; /** @type {boolean} */ this.uiNeedRefresh = AbstractUiMessage.uiNeedRefresh; } UiMessage.prototype = Object.create(Message.prototype); UiMessage.prototype.constructor = UiMessage; UiMessage.prototype.invalidate = function() { return AbstractUiMessage.invalidate(this); }; UiMessage.prototype.formatText = function(text) { return AbstractUiMessage.formatText(this, text); }; UiMessage.prototype.removeDom = function() { return AbstractUiMessage.removeDom(this); }; UiMessage.prototype.getDom = function() { return AbstractUiMessage.getDom(this); }; UiMessage.prototype.createDom = function() { this.dom = doCreateMessageDom(this); return this; }; UiMessage.prototype.duplicateDom = function() { return AbstractUiMessage.duplicateDom(this); }; UiMessage.prototype.updateDom = function() { AbstractUiMessage.updateDom(this); return this; }; UiMessage.prototype.update = function(ev, ts) { Message.prototype.update.call(this, ev, ts); this.invalidate(); var match = this.text.match(/^