/**
* # Terminal Riot Tag
*
* Provides a pretend commandline interface, capable of displaying output from a
* "shell" javascript class. Multiple terminal tags can be used on a page, using
* the same shell class or different ones - each terminal will work
* independently.
*
* ## Defaults
*
* The default prompt is `'$'`. There is no default the welcome message. The
* default shell performs no actions: pressing enter will simply move the cursor
* to the next prompt line.
*
* ## Usage
*
*
*
*
*
*
*
*
*
* ## Dependencies
*
* - riot.js (http://riotjs.com/)
* - he.js (https://github.com/mathiasbynens/he) for HTML Entity conversion.
*
* ## Making A Shell Class
*
* The shell class must be defined before the tag is mounted. Shells can keep
* prompt and welcome message settings, and must define a `process()` function.
* Here is the structure of a minimal shell that does nothing:
*
* // Contents of myshell.js
* function() myshell() {
* this.prompt = ''
* this.welcome = ''
* this.process = function(input) {
* return ''
* }
* }
*
* // shell class used by Terminal Riot Tag in index.html
*
*
* ## Special Shell Return Values
*
* The terminal tag recognises some predefined shell return values and upon
* receiving them, can perform actions other than printing output:
*
* - `'clear'`. Terminal will clear its output history.
*
*/
// Take defaults from shell, otherwise take from terminal tag options.
var shell = { 'process': function() { return ''; } }
this.shell = window[opts.shell] ? new window[opts.shell] : shell
this.welcome = this.shell.welcome ? this.shell.welcome : opts.welcome
this.prompt = this.shell.prompt ? this.shell.prompt : opts.prompt
/**
* How to process a command:
* - Make the input safe by transforming html entities.
* - Keep the last command in history as we go to the next line.
* - Based on shell response, perform special actions or append the output.
* - Update the prompt as required.
*/
process(prompt, input) {
input = he.encode(input)
var output = prompt + input + '\n'
var response = this.shell.process(input)
switch(response) {
case 'clear':
this.clear()
break
default:
this.tags.history.add(output + response)
}
this.tags.commandline.setPrompt(this.shell.prompt)
this.tags.commandline.command.value = ''
}
clear() {
this.tags.history.hist = []
this.update()
}
this.on('mount', function() {
document.getElementsByName('command')[0].focus()
})
/**
* Set the prompt to any truthy value.
*
* Note about `lhs.write()` check:
* Terminal tag setup fires `setPrompt()` before `lhs.write()` exists.
* At that point, `lhs` is initialised with just its `content` attribute.
* After that, `lhs.write()` is called to make all subsequent prompt changes.
*/
setPrompt(value) {
if (value) {
if (typeof this.tags.lhs.write != 'undefined') {
this.tags.lhs.write(value)
}
this.update({ 'prompt': value })
}
}
process(e) {
this.parent.process(this.prompt, this.command.value)
}
this.prompt = '$ '
this.setPrompt(opts.prompt)
add(output) {
if (output) {
output = this.preserveWhiteSpace(output)
this.hist.push({ 'content': output })
this.update()
}
}
/**
* Make sure whitespace displays correctly.
*
* Keep the following:
*
* - Newlines.
* - Whitespace for display. This means all spaces that are not within tags.
*/
preserveWhiteSpace(text) {
text = text.replace(/(?:\r\n|\r|\n)/g, '
')
// Search for tags or whitespace. Replace whitespace, leave the tags.
text = text.replace(/<[^<]+>|( )/g, function(match, group1) {
if (group1 == " ") { return ' ' }
return match
})
return text
}
this.hist = []
this.add(opts.welcome)
// Initialise contents from tag options, but expose `write()` for updating.
write(value) {
this.root.innerHTML = value
}
this.write(opts.content)