1
0

dom.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. /**
  2. * @return {Element!}
  3. **/
  4. function createTypingDisplay() {
  5. var dom = document.createElement("span")
  6. ,dot1 = document.createElement("span")
  7. ,dot2 = document.createElement("span")
  8. ,dot3 = document.createElement("span");
  9. dom.className = R.klass.typing.container;
  10. dot1.className = R.klass.typing.dot1;
  11. dot2.className = R.klass.typing.dot2;
  12. dot3.className = R.klass.typing.dot3;
  13. dot1.textContent = dot2.textContent = dot3.textContent = '.';
  14. dom.appendChild(dot1);
  15. dom.appendChild(dot2);
  16. dom.appendChild(dot3);
  17. return dom;
  18. }
  19. /**
  20. * @param {Room} chan
  21. * @param {string|undefined} alternateChanName
  22. * @return {Element}
  23. **/
  24. function createChanListItem(chan, alternateChanName) {
  25. var dom = document.createElement("li")
  26. ,link = document.createElement("a");
  27. dom.id = "room_" +chan.id;
  28. link.href = '#' +chan.id;
  29. if (chan.isPrivate) {
  30. dom.className = R.klass.chatList.entry + " " +R.klass.chatList.typePrivate;
  31. dom.dataset["count"] = Object.keys((chan.users) || {}).length;
  32. } else {
  33. dom.className = R.klass.chatList.entry + " " +R.klass.chatList.typeChannel;
  34. }
  35. if (SELECTED_ROOM === chan)
  36. dom.classList.add(R.klass.selected);
  37. link.textContent = alternateChanName || chan.name;
  38. dom.appendChild(createTypingDisplay());
  39. dom.appendChild(link);
  40. if (chan.lastMsg !== chan.lastRead && chan.lastMsg !== undefined) {
  41. dom.classList.add(R.klass.unread);
  42. if (HIGHLIGHTED_CHANS.indexOf(chan) >= 0)
  43. dom.classList.add(R.klass.unreadHi);
  44. }
  45. return dom;
  46. }
  47. /** @type {function(string):Element} */
  48. var createChanListHeader = (function() {
  49. var cache = {};
  50. return function(title) {
  51. var dom = cache[title];
  52. if (!dom) {
  53. dom = cache[title] = document.createElement("header");
  54. dom.textContent = title;
  55. }
  56. return dom;
  57. };
  58. })();
  59. /**
  60. * @param {PrivateMessageRoom} ims
  61. * @param {string|undefined} alternateChanName
  62. * @return {Element}
  63. **/
  64. function createImsListItem(ims, alternateChanName) {
  65. var dom = document.createElement("li")
  66. ,link = document.createElement("a");
  67. dom.id = "room_" +ims.id;
  68. link.href = '#' +ims.id;
  69. dom.className = R.klass.chatList.entry + " " +R.klass.chatList.typeDirect +" " +R.klass.presenceIndicator;
  70. link.textContent = alternateChanName || ims.user.getName();
  71. dom.appendChild(createTypingDisplay());
  72. dom.appendChild(link);
  73. if (!ims.user.presence)
  74. dom.classList.add(R.klass.presenceAway);
  75. if (SELECTED_ROOM === ims)
  76. dom.classList.add(R.klass.selected);
  77. if (ims.lastMsg !== ims.lastRead && ims.lastMsg !== undefined) {
  78. dom.classList.add(R.klass.unread);
  79. dom.classList.add(R.klass.unreadHi); // Always HI on private message
  80. }
  81. return dom;
  82. }
  83. /**
  84. * Try to resolve emoji from customized context
  85. * @param {string} emoji
  86. * @return {Element|string}
  87. **/
  88. function tryGetCustomEmoji(emoji) {
  89. var loop = {},
  90. emojiName = emoji;
  91. if (SELECTED_CONTEXT) {
  92. var ctx = SELECTED_CONTEXT.getChatContext();
  93. while (!loop[emoji]) {
  94. var emojisrc= ctx.emojis.data[emoji];
  95. if (emojisrc) {
  96. if (emojisrc.substr(0, 6) == "alias:") {
  97. loop[emoji] = true;
  98. emoji = emojisrc.substr(6);
  99. } else {
  100. var dom = document.createElement("span");
  101. dom.className = R.klass.emoji.custom +' ' +R.klass.emoji.emoji;
  102. dom.style.backgroundImage = "url('" +emojisrc +"')";
  103. dom.textContent = ':' +emojiName +':';
  104. dom.title = emojiName;
  105. return dom;
  106. }
  107. } else {
  108. return emoji; // Emoji not found, fallback to std emoji
  109. }
  110. }
  111. }
  112. return emojiName; //loop detected, return first emoji
  113. }
  114. function makeEmojiDom(emojiCode) {
  115. var emoji = tryGetCustomEmoji(emojiCode);
  116. if (typeof emoji === "string" && "makeEmoji" in window)
  117. emoji = window['makeEmoji'](emoji);
  118. return typeof emoji === "string" ? null : emoji;
  119. }
  120. /**
  121. * @param {string} chanId
  122. * @param {string} msgId
  123. * @param {string} reaction
  124. * @param {Array.<string>} users
  125. * @return {Element|null}
  126. **/
  127. function createReactionDom(chanId, msgId, reaction, users) {
  128. var emojiDom = makeEmojiDom(reaction);
  129. if (emojiDom) {
  130. var dom = document.createElement("li")
  131. ,a = document.createElement("a")
  132. ,emojiContainer = document.createElement("span")
  133. ,userList = document.createElement("span")
  134. ,userNames = [];
  135. for (var i =0, nbUser = users.length; i < nbUser; i++) {
  136. var user = DATA.context.getUser(users[i]);
  137. if (user)
  138. userNames.push(user.getName());
  139. }
  140. userNames.sort();
  141. userList.textContent = userNames.join(", ");
  142. emojiContainer.appendChild(emojiDom);
  143. emojiContainer.className = R.klass.emoji.small;
  144. a.href = "javascript:toggleReaction('" +chanId +"', '" +msgId +"', '" +reaction +"')";
  145. a.appendChild(emojiContainer);
  146. a.appendChild(userList);
  147. dom.className = R.klass.msg.reactions.item;
  148. dom.appendChild(a);
  149. return dom;
  150. } else {
  151. console.warn("Reaction id not found: " +reaction);
  152. }
  153. return null;
  154. }
  155. /**
  156. * @param {Element} hover
  157. * @param {UiMessage|UiMeMessage|UiNoticeMessage} msg
  158. **/
  159. function addHoverButtons(hover, msg) {
  160. var capacities = msg.context.getChatContext().capacities,
  161. isMe = DATA.context.isMe(msg.userId);
  162. if (capacities["replyToMsg"]) {
  163. var hoverReply = document.createElement("li");
  164. hoverReply.className = R.klass.msg.hover.reply;
  165. hoverReply.style.backgroundImage = 'url("repl.svg")';
  166. hover.appendChild(hoverReply);
  167. }
  168. if (capacities["reactMsg"]) {
  169. var hoverReaction = document.createElement("li");
  170. hoverReaction.className = R.klass.msg.hover.reaction;
  171. hoverReaction.style.backgroundImage = 'url("smile.svg")';
  172. hover.appendChild(hoverReaction);
  173. }
  174. if (isMe && capacities["editMsg"] || capacities["editOtherMsg"]) {
  175. var hoverEdit = document.createElement("li");
  176. hoverEdit.className = R.klass.msg.hover.edit;
  177. hoverEdit.style.backgroundImage = 'url("edit.svg")';
  178. hover.appendChild(hoverEdit);
  179. }
  180. if (capacities["starMsg"]) {
  181. hover.hoverStar = document.createElement("li")
  182. hover.hoverStar.className = R.klass.msg.hover.star;
  183. hover.appendChild(hover.hoverStar);
  184. }
  185. if (capacities["pinMsg"]) {
  186. var hoverPin = document.createElement("li")
  187. hoverPin.className = R.klass.msg.hover.pin;
  188. hover.appendChild(hoverPin);
  189. hoverPin.style.backgroundImage = 'url("pin.svg")';
  190. }
  191. if (isMe && capacities["removeMsg"] || capacities["moderate"]) {
  192. var hoverRemove = document.createElement("li")
  193. hoverRemove.className = R.klass.msg.hover.remove;
  194. hoverRemove.style.backgroundImage = 'url("remove.svg")';
  195. hover.appendChild(hoverRemove);
  196. }
  197. }
  198. /**
  199. * @param {UiMessage|UiMeMessage|UiNoticeMessage} msg
  200. * @return {Element}
  201. **/
  202. function doCreateMessageDom(msg) {
  203. var channelId = msg.channelId;
  204. var dom = document.createElement("div")
  205. ,msgBlock = document.createElement("div")
  206. ,hoverReaction = document.createElement("li");
  207. dom.hover = document.createElement("ul");
  208. dom.attachments = document.createElement("ul");
  209. dom.reactions = document.createElement("ul");
  210. dom.ts = document.createElement("div");
  211. dom.textDom = document.createElement("div");
  212. dom.authorName = document.createElement("span");
  213. dom.id = channelId +"_" +msg.id;
  214. dom.className = R.klass.msg.item;
  215. dom.ts.className = R.klass.msg.ts;
  216. dom.textDom.className = R.klass.msg.msg;
  217. dom.authorName.className = R.klass.msg.authorname;
  218. addHoverButtons(dom.hover, msg);
  219. dom.hover.className = R.klass.msg.hover.container;
  220. msgBlock.appendChild(dom.authorName);
  221. msgBlock.appendChild(dom.textDom);
  222. msgBlock.appendChild(dom.ts);
  223. msgBlock.appendChild(dom.attachments);
  224. dom.edited = document.createElement("div");
  225. dom.edited.className = R.klass.msg.edited;
  226. msgBlock.appendChild(dom.edited);
  227. msgBlock.appendChild(dom.reactions);
  228. msgBlock.className = R.klass.msg.content;
  229. dom.attachments.className = R.klass.msg.attachment.list;
  230. dom.reactions.className = R.klass.msg.reactions.container;
  231. dom.appendChild(msgBlock);
  232. dom.appendChild(dom.hover);
  233. return dom;
  234. }
  235. function makeUserColor(userName) {
  236. return "black";
  237. }
  238. /**
  239. * @param {Chatter} user
  240. * @param {string} userName
  241. * @return {Element}
  242. **/
  243. function createMessageGroupDom(user, userName) {
  244. var dom = document.createElement("div")
  245. ,authorBlock = document.createElement("div")
  246. ,authorName = document.createElement("a")
  247. ,authorImg = document.createElement("img");
  248. dom.authorImgWrapper = document.createElement("span")
  249. dom.authorImgWrapper.className = R.klass.msg.authorAvatarWrapper;
  250. authorImg.className = R.klass.msg.authorAvatar;
  251. authorName.className = R.klass.msg.authorname;
  252. authorName.href = "#" +user.id;
  253. if (user) {
  254. authorName.textContent = user.getName();
  255. authorName.style.color = makeUserColor(user.getName());
  256. authorImg.src = user.getSmallIcon();
  257. } else {
  258. authorName.textContent = userName || "?";
  259. authorImg.src = "";
  260. }
  261. dom.authorImgWrapper.appendChild(authorImg);
  262. authorBlock.appendChild(dom.authorImgWrapper);
  263. authorBlock.appendChild(authorName);
  264. authorBlock.className = R.klass.msg.author;
  265. dom.className = R.klass.msg.authorGroup;
  266. dom.appendChild(authorBlock);
  267. dom.content = document.createElement("div");
  268. dom.content.className = R.klass.msg.authorMessages;
  269. dom.appendChild(dom.content);
  270. return dom;
  271. }
  272. /**
  273. * @param {string=} colorText
  274. * @return {string}
  275. **/
  276. function getColor(colorText) {
  277. /** @const @type {Object.<string, string>} */
  278. var colorMap = {
  279. "good": "#2fa44f"
  280. ,"warning": "#de9e31"
  281. ,"danger": "#d50200"
  282. };
  283. if (colorText) {
  284. if (colorText[0] === '#')
  285. return colorText;
  286. else if (colorMap[colorText])
  287. return colorMap[colorText];
  288. }
  289. return "#e3e4e6";
  290. }
  291. /**
  292. * @param {string} channelId
  293. * @param {UiNoticeMessage|UiMessage} msg
  294. * @param {*} attachment
  295. * @param {number} attachmentIndex
  296. * @return {Element|null}
  297. **/
  298. function createAttachmentDom(channelId, msg, attachment, attachmentIndex) {
  299. var rootDom = document.createElement("li")
  300. ,attachmentBlock = document.createElement("div")
  301. ,pretext = document.createElement("div")
  302. ,titleBlock = document.createElement("a")
  303. ,authorBlock = document.createElement("div")
  304. ,authorImg = document.createElement("img")
  305. ,authorName = document.createElement("a")
  306. ,textBlock = document.createElement("div")
  307. ,textDom = document.createElement("div")
  308. ,thumbImgDom = document.createElement("div")
  309. ,imgDom = document.createElement("img")
  310. ,footerBlock = document.createElement("div")
  311. ;
  312. rootDom.className = R.klass.msg.attachment.container;
  313. //Color
  314. attachmentBlock.style.borderColor = getColor(attachment["color"] || "");
  315. attachmentBlock.className = R.klass.msg.attachment.block;
  316. //Pretext
  317. pretext.className = R.klass.msg.attachment.pretext;
  318. if (attachment["pretext"]) {
  319. pretext.innerHTML = msg.formatText(attachment["pretext"]);
  320. } else {
  321. pretext.classList.add(R.klass.hidden);
  322. }
  323. //Title
  324. titleBlock.target = "_blank";
  325. if (attachment["title"]) {
  326. titleBlock.innerHTML = msg.formatText(attachment["title"]);
  327. if (attachment["title_link"]) {
  328. titleBlock.href = attachment["title_link"];
  329. }
  330. titleBlock.className = R.klass.msg.attachment.title;
  331. } else {
  332. titleBlock.className = R.klass.hidden + " " +R.klass.msg.attachment.title;
  333. }
  334. //Author
  335. authorName.target = "_blank";
  336. authorBlock.className = R.klass.msg.author;
  337. if (attachment["author_name"]) {
  338. authorName.innerHTML = msg.formatText(attachment["author_name"]);
  339. authorName.href = attachment["author_link"] || "";
  340. authorName.className = R.klass.msg.authorname;
  341. authorImg.className = R.klass.msg.authorAvatar;
  342. if (attachment["author_icon"]) {
  343. authorImg.src = attachment["author_icon"];
  344. authorBlock.appendChild(authorImg);
  345. }
  346. authorBlock.appendChild(authorName);
  347. }
  348. // Img (small one)
  349. thumbImgDom.className = R.klass.msg.attachment.thumbImg;
  350. if (attachment["thumb_url"]) {
  351. var img = document.createElement("img");
  352. img.src = attachment["thumb_url"];
  353. thumbImgDom.appendChild(img);
  354. attachmentBlock.classList.add(R.klass.msg.attachment.hasThumb);
  355. if (attachment["video_html"])
  356. thumbImgDom.dataset["video"] = attachment["video_html"];
  357. } else {
  358. thumbImgDom.classList.add(R.klass.hidden);
  359. }
  360. //Text
  361. textBlock.className = R.klass.msg.attachment.content;
  362. var textContent = msg.formatText(attachment["text"] || "");
  363. textDom.className = R.klass.msg.attachment.text;
  364. if (textContent && textContent != "") {
  365. textDom.innerHTML = textContent;
  366. } else {
  367. textDom.classList.add(R.klass.hidden);
  368. }
  369. textBlock.appendChild(thumbImgDom);
  370. textBlock.appendChild(textDom);
  371. if (attachment["geo"]) {
  372. var geoTileDom = makeOSMTiles(attachment["geo"]);
  373. if (geoTileDom)
  374. textBlock.appendChild(geoTileDom);
  375. }
  376. //Img (the big one)
  377. imgDom.className = R.klass.msg.attachment.img;
  378. if (attachment["image_url"])
  379. imgDom.src = attachment["image_url"];
  380. else
  381. imgDom.classList.add(R.klass.hidden);
  382. //Footer
  383. footerBlock.className = R.klass.msg.attachment.footer;
  384. if (attachment["footer"]) {
  385. var footerText = document.createElement("span")
  386. footerText.className = R.klass.msg.attachment.footerText;
  387. footerText.innerHTML = msg.formatText(attachment["footer"]);
  388. if (attachment["footer_icon"]) {
  389. var footerIcon = document.createElement("img")
  390. footerIcon.src = attachment["footer_icon"];
  391. footerIcon.className = R.klass.msg.attachment.footerIcon;
  392. footerBlock.appendChild(footerIcon);
  393. }
  394. footerBlock.appendChild(footerText);
  395. }
  396. //Ts
  397. if (attachment["ts"]) {
  398. var footerTs = document.createElement("span")
  399. footerTs.className = R.klass.msg.ts;
  400. footerTs.innerHTML = locale.formatDate(attachment["ts"]);
  401. footerBlock.appendChild(footerTs);
  402. }
  403. attachmentBlock.appendChild(titleBlock);
  404. attachmentBlock.appendChild(authorBlock);
  405. attachmentBlock.appendChild(textBlock);
  406. attachmentBlock.appendChild(imgDom);
  407. // Fields
  408. if (attachment["fields"] && attachment["fields"].length) {
  409. var fieldsContainer = document.createElement("ul");
  410. attachmentBlock.appendChild(fieldsContainer);
  411. fieldsContainer.className = R.klass.msg.attachment.field.container;
  412. attachment["fields"].forEach(function(fieldData) {
  413. var fieldDom = createFieldDom(msg, fieldData["title"] || "", fieldData["value"] || "", !!fieldData["short"]);
  414. if (fieldDom) {
  415. fieldsContainer.appendChild(fieldDom);
  416. }
  417. });
  418. }
  419. // Buttons
  420. if (attachment["actions"] && attachment["actions"].length) {
  421. var buttons;
  422. buttons = document.createElement("ul");
  423. buttons.className = R.klass.msg.attachment.actions +' ' +R.klass.buttonContainer;
  424. attachmentBlock.appendChild(buttons);
  425. for (var i =0, nbAttachments = attachment["actions"].length; i < nbAttachments; i++) {
  426. var action = attachment["actions"][i];
  427. if (action) {
  428. var button = createActionButtonDom(attachmentIndex, i, action);
  429. if (button) {
  430. buttons.appendChild(button);
  431. }
  432. }
  433. }
  434. }
  435. attachmentBlock.appendChild(footerBlock);
  436. rootDom.appendChild(pretext);
  437. rootDom.appendChild(attachmentBlock);
  438. return rootDom;
  439. }
  440. /**
  441. * @param {UiMessage|UiNoticeMessage} msg
  442. * @param {string} title
  443. * @param {string} text
  444. * @param {boolean} isShort
  445. * @return {Element}
  446. **/
  447. function createFieldDom(msg, title, text, isShort) {
  448. var fieldDom = document.createElement("li")
  449. ,titleDom = document.createElement("div")
  450. ,textDom = document.createElement("div");
  451. fieldDom.className = R.klass.msg.attachment.field.item;
  452. if (!isShort) {
  453. fieldDom.classList.add(R.klass.msg.attachment.field.longField);
  454. }
  455. titleDom.className = R.klass.msg.attachment.field.title;
  456. titleDom.textContent = title;
  457. textDom.className = R.klass.msg.attachment.field.text;
  458. textDom.innerHTML = msg.formatText(text);
  459. fieldDom.appendChild(titleDom);
  460. fieldDom.appendChild(textDom);
  461. return fieldDom;
  462. }
  463. /**
  464. * @param {number} attachmentIndex
  465. * @param {number} actionIndex
  466. * @param {*} action
  467. * @return {Element}
  468. **/
  469. function createActionButtonDom(attachmentIndex, actionIndex, action) {
  470. var li = document.createElement("li")
  471. ,color = getColor(action["style"]);
  472. li.textContent = action["text"];
  473. if (color !== getColor())
  474. li.style.color = color;
  475. li.style.borderColor = color;
  476. li.dataset["attachmentIndex"] = attachmentIndex;
  477. li.dataset["actionIndex"] = actionIndex;
  478. li.className = R.klass.msg.attachment.actionItem +' ' +R.klass.button;
  479. return li;
  480. }
  481. /**
  482. * @param {Chatter} user
  483. * @return {Element}
  484. **/
  485. function makeUserIsTypingDom(user) {
  486. var dom = document.createElement("li")
  487. ,userName = document.createElement("span");
  488. userName.textContent = user.getName();
  489. dom.appendChild(createTypingDisplay());
  490. dom.appendChild(userName);
  491. return dom;
  492. }