uiMessage.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /**
  2. * @constructor
  3. * @extends {RoomHistory}
  4. * @param {Room|string} room or roomId
  5. * @param {number} keepMessages number of messages to keep in memory
  6. * @param {Array|undefined} evts
  7. * @param {number|undefined} now
  8. **/
  9. function UiRoomHistory(room, keepMessages, evts, now) {
  10. RoomHistory.call(this, room, keepMessages, evts, now);
  11. }
  12. UiRoomHistory.prototype = Object.create(RoomHistory.prototype);
  13. UiRoomHistory.prototype.constructor = UiRoomHistory;
  14. UiRoomHistory.prototype.messageFactory = function(ev, ts) {
  15. if (ev["isMeMessage"] === true)
  16. return new UiMeMessage(this.id, ev, ts);
  17. if (ev["isNotice"] === true)
  18. return new UiNoticeMessage(this.id, ev, ts);
  19. return new UiMessage(this.id, ev, ts);
  20. }
  21. /** @interface */
  22. function IUiMessage() {};
  23. /**
  24. * @return {Element}
  25. **/
  26. IUiMessage.prototype.getDom = function() {};
  27. /**
  28. * @return {IUiMessage}
  29. **/
  30. IUiMessage.prototype.removeDom = function() {};
  31. /**
  32. * @return {IUiMessage}
  33. **/
  34. IUiMessage.prototype.invalidate = function() {};
  35. /**
  36. * @return {IUiMessage}
  37. **/
  38. IUiMessage.prototype.createDom = function() {};
  39. /**
  40. * @return {IUiMessage}
  41. **/
  42. IUiMessage.prototype.updateDom = function() {};
  43. /**
  44. * @return {Element}
  45. **/
  46. IUiMessage.prototype.duplicateDom = function() {};
  47. /** @const */
  48. var AbstractUiMessage = (function() {
  49. var updateReactions = function(_this, channelId) {
  50. var reactionFrag = document.createDocumentFragment();
  51. if (_this.reactions) {
  52. for (var reaction in _this.reactions) {
  53. var reac = createReactionDom(channelId, _this.id, reaction, _this.reactions[reaction]);
  54. if (reac)
  55. reactionFrag.appendChild(reac);
  56. }
  57. }
  58. _this.dom.reactions.textContent = '';
  59. _this.dom.reactions.appendChild(reactionFrag);
  60. },
  61. linkFilter = function(str) {
  62. var sep = str.indexOf('|')
  63. ,link
  64. ,text
  65. ,isInternal;
  66. if (sep === -1) {
  67. link = str;
  68. } else {
  69. link = str.substr(0, sep);
  70. text = str.substr(sep +1);
  71. }
  72. if (link[0] === '@') {
  73. isInternal = true;
  74. // FIXME FORCE get user (translate id into mimou-chat-id)
  75. } else if (link[0] === '#') {
  76. isInternal = true;
  77. // FIXME FORCE get channel (translate id into mimou-chat-id)
  78. } else {
  79. isInternal = false;
  80. // TODO check javascript:// injections
  81. }
  82. return {
  83. link: link,
  84. text: text || link,
  85. isInternal: isInternal
  86. };
  87. },
  88. _formatText = function(text) {
  89. return formatText(text, {
  90. highlight: [ "fromage" ],
  91. emojiFormatFunction: function(emoji) {
  92. var emojiSrc = emoji;
  93. if (emoji[0] === ':' && emoji[emoji.length -1] === ':')
  94. emoji = emoji.substr(1, emoji.length -2);
  95. var emojiDom = makeEmojiDom(emoji);
  96. if (emojiDom) {
  97. var domParent = document.createElement("span");
  98. domParent.className = R.klass.emoji.small;
  99. domParent.appendChild(emojiDom);
  100. return domParent.outerHTML;
  101. }
  102. return emojiSrc;
  103. },
  104. linkFilter: linkFilter
  105. });
  106. },
  107. updateAttachments = function(_this, channelId) {
  108. var attachmentFrag = document.createDocumentFragment();
  109. for (var i =0, nbAttachments = _this.attachments.length; i < nbAttachments; i++) {
  110. var attachment = _this.attachments[i];
  111. if (attachment) {
  112. var domAttachment = createAttachmentDom(channelId, _this, attachment, i);
  113. if (domAttachment)
  114. attachmentFrag.appendChild(domAttachment);
  115. }
  116. }
  117. _this.dom.attachments.textContent = '';
  118. _this.dom.attachments.appendChild(attachmentFrag);
  119. },
  120. updateCommon = function(_this, sender) {
  121. _this.dom.ts.innerHTML = locale.formatDate(_this.ts);
  122. _this.dom.textDom.innerHTML = _formatText(_this.text);
  123. _this.dom.authorName.textContent = sender ? sender.name : (_this.username || "?");
  124. };
  125. return {
  126. /** @type {Element|null} *
  127. dom: null,
  128. /** @type {boolean} *
  129. uiNeedRefresh: true,
  130. */
  131. /** @param {IUiMessage} _this */
  132. invalidate: function(_this) {
  133. _this.uiNeedRefresh = true;
  134. return _this;
  135. },
  136. /** @param {UiMessage|UiMeMessage|UiNoticeMessage} _this */
  137. removeDom: function(_this) {
  138. if (_this.dom && _this.dom.parentElement) {
  139. _this.dom.remove();
  140. delete(_this.dom);
  141. }
  142. return _this;
  143. },
  144. /** @param {UiMessage|UiMeMessage|UiNoticeMessage} _this */
  145. getDom: function(_this) {
  146. if (!_this.dom) {
  147. _this.createDom().updateDom();
  148. } else if (_this.uiNeedRefresh) {
  149. _this.uiNeedRefresh = false;
  150. _this.updateDom();
  151. }
  152. return _this.dom;
  153. },
  154. /** @param {UiMessage|UiMeMessage|UiNoticeMessage} _this */
  155. updateDom: function(_this) {
  156. var sender = SLACK.context.users[_this.userId];
  157. updateCommon(_this, sender);
  158. updateAttachments(_this, _this.channelId);
  159. updateReactions(_this, _this.channelId);
  160. if (_this.edited)
  161. _this.dom.classList.add(R.klass.msg.editedStatus);
  162. return _this;
  163. },
  164. duplicateDom: function(_this) {
  165. return _this.dom.cloneNode(true);
  166. },
  167. formatText: function(_this, text) {
  168. return _formatText(text);
  169. }
  170. };
  171. })();
  172. /**
  173. * @constructor
  174. * @implements {IUiMessage}
  175. * @extends {MeMessage}
  176. * @param {*} ev
  177. * @param {number} ts
  178. **/
  179. function UiMeMessage(channelId, ev, ts) {
  180. // Extends AbstractUiMessage and MeMessage
  181. MeMessage.call(this, ev, ts);
  182. /** @type {string} @const */
  183. this.channelId = channelId;
  184. this.dom = AbstractUiMessage.dom;
  185. this.uiNeedRefresh = AbstractUiMessage.uiNeedRefresh;
  186. }
  187. UiMeMessage.prototype = Object.create(MeMessage.prototype);
  188. UiMeMessage.prototype.constructor = UiMeMessage;
  189. UiMeMessage.prototype.invalidate = function() {
  190. return AbstractUiMessage.invalidate(this);
  191. };
  192. UiMeMessage.prototype.formatText = function(text) {
  193. return AbstractUiMessage.formatText(this, text);
  194. };
  195. UiMeMessage.prototype.removeDom = function() {
  196. return AbstractUiMessage.removeDom(this);
  197. };
  198. UiMeMessage.prototype.getDom = function() {
  199. return AbstractUiMessage.getDom(this);
  200. };
  201. UiMeMessage.prototype.createDom = function() {
  202. this.dom = doCreateMessageDom(this, false);
  203. this.dom.classList.add(R.klass.msg.meMessage);
  204. return this;
  205. };
  206. UiMeMessage.prototype.duplicateDom = function() {
  207. return AbstractUiMessage.duplicateDom(this);
  208. };
  209. UiMeMessage.prototype.updateDom = function() {
  210. AbstractUiMessage.updateDom(this);
  211. return this;
  212. };
  213. UiMeMessage.prototype.update = function(ev, ts) {
  214. MeMessage.prototype.update.call(this, ev, ts);
  215. this.invalidate();
  216. };
  217. /**
  218. * @constructor
  219. * @implements {IUiMessage}
  220. * @extends {Message}
  221. * @param {*} ev
  222. * @param {number} ts
  223. **/
  224. function UiMessage(channelId, ev, ts) {
  225. // Extends AbstractUiMessage and Message
  226. Message.call(this, ev, ts);
  227. /** @type {string} @const */
  228. this.channelId = channelId;
  229. /** @type {Element} */
  230. this.dom = AbstractUiMessage.dom;
  231. /** @type {boolean} */
  232. this.uiNeedRefresh = AbstractUiMessage.uiNeedRefresh;
  233. }
  234. UiMessage.prototype = Object.create(Message.prototype);
  235. UiMessage.prototype.constructor = UiMessage;
  236. UiMessage.prototype.invalidate = function() {
  237. return AbstractUiMessage.invalidate(this);
  238. };
  239. UiMessage.prototype.formatText = function(text) {
  240. return AbstractUiMessage.formatText(this, text);
  241. };
  242. UiMessage.prototype.removeDom = function() {
  243. return AbstractUiMessage.removeDom(this);
  244. };
  245. UiMessage.prototype.getDom = function() {
  246. return AbstractUiMessage.getDom(this);
  247. };
  248. UiMessage.prototype.createDom = function() {
  249. this.dom = doCreateMessageDom(this, false);
  250. return this;
  251. };
  252. UiMessage.prototype.duplicateDom = function() {
  253. return AbstractUiMessage.duplicateDom(this);
  254. };
  255. UiMessage.prototype.updateDom = function() {
  256. AbstractUiMessage.updateDom(this);
  257. return this;
  258. };
  259. UiMessage.prototype.update = function(ev, ts) {
  260. Message.prototype.update.call(this, ev, ts);
  261. this.invalidate();
  262. };
  263. /**
  264. * @constructor
  265. * @implements {IUiMessage}
  266. * @extends {NoticeMessage}
  267. * @param {*} ev
  268. * @param {number} ts
  269. **/
  270. function UiNoticeMessage(channelId, ev, ts) {
  271. // Extends AbstractUiMessage and NoticeMessage
  272. NoticeMessage.call(this, ev, ts);
  273. /** @type {string} @const */
  274. this.channelId = channelId;
  275. this.dom = AbstractUiMessage.dom;
  276. /** @type {Element} */
  277. this.domWrapper = null;
  278. this.uiNeedRefresh = AbstractUiMessage.uiNeedRefresh;
  279. }
  280. UiNoticeMessage.prototype = Object.create(NoticeMessage.prototype);
  281. UiNoticeMessage.prototype.constructor = UiNoticeMessage;
  282. UiNoticeMessage.prototype.invalidate = function() {
  283. return AbstractUiMessage.invalidate(this);
  284. };
  285. UiNoticeMessage.prototype.formatText = function(text) {
  286. return AbstractUiMessage.formatText(this, text);
  287. };
  288. UiNoticeMessage.prototype.removeDom = function() {
  289. if (this.domWrapper && this.domWrapper.parentElement) {
  290. this.domWrapper.remove();
  291. delete(this.domWrapper);
  292. }
  293. if (this.dom)
  294. delete(this.dom);
  295. return this;
  296. };
  297. UiNoticeMessage.prototype.getDom = function() {
  298. AbstractUiMessage.getDom(this);
  299. return this.domWrapper;
  300. };
  301. UiNoticeMessage.prototype.duplicateDom = function() {
  302. return this.domWrapper.cloneNode(true);
  303. };
  304. UiNoticeMessage.prototype.createDom = function() {
  305. this.dom = doCreateMessageDom(this, false);
  306. this.domWrapper = document.createElement("span");
  307. this.dom.classList.add(R.klass.msg.notice);
  308. this.domWrapper.className = R.klass.msg.notice;
  309. this.domWrapper.textContent = locale.onlyVisible;
  310. return this;
  311. };
  312. UiNoticeMessage.prototype.updateDom = function() {
  313. AbstractUiMessage.updateDom(this);
  314. return this;
  315. };
  316. UiNoticeMessage.prototype.update = function(ev, ts) {
  317. NoticeMessage.prototype.update.call(this, ev, ts);
  318. this.invalidate();
  319. };