ace/plugins/cockpit/lib/ui/plain.js

270 lines
9.5 KiB
JavaScript

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Skywriter.
*
* The Initial Developer of the Original Code is
* Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Joe Walker (jwalker@mozilla.com)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
var editorCss = require("text!cockpit/ui/plain.css");
var dom = require("pilot/dom").dom;
dom.importCssString(editorCss);
var CliRequisition = require('cockpit/cli').CliRequisition;
var Hint = require('cockpit/cli').Hint;
var keyutil = require('pilot/keyboard/keyutil');
var plainRow = require("text!cockpit/ui/plainRow.html");
var Templater = require("pilot/domtemplate").Templater;
var canon = require("pilot/canon");
var Status = require('pilot/types').Status;
/**
* On startup we need to:
* 1. Add 3 sets of elements to the DOM for:
* - command line output
* - input hints
* - completion
* 2. Attach a set of events so the command line works
*/
exports.startup = function(data, reason) {
// TODO: We should probably cut this up into an object
var settings = data.env.settings;
var doc = document;
var win = doc.defaultView;
var cli = new CliRequisition();
// TODO: we should have a better way to specify command lines???
var input = doc.getElementById('cockpit');
if (!input) {
console.log('No element with an id of cockpit. Bailing on plain cli');
return;
}
var templates = doc.createElement('div');
templates.innerHTML = plainRow;
var row = templates.firstChild;
var completer = doc.createElement('div');
completer.className = 'cptCompletion VALID';
input.parentNode.insertBefore(completer, input);
var hinter = doc.createElement('div');
hinter.className = 'cptHints';
input.parentNode.insertBefore(hinter, input);
var output = doc.createElement('div');
output.className = 'cptOutput';
input.parentNode.insertBefore(output, input);
function resizer() {
var top, height, left, width;
if (input.getClientRects) {
var rect = input.getClientRects()[0];
top = rect.top;
height = rect.height;
left = rect.left;
width = rect.width;
}
else {
var style = win.getComputedStyle(input, null);
top = parseInt(style.getPropertyValue('top'), 10);
height = parseInt(style.getPropertyValue('height'), 10);
left = parseInt(style.getPropertyValue('left'), 10);
width = parseInt(style.getPropertyValue('width'), 10);
}
completer.style.top = top + 'px';
completer.style.height = height + 'px';
completer.style.left = left + 'px';
completer.style.width = width + 'px';
hinter.style.bottom = (win.innerHeight - top) + 'px';
hinter.style.left = (left + 30) + 'px';
output.style.bottom = (win.innerHeight - top) + 'px';
output.style.left = left + 'px';
output.style.width = width + 'px';
}
win.addEventListener('resize', resizer.bind(this), true);
resizer();
// TODO: be less brutal in how we update this
output.innerHTML = '';
canon.addEventListener('output', function(ev) {
ev.requests.forEach(function(request) {
request.outputs.forEach(function(out) {
if (typeof out === 'string') {
output.appendChild(doc.createTextNode(out));
} else {
output.appendChild(out);
}
}, this);
}, this);
}.bind(this));
var showHint = settings.getSetting('showHint');
function hintShower() {
if (showHint.get()) {
hinter.style.display = 'block';
}
else {
hinter.style.display = 'none';
}
}
hintShower();
showHint.addEventListener('change', hintShower.bind(this));
var outputHeight = settings.getSetting('outputHeight');
/*
// All this does is to kill TABs normal use. I wonder if we can train
// people to use right arrow? Probably not? but ...
keyutil.addKeyDownListener(input, function(ev) {
// env.commandLine = this;
// var handled = keyboardManager.processKeyEvent(ev, this, {
// isCommandLine: true, isKeyUp: false
// });
if (ev.keyCode === keyutil.KeyHelper.KEY.TAB) {
return true;
}
//return handled;
}.bind(this));
*/
var NO_HINT = new Hint(Status.VALID, '', 0, 0);
var hints = [];
var worst;
input.addEventListener('keyup', function(ev) {
/*
var handled = keyboardManager.processKeyEvent(ev, this, {
isCommandLine: true, isKeyUp: true
});
*/
if (ev.keyCode === keyutil.KeyHelper.KEY.RETURN) {
cli.exec();
input.value = '';
} else {
cli.update({
typed: input.value,
cursor: {
start: input.selectionStart,
end: input.selectionEnd
}
});
completer.classList.remove(Status.VALID.toString());
completer.classList.remove(Status.INCOMPLETE.toString());
completer.classList.remove(Status.INVALID.toString());
// TODO: borked implementation?
// dom.removeCssClass(completer, Status.VALID.toString());
// dom.removeCssClass(completer, Status.INCOMPLETE.toString());
// dom.removeCssClass(completer, Status.INVALID.toString());
hints = cli.getHints();
// Create a marked up version of the input
var highlightedInput = '';
if (input.value.length > 0) {
// 'scores' is an array which tells us what chars are errors
// Initialize with everything VALID
var scores = input.value.split('').map(function(char) {
return Status.VALID;
});
// For all chars in all hints, check and upgrade the score
hints.forEach(function(hint) {
for (var i = hint.start; i <= hint.end; i++) {
if (hint.status > scores[i]) {
scores[i] = hint.status;
}
}
}, this);
// Create markup
var i = 0;
var lastStatus = -1;
while (true) {
if (lastStatus !== scores[i]) {
highlightedInput += '<span class=' + scores[i].toString() + '>';
lastStatus = scores[i];
}
highlightedInput += input.value[i];
i++;
if (i === input.value.length) {
highlightedInput += '</span>';
break;
}
if (lastStatus !== scores[i]) {
highlightedInput += '</span>';
}
}
}
worst = (hints.length > 0) ? hints[0] : NO_HINT;
var message = worst.message;
if (worst.predictions && worst.predictions.length > 0) {
message += ': [ ';
worst.predictions.forEach(function(prediction) {
if (prediction.name) {
message += prediction.name + ' | ';
}
else {
message += prediction + ' | ';
}
}, this);
message = message.replace(/\| $/, ']');
var completion = worst.predictions[0];
completion = completion.name ? completion.name : completion;
completer.innerHTML = highlightedInput + ' &nbsp;-&gt; ' + completion;
}
else {
completer.innerHTML = highlightedInput;
}
hinter.innerHTML = message;
completer.classList.add(worst.status.toString());
// dom.addCssClass(input, worst.status.toString());
}
// return handled;
}.bind(this), true);
};
});