/** * @param {string} servicename * @return {Element} **/ function createSlashAutocompleteHeader(servicename) { var lh = document.createElement("lh"); lh.textContent = servicename; lh.className = R.klass.commands.header; return lh; } /** * @param {Command|{names: Array!, desc: string!, usage: string!,category: string!, exec: Function!}|string} cmd * @param {Element=} imgSpan * @return {Element} **/ function createSlashAutocompleteDom(cmd, imgSpan) { var li = document.createElement("li") ,name = document.createElement("span") name.className = R.klass.commands.name; if (typeof cmd === "string") { if (imgSpan) { li.appendChild(imgSpan); } name.textContent = cmd; li.appendChild(name); } else { var usage = document.createElement("span") ,desc = document.createElement("span"); name.textContent = cmd.name; usage.textContent = cmd.usage; desc.textContent = cmd.desc; usage.className = R.klass.commands.usage; desc.className = R.klass.commands.desc; li.appendChild(name); li.appendChild(usage); li.appendChild(desc); } li.dataset.input = name.textContent; li.className = R.klass.commands.item; return li; } function autoComplete(inputDom) { var now = Date.now(), slashDom = document.getElementById(R.id.message.slashComplete); if (slashDom.dataset.cursor) delete slashDom.dataset.cursor; var /** @type {Array!, desc: string!, usage: string!, category: string!, exec: Function!}|{name: string, provider: string, emojiSpan: Element}>} */ commands = [], /** @const @type {string} */ input = inputDom.value; if (inputDom.selectionStart === inputDom.selectionEnd && inputDom.selectionStart) { var start = inputDom.selectionStart, end = inputDom.selectionEnd; for (; start && input[start -1] !== ' '; start--) {} for (var valueLen = input.length; end < valueLen && input[end] !== ' '; end++) {} if (start !== end && end -start -1 > 0) { if (input[start] === '#') { var channels = SELECTED_CONTEXT.getChatContext().channels, inputStr = input.substr(start +1, end -start -1); for (var i in channels) { if (channels[i].name.length >= inputStr.length && channels[i].name.substr(0, inputStr.length) === inputStr) commands.push(channels[i]); } } else if (input[start] === '@') { var users = SELECTED_ROOM instanceof PrivateMessageRoom ? SELECTED_CONTEXT.getChatContext().users : SELECTED_ROOM.users, inputStr = input.substr(start +1, end -start -1); for (var i in users) { var userName = users[i].getName(); if (userName.length >= inputStr.length && userName.substr(0, inputStr.length) === inputStr) commands.push(users[i]); } } else if (input[start] === ':' && window['searchEmojis']) { var inputCmd = input.substr(start +1, end -start -1), emojiList = window['searchEmojis'](inputCmd); for (var emojiCode in emojiList) { var domEmoji = window['makeEmoji'](emojiCode, false), domParent = document.createElement("span"); domParent.appendChild(domEmoji); domParent.className = R.klass.emoji.small; commands.push({ name: ':' +emojiCode +':', emojiSpan: domParent, provider: CURRENT_EMOJI_PROVIDER.name }); } for (var i in SELECTED_CONTEXT.getChatContext().emojis.data) { if (i.length >= inputCmd.length && i.substr(0, inputCmd.length) === inputCmd) { var emojiSpan = document.createElement("span"); emojiSpan.className = R.klass.emoji.small; emojiSpan.appendChild(makeEmojiDom(i)); commands.push({ name: ':' +i +':', emojiSpan: emojiSpan, provider: "custom" }); } } } if (commands.length) slashDom.dataset.cursor = JSON.stringify([start, end]); } } if (!commands.length && input[0] === '/') { var endCmd = input.indexOf(' '), inputFinished = endCmd !== -1; endCmd = endCmd === -1 ? input.length : endCmd; var inputCmd = input.substr(0, endCmd); if (inputFinished) { var currentClientCmd = CLIENT_COMMANDS.getCommand(inputCmd); if (currentClientCmd) commands.push(currentClientCmd); } else { commands = CLIENT_COMMANDS.getCommandsStartingWith(inputCmd); slashDom.dataset.cursor = JSON.stringify([0, inputDom.selectionEnd]); } var availableCommands = (SELECTED_CONTEXT ? SELECTED_CONTEXT.getChatContext().commands.data : {}); for (var currentCmdId in availableCommands) { var currentCmd = availableCommands[currentCmdId]; if ((!inputFinished && currentCmd.name.substr(0, endCmd) === inputCmd) || (inputFinished && currentCmd.name === inputCmd)) commands.push(currentCmd); } commands.sort(function(a, b) { return a.category.localeCompare(b.category) || a.name.localeCompare(b.name); }); } slashDom.textContent = ''; if (commands.length) { var slashFrag = document.createDocumentFragment(), prevService; for (var i =0, nbCmd = commands.length; i < nbCmd; i++) { var command = commands[i]; if (command instanceof Chatter) { if (!prevService) { prevService = true; slashFrag.appendChild(createSlashAutocompleteHeader(locale.members)); } var span = document.createElement("span"); span.className = R.klass.commands.userIcon; span.style.backgroundImage = "url(\"" +command.getSmallIcon() +"\")"; slashFrag.appendChild(createSlashAutocompleteDom('@' +command.getName(), span)); } else if (command instanceof Room) { if (!prevService) { prevService = true; slashFrag.appendChild(createSlashAutocompleteHeader(locale.channels)); } slashFrag.appendChild(createSlashAutocompleteDom('#' +command.name)); } else if (command.emojiSpan) { if (prevService !== command.provider) { prevService = command.provider; slashFrag.appendChild(createSlashAutocompleteHeader(command.provider)); } slashFrag.appendChild(createSlashAutocompleteDom(command.name, command.emojiSpan)); } else { if (prevService !== command.category) { prevService = command.category; slashFrag.appendChild(createSlashAutocompleteHeader(command.category)); } slashFrag.appendChild(createSlashAutocompleteDom(command)); } } slashDom.appendChild(slashFrag); } } /** * @param {string} input * @param {boolean=} skipCommand * @return {boolean} true on recognized input **/ function onTextEntered(input, skipCommand) { var success = true; if (EDITING) { editMsg(SELECTED_ROOM, input, EDITING); return true; } if (input[0] === '/' && skipCommand !== true) { var endCmd = input.indexOf(' '), cmd = input.substr(0, endCmd === -1 ? undefined : endCmd), args = endCmd === -1 ? "" : input.substr(endCmd), ctx = SELECTED_CONTEXT, cliCmdObject = CLIENT_COMMANDS.getCommand(cmd); if (cliCmdObject) { args = args.trim(); cliCmdObject.exec(ctx, SELECTED_ROOM, args); return true; } else if (ctx) { var cmdObject = ctx.getChatContext().commands.data[cmd]; if (cmdObject) { if (cmdObject.name === "/me") { displayTmpMessage(SELECTED_ROOM, args, true); } else if (cmdObject.name === "/msg") { args = args.trim(); var argParts = (/(\S+)\s+(.*)/).exec(args), chan = SELECTED_CONTEXT.getRoom(argParts[1]); if (chan) displayTmpMessage(chan, argParts[2], false); } doCommand(SELECTED_ROOM, cmdObject, args); return true; } } return false; } var tmpMsg = displayTmpMessage(SELECTED_ROOM, input, false); sendMsg(SELECTED_ROOM, input, tmpMsg, REPLYING_TO); return true; } function focusInput() { document.getElementById(R.id.message.input).focus(); } function updateInputRowCount(input) { var nbRows = 1; for (var i =0, nbChars = input.value.length; i < nbChars; i++) if (input.value[i] === '\n') nbRows++; input.rows = Math.min(5, nbRows); while (input.rows < 5 && input.scrollHeight > input.clientHeight) // FIXME very very ugly input.rows++; } function initMsgInput() { var lastKeyDown = 0, input = document.getElementById(R.id.message.input); input.addEventListener('input', function() { if (SELECTED_ROOM) { var now = Date.now(); if (lastKeyDown + 3000 < now && (SELECTED_CONTEXT.getChatContext().self.presence || (SELECTED_ROOM instanceof PrivateMessageRoom))) { sendTyping(SELECTED_ROOM); lastKeyDown = now; } autoComplete(this); updateInputRowCount(this); } }); input.addEventListener("keydown", function(e) { var /** @const */ TAB_KEY = 9, /** @const */ ENTER_KEY = 13; if (e.keyCode === TAB_KEY) { e.preventDefault(); // FIXME cycle though propositions return false; } else if (e.keyCode === ENTER_KEY) { e.preventDefault(); if (e.shiftKey || e.altKey || e.ctrlKey) { var selectionEnd = this.selectionEnd, selectionStart = this.selectionStart; this.value = this.value.substr(0, selectionStart) +'\n' +this.value.substr(selectionEnd); updateInputRowCount(this); this.selectionStart = this.selectionEnd = selectionStart +1; } else { onMsgFormSubmit(); } return false; } }); document.getElementById(R.id.message.slashComplete).addEventListener("click", function(e) { if (SELECTED_ROOM) { var target = e.target; var cursor = this.dataset.cursor; if (cursor) { cursor = JSON.parse(cursor); while (target && target !== this) { if (target.dataset.input) { var inputElement = document.getElementById(R.id.message.input), toAdd = target.dataset.input; if (inputElement.value.length <= cursor[1]) toAdd += ' '; inputElement.value = inputElement.value.substr(0, cursor[0]) +toAdd +inputElement.value.substr(cursor[1]); inputElement.selectionStart = inputElement.selectionEnd = cursor[0] +toAdd.length; autoComplete(inputElement); inputElement.focus(); break; } target = target.parentElement; } } } }); }