msgInput.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /**
  2. * @param {string} servicename
  3. * @return {Element}
  4. **/
  5. function createSlashAutocompleteHeader(servicename) {
  6. var lh = document.createElement("lh");
  7. lh.textContent = servicename;
  8. lh.className = R.klass.commands.header;
  9. return lh;
  10. }
  11. /**
  12. * @param {Command|{names: Array<string>!, desc: string!, usage: string!,category: string!, exec: Function!}|string} cmd
  13. * @param {Element=} imgSpan
  14. * @return {Element}
  15. **/
  16. function createSlashAutocompleteDom(cmd, imgSpan) {
  17. var li = document.createElement("li")
  18. ,name = document.createElement("span")
  19. name.className = R.klass.commands.name;
  20. if (typeof cmd === "string") {
  21. if (imgSpan) {
  22. li.appendChild(imgSpan);
  23. }
  24. name.textContent = cmd;
  25. li.appendChild(name);
  26. } else {
  27. var usage = document.createElement("span")
  28. ,desc = document.createElement("span");
  29. name.textContent = cmd.name;
  30. usage.textContent = cmd.usage;
  31. desc.textContent = cmd.desc;
  32. usage.className = R.klass.commands.usage;
  33. desc.className = R.klass.commands.desc;
  34. li.appendChild(name);
  35. li.appendChild(usage);
  36. li.appendChild(desc);
  37. }
  38. li.dataset.input = name.textContent;
  39. li.className = R.klass.commands.item;
  40. return li;
  41. }
  42. function autoComplete(inputDom) {
  43. var now = Date.now(),
  44. slashDom = document.getElementById(R.id.message.slashComplete);
  45. if (slashDom.dataset.cursor)
  46. delete slashDom.dataset.cursor;
  47. var /** @type {Array<Chatter|Room|Command|{names: Array<string>!, desc: string!, usage: string!, category: string!, exec: Function!}|{name: string, provider: string, emojiSpan: Element}>} */
  48. commands = [],
  49. /** @const @type {string} */
  50. input = inputDom.value;
  51. if (inputDom.selectionStart === inputDom.selectionEnd && inputDom.selectionStart) {
  52. var start = inputDom.selectionStart,
  53. end = inputDom.selectionEnd;
  54. for (; start && input[start -1] !== ' '; start--) {}
  55. for (var valueLen = input.length; end < valueLen && input[end] !== ' '; end++) {}
  56. if (start !== end && end -start -1 > 0) {
  57. if (input[start] === '#') {
  58. var channels = SELECTED_CONTEXT.getChatContext().channels,
  59. inputStr = input.substr(start +1, end -start -1);
  60. for (var i in channels) {
  61. if (channels[i].name.length >= inputStr.length && channels[i].name.substr(0, inputStr.length) === inputStr)
  62. commands.push(channels[i]);
  63. }
  64. } else if (input[start] === '@') {
  65. var users = SELECTED_ROOM instanceof PrivateMessageRoom ? SELECTED_CONTEXT.getChatContext().users : SELECTED_ROOM.users,
  66. inputStr = input.substr(start +1, end -start -1);
  67. for (var i in users) {
  68. var userName = users[i].getName();
  69. if (userName.length >= inputStr.length && userName.substr(0, inputStr.length) === inputStr)
  70. commands.push(users[i]);
  71. }
  72. } else if (input[start] === ':' && window['searchEmojis']) {
  73. var inputCmd = input.substr(start +1, end -start -1),
  74. emojiList = window['searchEmojis'](inputCmd);
  75. for (var emojiCode in emojiList) {
  76. var domEmoji = window['makeEmoji'](emojiCode, false),
  77. domParent = document.createElement("span");
  78. domParent.appendChild(domEmoji);
  79. domParent.className = R.klass.emoji.small;
  80. commands.push({
  81. name: ':' +emojiCode +':',
  82. emojiSpan: domParent,
  83. provider: CURRENT_EMOJI_PROVIDER.name
  84. });
  85. }
  86. for (var i in SELECTED_CONTEXT.getChatContext().emojis.data) {
  87. if (i.length >= inputCmd.length && i.substr(0, inputCmd.length) === inputCmd) {
  88. var emojiSpan = document.createElement("span");
  89. emojiSpan.className = R.klass.emoji.small;
  90. emojiSpan.appendChild(makeEmojiDom(i));
  91. commands.push({
  92. name: ':' +i +':',
  93. emojiSpan: emojiSpan,
  94. provider: "custom"
  95. });
  96. }
  97. }
  98. }
  99. if (commands.length)
  100. slashDom.dataset.cursor = JSON.stringify([start, end]);
  101. }
  102. }
  103. if (!commands.length && input[0] === '/') {
  104. var endCmd = input.indexOf(' '),
  105. inputFinished = endCmd !== -1;
  106. endCmd = endCmd === -1 ? input.length : endCmd;
  107. var inputCmd = input.substr(0, endCmd);
  108. if (inputFinished) {
  109. var currentClientCmd = CLIENT_COMMANDS.getCommand(inputCmd);
  110. if (currentClientCmd)
  111. commands.push(currentClientCmd);
  112. } else {
  113. commands = CLIENT_COMMANDS.getCommandsStartingWith(inputCmd);
  114. slashDom.dataset.cursor = JSON.stringify([0, inputDom.selectionEnd]);
  115. }
  116. var availableCommands = (SELECTED_CONTEXT ? SELECTED_CONTEXT.getChatContext().commands.data : {});
  117. for (var currentCmdId in availableCommands) {
  118. var currentCmd = availableCommands[currentCmdId];
  119. if ((!inputFinished && currentCmd.name.substr(0, endCmd) === inputCmd) ||
  120. (inputFinished && currentCmd.name === inputCmd))
  121. commands.push(currentCmd);
  122. }
  123. commands.sort(function(a, b) {
  124. return a.category.localeCompare(b.category) || a.name.localeCompare(b.name);
  125. });
  126. }
  127. slashDom.textContent = '';
  128. if (commands.length) {
  129. var slashFrag = document.createDocumentFragment(),
  130. prevService;
  131. for (var i =0, nbCmd = commands.length; i < nbCmd; i++) {
  132. var command = commands[i];
  133. if (command instanceof Chatter) {
  134. if (!prevService) {
  135. prevService = true;
  136. slashFrag.appendChild(createSlashAutocompleteHeader(locale.members));
  137. }
  138. var span = document.createElement("span");
  139. span.className = R.klass.commands.userIcon;
  140. span.style.backgroundImage = "url(\"" +command.getSmallIcon() +"\")";
  141. slashFrag.appendChild(createSlashAutocompleteDom('@' +command.getName(), span));
  142. } else if (command instanceof Room) {
  143. if (!prevService) {
  144. prevService = true;
  145. slashFrag.appendChild(createSlashAutocompleteHeader(locale.channels));
  146. }
  147. slashFrag.appendChild(createSlashAutocompleteDom('#' +command.name));
  148. } else if (command.emojiSpan) {
  149. if (prevService !== command.provider) {
  150. prevService = command.provider;
  151. slashFrag.appendChild(createSlashAutocompleteHeader(command.provider));
  152. }
  153. slashFrag.appendChild(createSlashAutocompleteDom(command.name, command.emojiSpan));
  154. } else {
  155. if (prevService !== command.category) {
  156. prevService = command.category;
  157. slashFrag.appendChild(createSlashAutocompleteHeader(command.category));
  158. }
  159. slashFrag.appendChild(createSlashAutocompleteDom(command));
  160. }
  161. }
  162. slashDom.appendChild(slashFrag);
  163. }
  164. }
  165. function focusInput() {
  166. document.getElementById(R.id.message.input).focus();
  167. }
  168. function initMsgInput() {
  169. var lastKeyDown = 0,
  170. input = document.getElementById(R.id.message.input);
  171. input.addEventListener('input', function() {
  172. if (SELECTED_ROOM) {
  173. var now = Date.now();
  174. if (lastKeyDown + 3000 < now && (SELECTED_CONTEXT.getChatContext().self.presence || (SELECTED_ROOM instanceof PrivateMessageRoom))) {
  175. sendTyping(SELECTED_ROOM);
  176. lastKeyDown = now;
  177. }
  178. autoComplete(this);
  179. }
  180. });
  181. input.addEventListener("keydown", function(e) {
  182. /** @const */
  183. var TAB_KEY = 9;
  184. if (e.keyCode === TAB_KEY) {
  185. e.preventDefault();
  186. // FIXME cycle though propositions
  187. return false;
  188. }
  189. });
  190. document.getElementById(R.id.message.slashComplete).addEventListener("click", function(e) {
  191. if (SELECTED_ROOM) {
  192. var target = e.target;
  193. var cursor = this.dataset.cursor;
  194. if (cursor) {
  195. cursor = JSON.parse(cursor);
  196. while (target && target !== this) {
  197. if (target.dataset.input) {
  198. var inputElement = document.getElementById(R.id.message.input),
  199. toAdd = target.dataset.input;
  200. if (inputElement.value.length <= cursor[1])
  201. toAdd += ' ';
  202. inputElement.value = inputElement.value.substr(0, cursor[0]) +toAdd +inputElement.value.substr(cursor[1]);
  203. inputElement.selectionStart = inputElement.selectionEnd = cursor[0] +toAdd.length;
  204. autoComplete(inputElement);
  205. inputElement.focus();
  206. break;
  207. }
  208. target = target.parentElement;
  209. }
  210. }
  211. }
  212. });
  213. }