/** * # 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)