jsketcher/web/app/ui/terminal.js
2016-11-25 01:35:39 -08:00

111 lines
No EOL
3.3 KiB
JavaScript

export function Terminal(win, commandProcessor, variantsSupplier) {
this.win = win;
this.out = win.root.find('.terminal-output');
const input = win.root.find('.terminal-input input');
this.input = input;
win.onShowCallback = function() {
input.focus();
};
this.makeAlwaysFocusable();
this.history = [];
this.historyPointer = 0;
const setHistory = () => {
if (this.history.length == 0) return;
input.val(this.history[this.historyPointer]);
};
input.keydown((e) => {
function consumeEvent() {
e.preventDefault();
e.stopPropagation();
}
if (e.keyCode == 9) {
const text = input.val();
let variants = variantsSupplier().filter(v => v.startsWith(text));
variants.sort();
if (variants.length == 0) {
} else {
const shared = sharedStartOfSortedArray(variants);
if (shared.length != text.length) {
input.val(shared);
} else {
let autocompleteArea = this.out.find('.autocomplete-area');
if (autocompleteArea.length == 0) {
autocompleteArea = $('<div>', {'class': 'terminal-commandText autocomplete-area'});
this.out.append(autocompleteArea);
}
let more = '';
const limit = 20;
if (variants.length > limit) {
more = '... and ' + (variants.length - limit) + ' more';
variants = variants.slice(0,limit);
}
autocompleteArea.text(variants.join(' ') + more);
this.scrollToTheEnd();
}
}
consumeEvent();
} else if (e.keyCode == 38) {
this.historyPointer = Math.max(this.historyPointer - 1, 0);
setHistory();
consumeEvent();
} else if (e.keyCode == 40) {
if (this.historyPointer != this.history.length) {
this.historyPointer = Math.min(this.historyPointer + 1, this.history.length - 1);
setHistory();
}
consumeEvent();
}
});
input.keyup((e) => {
if(e.keyCode == 13) {
const command = input.val();
this.out.find('.autocomplete-area').remove();
input.val('');
this.out.append($('<div>', {text: '> '+command, 'class': 'terminal-commandText'}));
if (command != null && command.trim().length != 0) {
const result = commandProcessor(command);
this.print(result);
if (this.history.length == 0 || command != this.history[this.history.length - 1]) {
this.history.push(command);
}
this.historyPointer = this.history.length;
}
this.scrollToTheEnd();
}
});
}
Terminal.prototype.makeAlwaysFocusable = function() {
let wasMove = false;
this.win.root.mousedown(() => {
wasMove = false;
return true;
});
this.win.root.mousemove(() => {
wasMove = true;
return true;
});
this.win.root.mouseup(() => {
if (!wasMove) this.input.focus();
return true;
});
};
Terminal.prototype.scrollToTheEnd = function() {
this.out.parent().scrollTop(this.out.height());
};
Terminal.prototype.print = function(text) {
this.out.append($('<div>', {text, 'class': 'terminal-commandResult'}));
this.scrollToTheEnd();
};
function sharedStartOfSortedArray(array){
var a1= array[0], a2= array[array.length-1], L= a1.length, i= 0;
while(i<L && a1.charAt(i)=== a2.charAt(i)) i++;
return a1.substring(0, i);
}