ui.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. var
  2. /**
  3. * Minimum time between 2 notifications (ms)
  4. * @const
  5. * @type {number}
  6. **/
  7. NOTIFICATION_COOLDOWN = 30 * 1000, //30 sec
  8. /**
  9. * Maximum time the notification will stay visible (ms)
  10. * @const
  11. * @type {number}
  12. **/
  13. NOTIFICATION_DELAY = 5 * 1000, // 5 sec
  14. MSG_GROUPS = [],
  15. /** @type {number} */
  16. lastNotificationSpawn = 0;
  17. function onContextUpdated() {
  18. var chanListFram = document.createDocumentFragment(),
  19. sortedChans = DATA.context.getChannelIds(function(chan) {
  20. return !chan.archived && chan.isMember !== false;
  21. }),
  22. starred = [],
  23. channels = [],
  24. privs = [],
  25. priv = [];
  26. sortedChans.sort(function(a, b) {
  27. if (a[0] !== b[0]) {
  28. return a[0] - b[0];
  29. }
  30. return DATA.context.getChannel(a).name.localeCompare(DATA.context.getChannel(b).name);
  31. });
  32. sortedChans.forEach(function(chanId) {
  33. var chan = DATA.context.getChannel(chanId),
  34. chanListItem;
  35. if (chan instanceof PrivateMessageRoom) {
  36. if (!chan.user.deleted) {
  37. if ((chanListItem = createImsListItem(chan))) {
  38. if (chan.starred)
  39. starred.push(chanListItem);
  40. else
  41. priv.push(chanListItem);
  42. }
  43. }
  44. //FIXME else remove
  45. } else {
  46. if ((chanListItem = createChanListItem(chan))) {
  47. if (chan.starred)
  48. starred.push(chanListItem);
  49. else if (chan.isPrivate)
  50. privs.push(chanListItem);
  51. else
  52. channels.push(chanListItem);
  53. }
  54. }
  55. });
  56. if (starred.length)
  57. chanListFram.appendChild(createChanListHeader(locale.starred));
  58. starred.forEach(function(dom) {
  59. chanListFram.appendChild(dom);
  60. });
  61. if (channels.length)
  62. chanListFram.appendChild(createChanListHeader(locale.channels));
  63. channels.forEach(function(dom) {
  64. chanListFram.appendChild(dom);
  65. });
  66. privs.forEach(function(dom) {
  67. chanListFram.appendChild(dom);
  68. });
  69. if (priv.length)
  70. chanListFram.appendChild(createChanListHeader(locale.privateMessageRoom));
  71. priv.forEach(function(dom) {
  72. chanListFram.appendChild(dom);
  73. });
  74. document.getElementById(R.id.chanList).textContent = "";
  75. document.getElementById(R.id.chanList).appendChild(chanListFram);
  76. setRoomFromHashBang();
  77. updateTitle();
  78. if (SELECTED_CONTEXT) {
  79. createContextBackground(SELECTED_CONTEXT.getChatContext().team.id, SELECTED_CONTEXT.getChatContext().users, function(imgData) {
  80. document.getElementById(R.id.context).style.backgroundImage = 'url(' +imgData +')';
  81. });
  82. }
  83. }
  84. function onTypingUpdated() {
  85. DATA.context.foreachContext(function(ctx) {
  86. var typing = ctx.typing;
  87. for (var chanId in ctx.self.channels) {
  88. if (!ctx.self.channels[chanId].archived) {
  89. var chanDom = document.getElementById("room_" +chanId);
  90. if (typing[chanId])
  91. chanDom.classList.add(R.klass.chatList.typing);
  92. else
  93. chanDom.classList.remove(R.klass.chatList.typing);
  94. }
  95. }
  96. for (var userId in ctx.users) {
  97. var ims = ctx.users[userId].privateRoom;
  98. if (ims && !ims.archived) {
  99. var userDom = document.getElementById("room_" +ims.id);
  100. if (userDom) {
  101. if (typing[ims.id])
  102. userDom.classList.add(R.klass.chatList.typing);
  103. else
  104. userDom.classList.remove(R.klass.chatList.typing);
  105. }
  106. }
  107. }
  108. });
  109. updateTypingChat();
  110. }
  111. function updateTypingChat() {
  112. var typing;
  113. document.getElementById(R.id.typing).textContent = "";
  114. if (SELECTED_CONTEXT && SELECTED_ROOM && (typing = SELECTED_CONTEXT.getChatContext().typing[SELECTED_ROOM.id])) {
  115. var areTyping = document.createDocumentFragment(),
  116. isOutOfSync = false;
  117. for (var i in typing) {
  118. var member = DATA.context.getUser(i);
  119. if (member)
  120. areTyping.appendChild(makeUserIsTypingDom(member));
  121. else
  122. isOutOfSync = true;
  123. }
  124. if (isOutOfSync)
  125. outOfSync();
  126. document.getElementById(R.id.typing).appendChild(areTyping);
  127. }
  128. }
  129. function onNetworkStateUpdated(isNetworkWorking) {
  130. if (isNetworkWorking)
  131. document.body.classList.remove(R.klass.noNetwork);
  132. else
  133. document.body.classList.add(R.klass.noNetwork);
  134. updateTitle();
  135. }
  136. function onRoomSelected() {
  137. var name = SELECTED_ROOM.name || (SELECTED_ROOM.user ? SELECTED_ROOM.user.name : undefined);
  138. if (!name) {
  139. /** @type {Array.<string>} */
  140. var members = [];
  141. SELECTED_ROOM.users.forEach(function(i) {
  142. members.push(i.name);
  143. });
  144. name = members.join(", ");
  145. }
  146. var roomLi = document.getElementById("room_" +SELECTED_ROOM.id);
  147. document.getElementById(R.id.currentRoom.title).textContent = name;
  148. onRoomUpdated();
  149. focusInput();
  150. document.getElementById(R.id.message.file.formContainer).classList.add(R.klass.hidden);
  151. markRoomAsRead(SELECTED_ROOM);
  152. if (REPLYING_TO) {
  153. REPLYING_TO = null;
  154. onReplyingToUpdated();
  155. }
  156. if (EDITING) {
  157. EDITING = null;
  158. onReplyingToUpdated();
  159. }
  160. updateTypingChat();
  161. }
  162. function onReplyingToUpdated() {
  163. if (REPLYING_TO) {
  164. document.body.classList.add(R.klass.replyingTo);
  165. var domParent = document.getElementById(R.id.message.replyTo),
  166. closeLink = document.createElement("a");
  167. closeLink.addEventListener("click", function() {
  168. REPLYING_TO = null;
  169. onReplyingToUpdated();
  170. });
  171. closeLink.className = R.klass.msg.replyTo.close;
  172. closeLink.textContent = 'x';
  173. domParent.textContent = "";
  174. domParent.appendChild(closeLink);
  175. domParent.appendChild(REPLYING_TO.duplicateDom());
  176. focusInput();
  177. } else {
  178. document.body.classList.remove(R.klass.replyingTo);
  179. document.getElementById(R.id.message.replyTo).textContent = "";
  180. focusInput();
  181. }
  182. }
  183. function onEditingUpdated() {
  184. if (EDITING) {
  185. document.body.classList.add(R.klass.replyingTo);
  186. var domParent = document.getElementById(R.id.message.replyTo),
  187. closeLink = document.createElement("a");
  188. closeLink.addEventListener("click", function() {
  189. EDITING = null;
  190. onEditingUpdated();
  191. });
  192. closeLink.className = R.klass.msg.replyTo.close;
  193. closeLink.textContent = 'x';
  194. domParent.textContent = "";
  195. domParent.appendChild(closeLink);
  196. domParent.appendChild(EDITING.duplicateDom());
  197. document.getElementById(R.id.message.input).value = EDITING.text;
  198. focusInput();
  199. } else {
  200. document.body.classList.remove(R.klass.replyingTo);
  201. document.getElementById(R.id.message.replyTo).textContent = "";
  202. focusInput();
  203. }
  204. }
  205. /**
  206. * @param {string} chanId
  207. * @param {string} msgId
  208. * @param {string} reaction
  209. **/
  210. window['toggleReaction'] = function(chanId, msgId, reaction) {
  211. var hist = DATA.history[chanId],
  212. msg,
  213. ctx;
  214. if ((hist = DATA.history[chanId]) && (msg = hist.getMessageById(msgId)) && (ctx = DATA.context.getChannelContext(chanId))) {
  215. if (msg.hasReactionForUser(reaction, ctx.getChatContext().self.id)) {
  216. removeReaction(chanId, msgId, reaction);
  217. } else {
  218. addReaction(chanId, msgId, reaction);
  219. }
  220. }
  221. };
  222. /**
  223. * Try to resolve emoji from customized context
  224. * @param {string} emoji
  225. * @return {Element|string}
  226. **/
  227. function tryGetCustomEmoji(emoji) {
  228. var loop = {};
  229. if (SELECTED_CONTEXT) {
  230. var ctx = SELECTED_CONTEXT.getChatContext();
  231. while (!loop[emoji]) {
  232. var emojisrc= ctx.emojis.data[emoji];
  233. if (emojisrc) {
  234. if (emojisrc.substr(0, 6) == "alias:") {
  235. loop[emoji] = true;
  236. emoji = emojisrc.substr(6);
  237. } else {
  238. var dom = document.createElement("span");
  239. dom.className = R.klass.emoji.custom +' ' +R.klass.emoji.emoji;
  240. dom.style.backgroundImage = "url('" +emojisrc +"')";
  241. return dom;
  242. }
  243. }
  244. return emoji; // Emoji not found, fallback to std emoji
  245. }
  246. }
  247. return emoji; //loop detected, return first emoji
  248. }
  249. function makeEmojiDom(emojiCode) {
  250. var emoji = tryGetCustomEmoji(emojiCode);
  251. if (typeof emoji === "string" && "makeEmoji" in window)
  252. emoji = window['makeEmoji'](emoji);
  253. return typeof emoji === "string" ? null : emoji;
  254. }
  255. /**
  256. * @param {number} unreadhi
  257. * @param {number} unread
  258. **/
  259. function setFavicon(unreadhi, unread) {
  260. if (!unreadhi && !unread)
  261. document.getElementById(R.id.favicon).href = "favicon_ok.png";
  262. else
  263. document.getElementById(R.id.favicon).href = "favicon.png?h="+unreadhi+"&m="+unread;
  264. }
  265. function setNetErrorFavicon() {
  266. document.getElementById(R.id.favicon).href = "favicon_err.png";
  267. }
  268. function updateTitle() {
  269. var hasHl = HIGHLIGHTED_CHANS.length,
  270. title = "";
  271. if (NEXT_RETRY) {
  272. title = '!' +locale.netErrorShort +' - ';
  273. setNetErrorFavicon();
  274. } else if (hasHl) {
  275. title = "(!" +hasHl +") - ";
  276. setFavicon(hasHl, hasHl);
  277. } else {
  278. var hasUnread = 0;
  279. DATA.context.foreachChannels(function(i) {
  280. if (i.lastMsg > i.lastRead)
  281. hasUnread++;
  282. });
  283. if (hasUnread)
  284. title = "(" +hasUnread +") - ";
  285. setFavicon(0, hasUnread);
  286. }
  287. if (DATA.context.team)
  288. title += DATA.context.team.name;
  289. document.title = title;
  290. }
  291. function spawnNotification() {
  292. if (!("Notification" in window))
  293. {}
  294. else if (Notification.permission === "granted") {
  295. var now = Date.now();
  296. if (lastNotificationSpawn + NOTIFICATION_COOLDOWN < now) {
  297. var n = new Notification(locale.newMessage);
  298. lastNotificationSpawn = now;
  299. setTimeout(function() {
  300. n.close();
  301. }, NOTIFICATION_DELAY);
  302. }
  303. }
  304. else if (Notification.permission !== "denied")
  305. Notification.requestPermission();
  306. }
  307. function onRoomUpdated() {
  308. var chatFrag = document.createDocumentFragment(),
  309. currentRoomId = SELECTED_ROOM.id,
  310. prevMsg = null,
  311. firstTsCombo = 0,
  312. prevMsgDom = null,
  313. currentMsgGroupDom;
  314. MSG_GROUPS = [];
  315. if (DATA.history[currentRoomId])
  316. DATA.history[currentRoomId].messages.forEach(function(msg) {
  317. if (!msg.removed) {
  318. var dom = msg.getDom(),
  319. newGroupDom = false;
  320. if (prevMsg && prevMsg.userId === msg.userId && msg.userId) {
  321. if (Math.abs(firstTsCombo -msg.ts) < 30 && !(msg instanceof MeMessage))
  322. prevMsgDom.classList.add(R.klass.msg.sameTs);
  323. else
  324. firstTsCombo = msg.ts;
  325. } else {
  326. firstTsCombo = msg.ts;
  327. newGroupDom = true;
  328. }
  329. if ((!prevMsg || prevMsg.ts <= SELECTED_ROOM.lastRead) && msg.ts > SELECTED_ROOM.lastRead)
  330. dom.classList.add(R.klass.msg.firstUnread);
  331. else
  332. dom.classList.remove(R.klass.msg.firstUnread);
  333. if (msg instanceof MeMessage) {
  334. prevMsg = null;
  335. prevMsgDom = null;
  336. firstTsCombo = 0;
  337. newGroupDom = true;
  338. chatFrag.appendChild(dom);
  339. currentMsgGroupDom = null;
  340. } else {
  341. if (newGroupDom || !currentMsgGroupDom) {
  342. currentMsgGroupDom = createMessageGroupDom(DATA.context.getUser(msg.userId), msg.username);
  343. MSG_GROUPS.push(currentMsgGroupDom);
  344. chatFrag.appendChild(currentMsgGroupDom);
  345. }
  346. prevMsg = msg;
  347. prevMsgDom = dom;
  348. currentMsgGroupDom.content.appendChild(dom);
  349. }
  350. } else {
  351. msg.removeDom();
  352. }
  353. });
  354. var content = document.getElementById(R.id.currentRoom.content);
  355. //TODO lazy add dom if needed
  356. content.textContent = "";
  357. content.appendChild(chatFrag);
  358. //TODO scroll lock
  359. content.scrollTop = content.scrollHeight -content.clientHeight;
  360. if (window.hasFocus)
  361. markRoomAsRead(SELECTED_ROOM);
  362. }
  363. function onMsgClicked(target, msg) {
  364. if (target.classList.contains(R.klass.msg.hover.reply)) {
  365. if (EDITING) {
  366. EDITING = null;
  367. onEditingUpdated();
  368. }
  369. if (REPLYING_TO !== msg) {
  370. REPLYING_TO = msg;
  371. onReplyingToUpdated();
  372. }
  373. } else if (target.classList.contains(R.klass.msg.hover.reaction)) {
  374. EMOJI_BAR.spawn(document.body, {
  375. selectedRoomId: SELECTED_ROOM.id,
  376. msgId: msg.id
  377. }, function(emoji) {
  378. if (emoji)
  379. addReaction(this.selectedRoomId, this.msgId, emoji);
  380. });
  381. } else if (target.classList.contains(R.klass.msg.hover.edit)) {
  382. if (REPLYING_TO) {
  383. REPLYING_TO = null;
  384. onReplyingToUpdated();
  385. }
  386. if (EDITING !== msg) {
  387. EDITING = msg;
  388. onEditingUpdated();
  389. }
  390. } else if (target.classList.contains(R.klass.msg.hover.remove)) {
  391. //TODO prompt confirm
  392. if (REPLYING_TO) {
  393. REPLYING_TO = null;
  394. onReplyingToUpdated();
  395. }
  396. if (EDITING) {
  397. EDITING = null;
  398. onEditingUpdated();
  399. }
  400. removeMsg(SELECTED_ROOM, msg);
  401. }
  402. }
  403. function chatClickDelegate(e) {
  404. var target = e.target,
  405. getMessageId = function(e, target) {
  406. target = target || e.target;
  407. while (target !== e.currentTarget && target) {
  408. if (target.id && target.classList.contains(R.klass.msg.item)) {
  409. return target.id;
  410. }
  411. target = target.parentElement;
  412. }
  413. };
  414. while (target !== e.currentTarget && target) {
  415. if (target.classList.contains(R.klass.msg.hover.container)) {
  416. return;
  417. }
  418. var messageId,
  419. msg;
  420. if (target.parentElement && target.classList.contains(R.klass.msg.attachment.actionItem)) {
  421. var attachmentIndex = target.dataset["attachmentIndex"],
  422. actionIndex = target.dataset["actionIndex"];
  423. messageId = getMessageId(e, target);
  424. if (messageId && attachmentIndex !== undefined && actionIndex !== undefined) {
  425. messageId = messageId.substr(messageId.lastIndexOf("_") +1);
  426. msg = DATA.history[SELECTED_ROOM.id].getMessageById(messageId);
  427. if (msg && msg.attachments[attachmentIndex] && msg.attachments[attachmentIndex].actions && msg.attachments[attachmentIndex].actions[actionIndex]) {
  428. confirmAction(SELECTED_ROOM.id, msg, msg.attachments[attachmentIndex], msg.attachments[attachmentIndex].actions[actionIndex]);
  429. }
  430. return;
  431. }
  432. }
  433. if (target.parentElement && target.parentElement.classList.contains(R.klass.msg.hover.container)) {
  434. if ((messageId = getMessageId(e, target))) {
  435. messageId = messageId.substr(messageId.lastIndexOf("_") +1);
  436. msg = DATA.history[SELECTED_ROOM.id].getMessageById(messageId);
  437. if (msg)
  438. onMsgClicked(target, msg);
  439. }
  440. return;
  441. }
  442. target = target.parentElement;
  443. }
  444. }
  445. function confirmAction(roomId, msg, attachmentObject, actionObject) {
  446. var confirmed = function() {
  447. var payload = getActionPayload(roomId, msg, attachmentObject, actionObject);
  448. sendCommand(payload, msg.userId);
  449. };
  450. if (actionObject["confirm"]) {
  451. (new ConfirmDialog(actionObject["confirm"]["title"], actionObject["confirm"]["text"]))
  452. .setButtonText(actionObject["confirm"]["ok_text"], actionObject["confirm"]["dismiss_text"])
  453. .onConfirm(confirmed)
  454. .spawn();
  455. } else {
  456. confirmed();
  457. }
  458. }
  459. function focusInput() {
  460. document.getElementById(R.id.message.input).focus();
  461. }
  462. function setRoomFromHashBang() {
  463. var hashId = document.location.hash.substr(1),
  464. room = DATA.context.getChannel(hashId);
  465. if (room && room !== SELECTED_ROOM)
  466. selectRoom(room);
  467. else {
  468. var user = DATA.context.getUser(hashId);
  469. if (user && user.ims)
  470. selectRoom(user.ims);
  471. }
  472. }
  473. function updateAuthorAvatarImsOffset() {
  474. var chatDom = document.getElementById(R.id.currentRoom.content),
  475. chatTop = chatDom.getBoundingClientRect().top;
  476. MSG_GROUPS.forEach(function(group) {
  477. var imgDom = group.authorImg,
  478. imgSize = imgDom.clientHeight,
  479. domRect = group.getBoundingClientRect(),
  480. _top = 0;
  481. imgDom.style.top = Math.max(0, Math.min(chatTop -domRect.top, domRect.height -imgSize -(imgSize /2))) +"px";
  482. });
  483. }
  484. document.addEventListener('DOMContentLoaded', function() {
  485. initLang();
  486. // FIXME load config
  487. initHljs();
  488. document.getElementById(R.id.currentRoom.content).addEventListener("click", chatClickDelegate);
  489. window.addEventListener("hashchange", function(e) {
  490. if (document.location.hash && document.location.hash[0] === '#') {
  491. setRoomFromHashBang();
  492. }
  493. });
  494. document.getElementById(R.id.message.file.cancel).addEventListener("click", function(e) {
  495. e.preventDefault();
  496. document.getElementById(R.id.message.file.error).classList.add(R.klass.hidden);
  497. document.getElementById(R.id.message.file.formContainer).classList.add(R.klass.hidden);
  498. document.getElementById(R.id.message.file.fileInput).value = "";
  499. return false;
  500. });
  501. document.getElementById(R.id.message.file.form).addEventListener("submit", function(e) {
  502. e.preventDefault();
  503. var fileInput = document.getElementById(R.id.message.file.fileInput),
  504. filename = fileInput.value;
  505. if (filename) {
  506. filename = filename.substr(filename.lastIndexOf('\\') +1);
  507. uploadFile(SELECTED_ROOM, filename, fileInput.files[0], function(errorMsg) {
  508. var error = document.getElementById(R.id.message.file.error);
  509. if (errorMsg) {
  510. error.textContent = errorMsg;
  511. error.classList.remove(R.klass.hidden);
  512. } else {
  513. error.classList.add(R.klass.hidden);
  514. document.getElementById(R.id.message.file.fileInput).value = "";
  515. document.getElementById(R.id.message.file.formContainer).classList.add(R.klass.hidden);
  516. }
  517. });
  518. }
  519. return false;
  520. });
  521. document.getElementById(R.id.message.file.bt).addEventListener("click", function(e) {
  522. e.preventDefault();
  523. if (SELECTED_ROOM) {
  524. document.getElementById(R.id.message.file.formContainer).classList.remove(R.klass.hidden);
  525. }
  526. return false;
  527. });
  528. document.getElementById(R.id.message.form).addEventListener("submit", function(e) {
  529. e.preventDefault();
  530. var input =document.getElementById(R.id.message.input);
  531. if (SELECTED_ROOM && input.value) {
  532. if (onTextEntered(input.value)) {
  533. input.value = "";
  534. if (REPLYING_TO) {
  535. REPLYING_TO = null;
  536. onReplyingToUpdated();
  537. }
  538. if (EDITING) {
  539. EDITING = null;
  540. onReplyingToUpdated();
  541. }
  542. document.getElementById(R.id.message.slashComplete).textContent = '';
  543. }
  544. }
  545. focusInput();
  546. return false;
  547. });
  548. window.addEventListener('blur', function() {
  549. window.hasFocus = false;
  550. });
  551. window.addEventListener('focus', function() {
  552. window.hasFocus = true;
  553. lastNotificationSpawn = 0;
  554. if (SELECTED_ROOM)
  555. markRoomAsRead(SELECTED_ROOM);
  556. focusInput();
  557. });
  558. document.getElementById(R.id.currentRoom.content).addEventListener('scroll', updateAuthorAvatarImsOffset);
  559. var lastKeyDown = 0;
  560. document.getElementById(R.id.message.input).addEventListener('input', function() {
  561. if (SELECTED_ROOM) {
  562. var now = Date.now();
  563. if (lastKeyDown + 3000 < now && (SELECTED_CONTEXT.getChatContext().self.presence || (SELECTED_ROOM instanceof PrivateMessageRoom))) {
  564. sendTyping(SELECTED_ROOM);
  565. lastKeyDown = now;
  566. }
  567. var /** @type {Array<Command|{names: Array<string>!, desc: string!, usage: string!, category: string!, exec: Function!}>} */
  568. commands = [],
  569. input = this.value;
  570. if (this.value[0] === '/') {
  571. var endCmd = input.indexOf(' '),
  572. inputFinished = endCmd !== -1;
  573. endCmd = endCmd === -1 ? input.length : endCmd;
  574. var inputCmd = input.substr(0, endCmd);
  575. if (inputFinished) {
  576. var currentClientCmd = CLIENT_COMMANDS.getCommand(inputCmd);
  577. if (currentClientCmd)
  578. commands.push(currentClientCmd);
  579. } else {
  580. commands = CLIENT_COMMANDS.getCommandsStartingWith(inputCmd);
  581. }
  582. var availableCommands = (SELECTED_CONTEXT ? SELECTED_CONTEXT.getChatContext().commands.data : {});
  583. for (var currentCmdId in availableCommands) {
  584. var currentCmd = availableCommands[currentCmdId];
  585. if ((!inputFinished && currentCmd.name.substr(0, endCmd) === inputCmd) ||
  586. (inputFinished && currentCmd.name === inputCmd))
  587. commands.push(currentCmd);
  588. }
  589. }
  590. commands.sort(function(a, b) {
  591. return a.category.localeCompare(b.category) || a.name.localeCompare(b.name);
  592. });
  593. var slashDom = document.getElementById(R.id.message.slashComplete),
  594. slashFrag = document.createDocumentFragment(),
  595. prevService;
  596. slashDom.textContent = '';
  597. for (var i =0, nbCmd = commands.length; i < nbCmd; i++) {
  598. var command = commands[i];
  599. if (prevService !== command.category) {
  600. prevService = command.category;
  601. slashFrag.appendChild(createSlashAutocompleteHeader(command.category));
  602. }
  603. slashFrag.appendChild(createSlashAutocompleteDom(command));
  604. }
  605. slashDom.appendChild(slashFrag);
  606. }
  607. });
  608. window.hasFocus = true;
  609. //Emoji closure
  610. (function() {
  611. var emojiButton = document.getElementById(R.id.message.emoji);
  612. if ('makeEmoji' in window) {
  613. var emojiDom = window['makeEmoji']('smile');
  614. if (emojiDom) {
  615. emojiButton.innerHTML = "<span class='" +R.klass.emoji.small +"'>" +emojiDom.outerHTML +"</span>";
  616. } else {
  617. emojiButton.style.backgroundImage = 'url("smile.svg")';
  618. }
  619. emojiDom = window['makeEmoji']('paperclip');
  620. if (emojiDom) {
  621. document.getElementById(R.id.message.file.bt).innerHTML = "<span class='" +R.klass.emoji.small +"'>" +emojiDom.outerHTML +"</span>";
  622. } else {
  623. document.getElementById(R.id.message.file.bt).style.backgroundImage = 'url("public/paperclip.svg")';
  624. }
  625. emojiButton.addEventListener("click", function() {
  626. if (SELECTED_CONTEXT)
  627. EMOJI_BAR.spawn(document.body, SELECTED_CONTEXT.getChatContext(), function(e) {
  628. if (e) document.getElementById(R.id.message.input).value += ":"+e+":";
  629. focusInput();
  630. });
  631. });
  632. } else {
  633. emojiButton.classList.add(R.klass.hidden);
  634. }
  635. })();
  636. startPolling();
  637. });