/** * # 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/) * - he.js (https://github.com/mathiasbynens/he) for HTML Entity conversion. * * ## 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 = this.preserveWhiteSpace(text) this.output.push({ 'content': text }) this.update() } } clear() { this.output.length = 0 this.update() } preserveWhiteSpace(text) { text = text.replace(/\r\n|\r|\n/g, '
') // Search for tags or whitespace. Escape whitespace, leave tags. text = text.replace(/<[^<]+>|( )/g, function(match, group1) { if (group1 == " ") { return ' ' } return match }) return text }
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 // `write()` is called to actually update the `raw` tag's html. 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 = he.encode(this.command.value) this.command.value = '' ev.trigger('disp_add', prompt + command + '\n') ev.trigger('cmd_entered', command) }
// Initialise HTML from tag options and expose `write()` for updating. write(text) { this.root.innerHTML = text } this.write(opts.content)