var commandHooks = null; var commandHooksRequest = null; var commandMenuGeneration = 0; var suppressCommandMenuKeypress = false; var serverCommandFallbacks = [ {command:'defcon',args:'[]',description:'Set or show Ergo emergency restrictions',insertion:'/quote DEFCON',serverCommand:true}, {command:'deoper',args:'',description:'Remove IRC operator privileges',insertion:'/quote DEOPER',serverCommand:true}, {command:'dline',args:'[] / [] || LIST',description:'Add or list IP/network bans',insertion:'/quote DLINE',serverCommand:true}, {command:'kline',args:'[] [] || LIST',description:'Add or list client-mask bans',insertion:'/quote KLINE',serverCommand:true}, {command:'uban',args:'ADD|DEL|LIST|INFO ',description:'Manage unified bans',insertion:'/quote UBAN',serverCommand:true}, {command:'undline',args:'/',description:'Remove an IP/network ban',insertion:'/quote UNDLINE',serverCommand:true}, {command:'unkline',args:'',description:'Remove a client-mask ban',insertion:'/quote UNKLINE',serverCommand:true} ]; var mergeCommandHooks = function(hooks) { var unique = Object.create(null); var hasOwn = Object.prototype.hasOwnProperty; hooks.forEach(function(hook) { var key = String(hook.command || '').toLowerCase(); if (key && (!hasOwn.call(unique, key) || hook.priority > unique[key].priority)) { unique[key] = Object.assign({}, hook, {command:key}); } }); serverCommandFallbacks.forEach(function(hook) { if (!hasOwn.call(unique, hook.command)) unique[hook.command] = hook; }); return Object.keys(unique).map(function(key){return unique[key];}).sort(function(left,right){return left.command.localeCompare(right.command);}); }; $scope.commandMenuOpen = false; $scope.commandMenuLoading = false; $scope.commandMenuMatches = []; $scope.commandMenuIndex = 0; var closeCommandMenu = function() { $scope.commandMenuOpen = false; $scope.commandMenuLoading = false; $scope.commandMenuMatches = []; $scope.commandMenuIndex = 0; }; var updateCommandMenu = function() { var match = /^\/([^\s]*)$/.exec($scope.command || ''); if (!match) { closeCommandMenu(); return; } $scope.commandMenuOpen = true; $scope.commandMenuIndex = 0; if (commandHooks === null) { $scope.commandMenuLoading = true; if (commandHooksRequest === null) { var requestGeneration = commandMenuGeneration; commandHooksRequest = connection.requestCommandHooks().then(function(hooks) { if (requestGeneration !== commandMenuGeneration) { return; } commandHooks = mergeCommandHooks(hooks); commandHooksRequest = null; updateCommandMenu(); }, function() { if (requestGeneration !== commandMenuGeneration) { return; } commandHooks = mergeCommandHooks([]); commandHooksRequest = null; updateCommandMenu(); }); } return; } var prefix = match[1].toLowerCase(); var activeBuffer = models.getActiveBuffer(); var isIrcBuffer = activeBuffer && activeBuffer.plugin === 'irc'; $scope.commandMenuLoading = false; $scope.commandMenuMatches = commandHooks.filter(function(hook) { return (!hook.serverCommand || isIrcBuffer) && hook.command.indexOf(prefix) === 0; }); }; $scope.selectCommand = function(item) { var suffix = item.args ? ' ' : ''; $scope.command = (item.insertion || ('/' + item.command.toLowerCase())) + suffix; closeCommandMenu(); setTimeout(function() { var inputNode = $scope.getInputNode(); inputNode.focus(); inputNode.setSelectionRange($scope.command.length, $scope.command.length); }, 0); }; var scrollCommandMenuSelection = function() { setTimeout(function() { var selected = document.getElementById( $scope.inputId + '-command-' + $scope.commandMenuIndex ); if (selected) { selected.scrollIntoView({block: 'nearest'}); } }, 0); }; $scope.handleCommandMenuKey = function(event, code, inputNode) { if (suppressCommandMenuKeypress && event.type === 'keypress' && (code === 9 || code === 13)) { event.preventDefault(); return true; } if (!$scope.commandMenuOpen || event.type !== 'keydown' || document.activeElement !== inputNode || event.altKey || event.ctrlKey || event.metaKey) { return false; } var count = $scope.commandMenuMatches.length; if ((code === 38 || code === 40) && count > 0) { var direction = code === 38 ? -1 : 1; $scope.commandMenuIndex = ($scope.commandMenuIndex + direction + count) % count; scrollCommandMenuSelection(); event.preventDefault(); return true; } if ((code === 9 || code === 13) && count > 0 && !event.shiftKey) { $scope.selectCommand($scope.commandMenuMatches[$scope.commandMenuIndex]); suppressCommandMenuKeypress = true; setTimeout(function() { suppressCommandMenuKeypress = false; }, 0); event.preventDefault(); return true; } if (code === 27) { closeCommandMenu(); event.preventDefault(); return true; } return false; }; var removeCommandMenuDisconnect = $rootScope.$on('relayDisconnect', function() { commandMenuGeneration++; commandHooks = null; commandHooksRequest = null; closeCommandMenu(); }); $scope.$on('$destroy', removeCommandMenuDisconnect);