/**
* # 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 `'$ '`.
* The default welcome message is empty.
* 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/)
*
* ## Making A Shell Class
*
* A shell class must be defined before the tag is mounted. Shells can keep
* prompt and welcome message settings, and should listen to the `'cmd_entered'`
* event to process input from the `commandline` tag.
*
* Here is the structure of a minimal shell that does nothing:
*
* // Contents of myshell.js
* function myshellclass(events) {
* this.prompt = ''
* this.welcome = ''
* events.on('cmd_entered', function(input) {
* // Do nothing
* })
* }
*
* The shell can trigger events available on the `display` and `commandline`
* tags to make things happen:
*
* events.trigger('disp_add', text) // Append `text` to the display
* events.trigger('disp_set', text) // Display only `text`
* events.trigger('disp_clear') // Clear the display
* events.trigger('disp_hide') // Save the display, then clear it
* events.trigger('disp_restore') // Restore the saved display
* events.trigger('prompt_set', text) // Change the command prompt to `text`
* events.trigger('prompt_hide') // Hide the command prompt
* events.trigger('prompt_show') // Show the command prompt, if hidden
*
*/
/**
* Create a new shell with the class name given to the terminal tag.
* The terminal tag object passes events between the shell and the other tags.
*/
var shell = window[opts.shell] ? new window[opts.shell](this) : {}
this.welcome = shell.welcome || opts.welcome
this.prompt = shell.prompt || opts.prompt
var ev = opts.events
var self = this
this.output = []
this.on('mount', function() {
this.add(opts.welcome)
})
ev.on('disp_add', function(text) {
self.add(text)
})
ev.on('disp_set', function(text) {
if (text) {
self.clear()
self.add(text)
}
})
ev.on('disp_clear', function() {
self.clear()
})
ev.on('disp_hide', function() {
self.saved = self.output.splice(0, self.output.length)
self.clear()
})
ev.on('disp_restore', function() {
if (self.saved.length > 0) {
self.clear()
self.output = self.saved.splice(0, self.saved.length)
self.update()
}
})
add(text) {
if (text) {
text = text.replace(/\r\n|\r|\n/g, '
')
this.output.push({ 'content': text })
this.update()
}
}
clear() {
this.output.length = 0
this.update()
}
var ev = opts.events
var self = this
this.prompt = opts.prompt || '$ '
this.visible = true
this.on('mount', function() {
document.getElementsByName('command')[0].focus()
})
ev.on('prompt_set', function(value) {
self.prompt = value
self.tags.lhs.write(value)
})
ev.on('prompt_hide', function() {
self.update({ visible: false })
})
ev.on('prompt_show', function() {
self.update({ visible: true })
})
process() {
var prompt = this.visible ? this.prompt : ''
var command = this.encode(this.command.value)
this.command.value = ''
ev.trigger('disp_add', prompt + command + '\n')
ev.trigger('cmd_entered', command)
}
encode(text) {
return text.replace(/&/g,'&').replace(//g,'>')
}
// Set the initial html using the `content` option.
this.on('mount', function() {
this.write(opts.content)
})
// Call `write()` manually to update the html.
write(text) {
this.root.innerHTML = text
}