var UserInterface = function(terminal) { this.terminal = terminal; this.view = document.createElement('div'); this.view.style.whiteSpace = 'pre-wrap'; this.view.style.wordWrap = 'break-word'; this.view.style.wordBreak = 'break-all'; this.code = document.createElement('code'); this.completion = document.createElement('span'); this.completion.style.color = '#d09050'; this.prompt = document.createElement('span'); this.input = document.createElement('input'); this.input.type = 'text'; this.DEFAULT_INPUT_SIZE = this.input.size; this.view.appendChild(this.code); this.code.appendChild(this.completion); this.code.appendChild(this.prompt); this.code.appendChild(this.input); this.parser = new Parser(); this.inputHistory = []; this.inputHistoryIndex = this.inputHistory.length; var self = this; this.splitPath = function(path) { var slash = path.lastIndexOf('/'); if (slash == -1) { return [ '', path ]; } else if (path.length > 1 && slash == path.length - 1) { return [ path, '' ]; } else if (slash == 0) { return [ '/', path.substr(1) ]; } else { return [ path.substr(0, slash), path.substr(slash + 1) ]; } }; this.headCompletionCallback = function(packet, args) { if (packet.opcode == 'StringList') { self.parser.setLine(self.input.value); self.parser.parse(); var elements = self.parser.tokens; var originalElements = self.parser.originalTokens; var length = 0; var index; if (originalElements.length < 1) { index = -1; } else { for (index = 0; index < originalElements.length; index++) { if (length + originalElements[index].length >= self.input.selectionStart) { break; } length += originalElements[index].length; } } var word = ''; if (index >= 0 && index < elements.length) { word = elements[index]; } var list = args.concat(self.createCompletionList(packet.list, word)); if (list.length < 2) { if (index >= 0) { if (index < 1) { originalElements[index] = list[0]; } else { originalElements[index] = ' ' + list[0]; } self.input.value = originalElements.join('') + ' '; self.completion.textContent = ''; } } else { self.completion.textContent = list.join(' ') + '\n'; self.scrollToBottom(); } } else { self.println(packet.opcode); } }; this.completionCallback = function(packet, args) { var list = []; var i; if (packet.opcode == 'FileException') { if (packet.type == 'IS_FILE') { list.push({ name: args[1], type: 'FILE' }); return; } else { self.completion.textContent = packet.type; return; } } else if (packet.opcode == 'FileList') { for (i = 0; i < packet.list.length; i++) { if (packet.list[i].name.indexOf(args[1]) == 0) { list.push(packet.list[i]); } } } if (list.length < 1) { self.completion.textContent = ''; } else if (list.length < 2) { self.parser.setLine(self.input.value); self.parser.parse(); var elements = self.parser.tokens; var originalElements = self.parser.originalTokens; var length = 0; var index; if (originalElements.length < 1) { index = -1; } else { for (index = 0; index < originalElements.length; index++) { if (length + originalElements[index].length >= self.input.selectionStart) { break; } length += originalElements[index].length; } } if (index >= 0) { var filename; if (args[0].length < 1) { filename = list[0].name; } else { if (args[0] == '/') { filename = '/' + list[0].name; } else { if (args[0].charAt(args[0].length - 1) == '/') { filename = args[0] + list[0].name; } else { filename = args[0] + '/' + list[0].name; } } } if (filename.indexOf(' ') >= 0) { filename = '"' + filename + '"'; } originalElements[index] = ' ' + filename; if (list[0].type == 'DIRECTORY') { self.input.value = originalElements.join('') + '/'; } else { self.input.value = originalElements.join('') + ' '; } self.completion.textContent = ''; if (self.input.value.length < self.DEFAULT_INPUT_SIZE) { self.input.size = self.DEFAULT_INPUT_SIZE; } else { self.input.size = self.input.value.length; } } } else { var names = []; for (i = 0; i < list.length; i++) { names.push(list[i].name); } self.completion.textContent = names.join(' ') + '\n'; self.scrollToBottom(); } }; this.handleInput = function(e) { if (e.keyCode == 13) { // ENTER var value = self.input.value; self.input.value = ''; self.addHistory(value); self.inputHistoryIndex = self.inputHistory.length; self.completion.textContent = ''; } else if (e.keyCode == 9) { // TAB e.preventDefault(); self.parser.setLine(self.input.value); self.parser.parse(); var elements = self.parser.tokens; var originalElements = self.parser.originalTokens; var length = 0; var index; if (originalElements.length < 1) { index = -1; } else { for (index = 0; index < originalElements.length; index++) { if (length + originalElements[index].length >= self.input.selectionStart) { break; } length += originalElements[index].length; } } var word = ''; if (index >= 0 && index < elements.length) { word = elements[index]; } if (index < 1) { var list = self.createCompletionList(self.completionListHead, word); self.terminal.api.commandList(self.headCompletionCallback, list); } else { var sp = self.splitPath(word); var absolutePath = self.terminal.session.toAbsolutePath(sp[0]); self.terminal.fileAPI.list(self.completionCallback, sp, absolutePath); } } else if (e.keyCode == 38) { // UP if (self.inputHistoryIndex < 0) { self.input.value = ''; } else { self.inputHistoryIndex--; if (self.inputHistoryIndex < 0) { self.input.value = ''; } else { self.input.value = self.inputHistory[self.inputHistoryIndex]; } } } else if (e.keyCode == 40) { // DOWN if (self.inputHistoryIndex >= self.inputHistory.length) { self.input.value = ''; } else { self.inputHistoryIndex++; if (self.inputHistoryIndex >= self.inputHistory.length) { self.input.value = ''; } else { self.input.value = self.inputHistory[self.inputHistoryIndex]; } } } if (self.input.value.length < self.DEFAULT_INPUT_SIZE) { self.input.size = self.DEFAULT_INPUT_SIZE; } else { self.input.size = self.input.value.length; } if (e.keyCode == 13) { // ENTER self.input.onkeydown = null; self.inputCallback(value); } }; this.handleTemporalInput = function(e) { if (e.keyCode == 13) { // ENTER var value = self.input.value; self.input.value = ''; } if (self.input.value.length < self.DEFAULT_INPUT_SIZE) { self.input.size = self.DEFAULT_INPUT_SIZE; } else { self.input.size = self.input.value.length; } if (e.keyCode == 13) { // ENTER self.input.onkeydown = null; self.temporalInputCallback(value); } }; this.login2 = function(line) { self.account = line; self.print(self.prompt.textContent); self.println(line); self.temporalInputCallback = self.login3; self.prompt.textContent = 'Password: '; self.input.type = 'password'; self.input.onkeydown = self.handleTemporalInput; }; this.login3 = function(line) { self.println(self.prompt.textContent); self.prompt.textContent = self.terminal.promptString; self.temporalInputCallback = null; self.input.type = 'text'; var account = self.account; self.account = null; self.inputCallback(account, line); }; }; UserInterface.prototype.addHistory = function(s) { if (s.length < 1) { return; } for (var i = this.inputHistory.length - 1; i >= 0; i--) { if (this.inputHistory[i] == s) { this.inputHistory.splice(i, 1); break; } } this.inputHistory.push(s); }; UserInterface.prototype.createCompletionList = function(list, word) { var result = []; for (var i = 0; i < list.length; i++) { if (list[i].indexOf(word) == 0) { result.push(list[i]); } } return result; }; UserInterface.prototype.scrollToBottom = function() { window.scrollTo(0, document.body.scrollHeight); }; UserInterface.prototype.clear = function() { while (this.code.firstChild != this.completion) { this.code.removeChild(this.code.firstChild); } }; UserInterface.prototype.print = function(s, color, background) { var output = document.createElement('span'); output.textContent = s; output.style.color = color; output.style.background = background; this.code.insertBefore(output, this.completion); this.scrollToBottom(); }; UserInterface.prototype.println = function(s, color, background) { this.print(s + '\n', color, background); }; UserInterface.prototype.append = function(node) { this.code.insertBefore(node, this.prompt); this.scrollToBottom(); }; UserInterface.prototype.readLine = function(callback, headList) { this.inputCallback = callback; this.completionListHead = headList; this.input.onkeydown = this.handleInput; this.input.focus(); }; UserInterface.prototype.login = function(callback) { this.terminal.command.inputCallback = null; this.inputCallback = callback; this.temporalInputCallback = this.login2; this.prompt.textContent = 'Account: '; this.input.onkeydown = this.handleTemporalInput; this.input.focus(); };