|
| 1 | +<!doctype html> |
| 2 | +<html> |
| 3 | +<head> |
| 4 | + <meta charset="utf-8"> |
| 5 | + <meta http-equiv="X-UA-Compatible" content="chrome=1"> |
| 6 | + <title>Josh.js by sdether</title> |
| 7 | + <link rel="stylesheet" href="stylesheets/styles.css"> |
| 8 | + <link rel="stylesheet" href="stylesheets/pygment_trac.css"> |
| 9 | + <script src="javascripts/respond.js"></script> |
| 10 | + <!--[if lt IE 9]> |
| 11 | + <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> |
| 12 | + <![endif]--> |
| 13 | + <!--[if lt IE 8]> |
| 14 | + <link rel="stylesheet" href="stylesheets/ie.css"> |
| 15 | + <![endif]--> |
| 16 | + <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> |
| 17 | + <link href='http://fonts.googleapis.com/css?family=Source+Code+Pro' rel='stylesheet' type='text/css'> |
| 18 | + <link rel="stylesheet" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css"> |
| 19 | + <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script> |
| 20 | + <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js"></script> |
| 21 | + <script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.2/underscore-min.js"></script> |
| 22 | + <script>Josh = {Debug: true };</script> |
| 23 | + <script src="javascripts/killring.js"></script> |
| 24 | + <script src="javascripts/history.js"></script> |
| 25 | + <script src="javascripts/readline.js"></script> |
| 26 | + <script src="javascripts/shell.js"></script> |
| 27 | + <script src="javascripts/pathhandler.js"></script> |
| 28 | + <style type="text/css"> |
| 29 | + #shell-panel { |
| 30 | + height: 200px; |
| 31 | + width: 100%; |
| 32 | + background-color: #002f05; |
| 33 | + color: #00fe00; |
| 34 | + padding: 20px 20px 20px 20px; |
| 35 | + font-family: 'Source Code Pro'; |
| 36 | + overflow: scroll; |
| 37 | + overflow-x: hidden; |
| 38 | + overflow-y: scroll; |
| 39 | + border: 1px dashed #E6EBE0; |
| 40 | + }</style> |
| 41 | + <script> |
| 42 | + $(document).ready(function() { |
| 43 | + var history = new Josh.History({ key: 'josh.helloworld'}); |
| 44 | + var shell = Josh.Shell({history: history}); |
| 45 | + var promptCounter = 0; |
| 46 | + shell.onNewPrompt(function(callback) { |
| 47 | + promptCounter++; |
| 48 | + callback("[" + promptCounter + "] $"); |
| 49 | + }); |
| 50 | + shell.setCommandHandler("hello", { |
| 51 | + exec: function(cmd, args, callback) { |
| 52 | + var arg = args[0] || ''; |
| 53 | + var response = "who is this " + arg + " you are talking to?"; |
| 54 | + if(arg === 'josh') { |
| 55 | + response = 'pleased to meet you.'; |
| 56 | + } else if(arg === 'world') { |
| 57 | + response = 'world says hi.' |
| 58 | + } else if(!arg) { |
| 59 | + response = 'who are you saying hello to?'; |
| 60 | + } |
| 61 | + callback(response); |
| 62 | + }, |
| 63 | + completion: function(cmd, arg, line, callback) { |
| 64 | + callback(shell.bestMatch(arg, ['world', 'josh'])) |
| 65 | + } |
| 66 | + }); |
| 67 | + shell.activate(); |
| 68 | + });</script> |
| 69 | +</head> |
| 70 | +<body> |
| 71 | +<div id="header"> |
| 72 | + <nav> |
| 73 | + <li><a href="index.html">Back to the Documentation</a></li> |
| 74 | + </nav> |
| 75 | +</div> |
| 76 | +<div class="wrapper"> |
| 77 | + |
| 78 | + <section> |
| 79 | + <h1>Hello World Shell</h1> |
| 80 | + |
| 81 | + <p>This tutorial shows how easy it is to create the below shell window with a custom prompt and a new command |
| 82 | + <code>hello</code>.</p> |
| 83 | + |
| 84 | + <p> |
| 85 | + |
| 86 | + <div id="shell-panel"> |
| 87 | + <div>Type <code>help</code> or hit <code>TAB</code> for a list of commands.</div> |
| 88 | + <div id="shell-view"></div> |
| 89 | + </div> |
| 90 | + </p> |
| 91 | + <h2>Creating the Shell</h2> |
| 92 | + |
| 93 | + <p>The |
| 94 | + <code>Josh.Shell</code> uses local storage to store a history of the commands that you have typed. By default this is keyed with |
| 95 | + <em>josh.history</em>. That history is available to all instances of the shell on your site. For this tutorial, we want to make sure we have our own copy, so we don't get commands from other tutorials and examples, so we need to create a history object with its own key: |
| 96 | + </p> |
| 97 | + <pre>var history = new Josh.History({ key: 'helloworld.history'});</pre> |
| 98 | + <p>Now we can create a Shell instance with that history:</p> |
| 99 | + <pre>var shell = Josh.Shell({history: history});</pre> |
| 100 | + <p>Now the shell exists but has not yet been activated.</p> |
| 101 | + |
| 102 | + <p><em>Note on how the shell attaches to its UI elements:</em> By default |
| 103 | + <code>Josh.Shell</code> expects to find a <code>div#shell-panel</code> that contains a |
| 104 | + <code>div#shell-view</code>. The former is the physical container providing the dimensions of the shell, while the latter is a div the shell will continue to append to and scroll to mimic a screen. If you want to use other div IDs (because you have multiple shells on one page), you can provide |
| 105 | + <code>shell-panel-id</code> and <code>shell-view-id</code> in the constructor.</p> |
| 106 | + |
| 107 | + <h2>Rendering the prompt</h2> |
| 108 | + |
| 109 | + <p>The default prompt for Josh is |
| 110 | + <strong>jsh$</strong>. Let's create a prompt instead that keeps track of how many times it has been shown: |
| 111 | + </p> |
| 112 | + <pre>var promptCounter = 0; |
| 113 | +shell.onNewPrompt(function(callback) { |
| 114 | + promptCounter++; |
| 115 | + callback("[" + promptCounter + "] $ "); |
| 116 | +});</pre> |
| 117 | + <p> |
| 118 | + <code>onNewPrompt</code> is called every time Josh needs to re-render the prompt. This happens usually a command is executed, but can also happen on tab completion, when a list of possible completions is shown. |
| 119 | + <code>onNewPrompt</code> expects a function that accepts a callback as its only argument. Josh will not continue until the callback has been called with an html string to display. This allows the prompt to rendered as part of an asynchronous action. For our example, we just increment the promptCounter and send back a simple string with the counter. |
| 120 | + </p> |
| 121 | + |
| 122 | + <h2>Adding a new command</h2> |
| 123 | + |
| 124 | + <p>Josh implements just three commands out of the box:</p> |
| 125 | + <ul> |
| 126 | + <li><code>help</code> - show a list of known commands</li> |
| 127 | + <li><code>history</code> - show the commands previously entered</li> |
| 128 | + <li><code>clear</code> - clear the console (i.e. remove all children from <code>div#shell-view</code></li> |
| 129 | + </ul> |
| 130 | + <p>Let's add a new command called hello with tab completion:</p> |
| 131 | + <pre>shell.setCommandHandler("hello", { |
| 132 | + exec: function(cmd, args, callback) { |
| 133 | + var arg = args[0] || ''; |
| 134 | + var response = "who is this " + arg + " you are talking to?"; |
| 135 | + if(arg === 'josh') { |
| 136 | + response = 'pleased to meet you.'; |
| 137 | + } else if(arg === 'world') { |
| 138 | + response = 'world says hi.' |
| 139 | + } else if(!arg) { |
| 140 | + response = 'who are you saying hello to?'; |
| 141 | + } |
| 142 | + callback(response); |
| 143 | + }, |
| 144 | + completion: function(cmd, arg, line, callback) { |
| 145 | + callback(shell.bestMatch(arg, ['world', 'josh'])) |
| 146 | + } |
| 147 | +});</pre> |
| 148 | + <p>To add a command, simply call <code>shell.setCommandHandler</code> and provide it at least an |
| 149 | + <code>exec</code> handler, and optionally a <code> completion</code> handler.</p> |
| 150 | + |
| 151 | + <p> |
| 152 | + <code>exec</code> expects a function that takes the name of the called command, an array of whitespace separated arguments to the command and a callback that MUST be called with an html string to output to the console. For our toy command we implement a command named |
| 153 | + <code>hello</code> which understands arguments <em>josh</em> and |
| 154 | + <em>world</em> and has alternate outputs for no arguments and unknown arguments.</p> |
| 155 | + |
| 156 | + <p> |
| 157 | + <code>completion</code> expects a function that takes the current command, the current argument being completed, the complete line (since the cursor may not be at the tail) and a callback that MUST be called either with a completion data structure. The format of this data structure is: |
| 158 | + </p> |
| 159 | + <pre>{ |
| 160 | + completion: {string to append to current argument}, |
| 161 | + suggestions: [{array of possible completions},...] |
| 162 | +}</pre> |
| 163 | + <p>Here are some expected completions:</p> |
| 164 | + <ul> |
| 165 | + <li><code>hello <TAB></code> => <code>{completion:null,suggestions:['world', 'josh']}</code></li> |
| 166 | + <li><code>hello wo<TAB></code> => <code>{completion:'rld',suggestions:['world']}</code></li> |
| 167 | + <li><code>hello x<TAB></code> => <code>{completion:'',suggestions:[]}</code></li> |
| 168 | + </ul> |
| 169 | + <p>To simplify this process of finding the partial strings and possible completions, Shell offers a method |
| 170 | + <code>bestMatch</code> which expects as input the partial to match (our arg to the completion handler) and a list of all possible completions and it will narrow down what to append to the partial and what suggestions to show. |
| 171 | + </p> |
| 172 | + |
| 173 | + <h2>Turning it on</h2> |
| 174 | + |
| 175 | + <p>Now that we've added our custom behavior to |
| 176 | + <code>Josh.Shell</code>, all we have to do is activate the shell to render the prompt and start capturing all keystrokes via readline (i.e. if you want the shell to only capture keys while the shell has focus, it is up to you to write focus and blur code to activate and deactivate the shell.) |
| 177 | + </p> |
| 178 | + <pre>shell.activate();</pre> |
| 179 | + <p>And that's all there is to getting a custom Bash-like shell in your web page.</p> |
| 180 | + </section> |
| 181 | +</div> |
| 182 | +<!--[if !IE]> |
| 183 | +<script>fixScale(document);</script><![endif]--> |
| 184 | + |
| 185 | +</body> |
| 186 | +</html> |
0 commit comments