terminal.tag 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /**
  2. * # Terminal Riot Tag
  3. *
  4. * Provides a pretend commandline interface, capable of displaying output from a
  5. * "Shell" javascript object.
  6. *
  7. * ## Defaults
  8. *
  9. * The default prompt is `'$'`. There is no default the welcome message. Until a
  10. * shell is added, pressing enter will simply move to the next prompt.
  11. *
  12. * ## Usage
  13. *
  14. * <terminal shell='jsobject' welcome='text' prompt='text'></terminal>
  15. *
  16. * <script src='riot+compiler.min.js'></script>
  17. * <script src='he.js'></script>
  18. * <script src='shell.js'></script>
  19. * <script src='terminal.tag' type='riot/tag'></script>
  20. * <script>riot.mount('terminal')</script>
  21. *
  22. * ## Dependencies
  23. *
  24. * - riot.js (http://riotjs.com/)
  25. * - he.js (https://github.com/mathiasbynens/he) for HTML Entity conversion.
  26. *
  27. * ## Making a Shell javascript object
  28. *
  29. * Shell objects must be defined before the tag is mounted. Shells can keep
  30. * prompt character and welcome message settings, and must define a `process()`
  31. * function. Here is the structure of a minimumal shell that does nothing:
  32. *
  33. * // Contents of shell.js
  34. * function() shell() {
  35. * this.prompt = ''
  36. * this.welcome = ''
  37. * this.process = function(input) {
  38. * return ''
  39. * }
  40. * }
  41. * var myshell = new shell()
  42. *
  43. * // Shell object used by Terminal Riot Tag
  44. * <terminal shell='myshell'></terminal>
  45. */
  46. <terminal>
  47. <history welcome={ welcome } />
  48. <commandline prompt={ prompt } />
  49. // Take defaults from shell, otherwise take from `<terminal>` options.
  50. var shell = { 'process': function() { return ''; } }
  51. this.shell = window[opts.shell] ? window[opts.shell] : shell
  52. this.welcome = this.shell.welcome ? this.shell.welcome : opts.welcome
  53. this.prompt = this.shell.prompt ? this.shell.prompt : opts.prompt
  54. /**
  55. * How to process a command:
  56. * - Make the input safe by transforming html entities.
  57. * - Keep the last command in history as we go to the next line.
  58. * - Append the shell output, and update the prompt as required.
  59. */
  60. process(prompt, input) {
  61. var input = he.encode(input)
  62. var output = prompt + ' ' + input + '\n'
  63. output += this.shell.process(input)
  64. this.tags.history.add(output)
  65. this.tags.commandline.setprompt(this.shell.prompt)
  66. this.tags.commandline.command.value = ''
  67. }
  68. </terminal>
  69. <commandline>
  70. <form autocomplete='off' onsubmit={ process }>
  71. <raw name='lhs' content={ prompt } /><input type='text' name='command' />
  72. </form>
  73. <style>
  74. input[name='command'] {
  75. background: transparent;
  76. border: none; outline: none;
  77. padding: 0; margin: 0;
  78. width: 90%;
  79. }
  80. </style>
  81. this.on('mount', function() {
  82. document.getElementsByName('command')[0].focus()
  83. })
  84. /**
  85. * Set the prompt to any truthy value.
  86. *
  87. * Note about `lhs.write()` check:
  88. * `setprompt()` fires at `<terminal>` setup, before `lhs.write()` exists.
  89. * At that point, `lhs` is just initialised with its `content` option.
  90. * After that, `lhs.write()` is called for all subsequent prompt changes.
  91. */
  92. setprompt(value) {
  93. if (value) {
  94. if (typeof this.tags.lhs.write != 'undefined') {
  95. this.tags.lhs.write(value)
  96. }
  97. this.update({ 'prompt': value })
  98. }
  99. }
  100. process(e) {
  101. this.parent.process(this.prompt, this.command.value)
  102. }
  103. this.prompt = '$ '
  104. this.setprompt(opts.prompt)
  105. </commandline>
  106. <history>
  107. <div each={ hist }>
  108. <raw content={ content } />
  109. </div>
  110. /**
  111. * Append any truthy value to command history
  112. *
  113. * Replaces all newline characters with HTML breaks.
  114. */
  115. add(output) {
  116. if (output) {
  117. output = output.replace(/(?:\r\n|\r|\n)/g, '<br />');
  118. this.hist.push({ 'content': output })
  119. this.update()
  120. }
  121. }
  122. this.hist = []
  123. this.add(opts.welcome)
  124. </history>
  125. <raw>
  126. <span></span>
  127. // Initialise contents from tag options, but expose `write()` for updating.
  128. write(value) {
  129. this.root.innerHTML = value
  130. }
  131. this.write(opts.content)
  132. </raw>