├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── bin ├── base │ └── images │ │ ├── favicon.ico │ │ └── ipynblogo.png ├── custom │ ├── custom.css │ └── custom.js ├── ijs.js └── require │ └── element.js ├── dist ├── Dockerfile ├── build.sh ├── publish.sh ├── run.sh ├── shell.sh └── start.sh ├── docs ├── ClientScript.png ├── HTTPRequests.png ├── HelloWorld.png ├── HelloWorldAsync.png └── JSON.png ├── package.json ├── samples └── notebooks │ ├── Async.ipynb │ ├── Charting.ipynb │ ├── Client Script.ipynb │ ├── HTTP Extension - HTTP Client.ipynb │ ├── HTTP Extension - HTTP Server.ipynb │ ├── HTTP Requests.ipynb │ ├── Hello World.ipynb │ └── JSON Data.ipynb ├── src ├── evaluator │ ├── commands.js │ ├── dataCommands.js │ ├── displayCommands.js │ ├── error.js │ ├── extensions.js │ ├── modules.js │ └── shell.js ├── index.js ├── protocol │ ├── display.js │ ├── evaluation.js │ ├── handlers.js │ ├── messages.js │ ├── queue.js │ ├── session.js │ └── signers.js └── utils │ ├── installer.js │ └── streams.js └── tools ├── ijs.sh └── init.sh /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | docs/ 3 | node_modules/ 4 | proto/ 5 | samples/ 6 | tools/ 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ijs - Interactive JavaScript 2 | 3 | An environment for interactive authoring and executing JavaScript code using a 4 | notebook metaphor, built on top of IPython. 5 | 6 | An ijs notebook is powered by a javascript kernel written in node.js (and 7 | replaces the standard python kernel). A kernel runs behind the scenes and 8 | executes the code you write in notebooks. The notebook displays the outputs 9 | generated by your code. 10 | 11 | You can use node.js APIs, as well as the large library of node.js modules 12 | in your notebooks. You can also author client-side HTML and JavaScript to 13 | run code within the browser. JavaScript is truly universal! 14 | 15 | ![Hello World using node.js APIs and qr-image node module](docs/HelloWorld.png) 16 | 17 | ## Getting Started 18 | You can use ijs by installing it as a node module, or via a pre-packaged 19 | docker container. 20 | 21 | ### Local Installation 22 | You can install the `ijs` node module globally and start the notebook 23 | environment using the `ijs` command. 24 | 25 | sudo npm install -g ijs 26 | ijs 27 | 28 | Then browse to http://localhost:9999. 29 | 30 | The tool will create a `notebooks` directory within the specified working 31 | directory. 32 | 33 | Note: If you don't already have node.js and IPython installed, you'll need 34 | to do so first. Unfortunately setting these up can be a bit involved. This page 35 | might help: http://ipython.org/install.html ... as of right now ijs requires 36 | IPython v2.4.1. Support for 3.x is coming. 37 | 38 | ### Docker 39 | This avoids the need to go through the steps of getting a local setup. As 40 | long as you've got a working docker setup (using boot2docker on a mac), 41 | you're good to go. Just issue the following commands: 42 | 43 | docker pull nikhilk/ijs 44 | docker run -i -p 9999:9999 -v :/data -t nikhilk/ijs 45 | 46 | And then browse to http://localhost:9999 and you're on your way. 47 | 48 | ## Screenshots 49 | 50 | ### Authoring Async Code 51 | Lots of node.js APIs are async, and you can write async code in notebook 52 | cells too! 53 | ![Hello World - Async Edition](docs/HelloWorldAsync.png) 54 | 55 | ### Working with JSON 56 | JSON is everywhere, and you can use a `%%json` cell to easily declare 57 | JSON data. The notebook provides auto-complete functionality which extends 58 | to this JSON data. 59 | ![JSON Data](docs/JSON.png) 60 | 61 | ### HTTP requests 62 | You can use the notebook interface to experiment with HTTP APIs using the 63 | HTTP client provided by `request` node module. 64 | ![HTTP Requests](docs/HTTPRequests.png) 65 | 66 | ### Client-script 67 | You can easily add HTML markup to your notebook using an `%%html` cell 68 | and client-script using a `%%script` block to use a variety of javascript 69 | libraries such as d3.js. 70 | ![HTML, JavaScript, d3.js](docs/ClientScript.png) 71 | -------------------------------------------------------------------------------- /bin/base/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/interactivecomputing/ijs/3a29ec136520768a06aaa4671c0829bd80216a04/bin/base/images/favicon.ico -------------------------------------------------------------------------------- /bin/base/images/ipynblogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/interactivecomputing/ijs/3a29ec136520768a06aaa4671c0829bd80216a04/bin/base/images/ipynblogo.png -------------------------------------------------------------------------------- /bin/custom/custom.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Interactive Computing project 3 | * (https://github.com/interactivecomputing). All rights reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /* Fonts */ 19 | @import url(https://fonts.googleapis.com/css?family=Source+Code+Pro|Open+Sans|Lato:400,700&subset=latin,latin-ext); 20 | body { 21 | font-family: 'Open Sans', Calibri, helvetica, sans-serif; 22 | } 23 | .CodeMirror pre, div.prompt, pre, code { 24 | font-family: 'Source Code Pro', 'Lucida Console', Consolas, monospace; 25 | } 26 | 27 | 28 | /* General Cleanup of backgrounds and borders */ 29 | 30 | /* Header area */ 31 | #header div.navbar-inner { 32 | border: 0px; 33 | box-shadow: none; 34 | } 35 | #menubar-container div.navbar-inner { 36 | border: 0px; 37 | background: #eee; 38 | box-shadow: none; 39 | } 40 | #notebook_panel { 41 | border-top: none; 42 | -webkit-box-shadow: none; 43 | box-shadow: none !important; 44 | background-color: white; 45 | } 46 | #menubar .navbar-inner { 47 | background: #fff; 48 | -webkit-box-shadow: none; 49 | box-shadow: none; 50 | border-radius: 0; 51 | border: none; 52 | } 53 | div.navbar-inner { 54 | min-height: 30px; 55 | } 56 | div.navbar-fixed-top div.navbar-inner, div.navbar-static-top div.navbar-inner { 57 | box-shadow: none; 58 | -webkit-box-shadow: none; 59 | border: none; 60 | } 61 | #menubar .navbar .navbar-inner, .toolbar-inner { 62 | padding-left: 0; 63 | padding-right: 0; 64 | } 65 | #notebook_name { 66 | font-weight: 700; 67 | } 68 | #checkpoint_status, #autosave_status { 69 | color: rgba(0,0,0,0.5); 70 | font-size: smaller !important; 71 | } 72 | 73 | /* Hide some commands - cell toolbar, and interrupt kernel buttons */ 74 | #maintoolbar span.navbar-text, #ctb_select, #interrupt_b { 75 | display: none; 76 | } 77 | 78 | /* Indicator for command mode to complement the existing indicator for edit mode. */ 79 | .command_mode_icon:before { 80 | content: "\f069"; 81 | } 82 | 83 | /* Specific to notebook pages */ 84 | body.notebook_app #header div.navbar-inner { 85 | background-color: #eee; 86 | } 87 | body.notebook_app #site { 88 | background-color: #eee; 89 | } 90 | body.notebook_app #menubar-container .btn { 91 | background: #fafafa; 92 | -webkit-box-shadow: none; 93 | box-shadow: none; 94 | border: solid 1px #d0d0d0; 95 | } 96 | body.notebook_app #notebook { 97 | border-top: 1px solid rgba(0,0,0,0.15); 98 | } 99 | 100 | 101 | /* Notebook area - Input and output areas */ 102 | 103 | div.input_area { 104 | border-color: rgba(0,0,0,0.05); 105 | background: rbga(0,0,0,0.5); 106 | } 107 | div.prompt.input_prompt { 108 | color: rgba(0,0,0,0.75); 109 | } 110 | div.cell.command_mode.selected { 111 | border-color: rgba(0,0,0,0.1); 112 | } 113 | div.cell.edit_mode.selected { 114 | border-color: rgba(0,0,0,0.15); 115 | box-shadow: 0px 0px 5px #d0d0d0; 116 | -webkit-box-shadow: 0px 0px 5px #d0d0d0; 117 | } 118 | div.output_scroll { 119 | box-sizing: border-box; 120 | -webkit-box-shadow: inset 0 2px 8px rgba(0,0,0,0.1); 121 | box-shadow: inset 0 2px 8px rgba(0,0,0,0.1); 122 | border-radius: 2px; 123 | } 124 | div.out_prompt_overlay:hover { 125 | border: solid 1px rgba(0,0,0,0.1); 126 | box-shadow: none; 127 | } 128 | div.text_cell div.prompt { 129 | width: 0px; 130 | min-width: 0px; 131 | padding-right: 0px; 132 | } 133 | 134 | div.code_cell + div.text_cell { 135 | margin-top: 10px; 136 | } 137 | 138 | /* Notebook area - Markdown cells */ 139 | div.text_cell_render { 140 | font-size: 11pt; 141 | line-height: 125%; 142 | } 143 | div.text_cell_render h1, 144 | div.text_cell_render h2, 145 | div.text_cell_render h3 { 146 | font-weight: 300; 147 | } 148 | div.text_cell_render h4, 149 | div.text_cell_render h5, 150 | div.text_cell_render h6 { 151 | font-weight: 700; 152 | font-size: 12pt; 153 | } 154 | div.text_cell_render h1 { 155 | font-size: 24pt; 156 | } 157 | div.text_cell_render h2 { 158 | font-size: 20pt; 159 | } 160 | div.text_cell_render h3 { 161 | font-size: 16pt; 162 | } 163 | div.text_cell_render code { 164 | font-size: 11pt; 165 | } 166 | 167 | /* Notebook area - rendered output */ 168 | .rendered_html pre, .rendered_html code { 169 | font-size: medium; 170 | } 171 | .rendered_html ol { 172 | list-style:decimal; 173 | margin: 1em 2em; 174 | } 175 | 176 | 177 | /* Home page specific */ 178 | 179 | #ipython-main-app #tabs li:nth-child(3) { 180 | display: none; 181 | } 182 | -------------------------------------------------------------------------------- /bin/custom/custom.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // custom.js 14 | // 15 | 16 | // RequireJS configuration to add common scripts used. 17 | require.config({ 18 | paths: { 19 | d3: '//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min', 20 | elem: '/static/require/element' 21 | } 22 | }); 23 | 24 | // CodeCell and CodeMirror related functionality 25 | $(function() { 26 | function hiddenLineFormatter(n) { return ''; } 27 | function stringLineFormatter(n) { return n.toString(); } 28 | 29 | // load CodeMirror modes 30 | $.getScript('/static/components/codemirror/mode/clike/clike.js'); 31 | $.getScript('/static/components/codemirror/mode/clike/javascript.js'); 32 | 33 | // Configure CodeMirror settings 34 | var cmConfig = IPython.CodeCell.options_default.cm_config; 35 | cmConfig.mode = 'text/javascript'; 36 | cmConfig.indentUnit = 2; 37 | cmConfig.smartIndent = true; 38 | cmConfig.autoClearEmptyLines = true; 39 | cmConfig.gutter = true; 40 | cmConfig.fixedGutter = true; 41 | cmConfig.lineNumbers = true; 42 | cmConfig.lineNumberFormatter = hiddenLineFormatter; 43 | 44 | // %%json cell support 45 | IPython.config.cell_magic_highlight['magic_application/ld+json'] = { 46 | reg: [ /%%json/ ] 47 | }; 48 | 49 | // %%text cell support 50 | IPython.config.cell_magic_highlight['magic_text/plain'] = { 51 | reg: [ /%%text/ ] 52 | }; 53 | 54 | // %%script cell support 55 | IPython.config.cell_magic_highlight.magic_javascript.reg = [ /^%%script/ ]; 56 | 57 | var codeCellProto = IPython.CodeCell.prototype; 58 | var originalJSONConverter = codeCellProto.toJSON; 59 | var originalExecuteReplyHandler = codeCellProto._handle_execute_reply; 60 | var originalSelectHandler = codeCellProto.select; 61 | var originalUnselectHandler = codeCellProto.unselect; 62 | 63 | // Override JSON conversion to switch the language identifier. 64 | codeCellProto.toJSON = function() { 65 | var data = originalJSONConverter.apply(this); 66 | data.language = 'javascript'; 67 | 68 | return data; 69 | } 70 | 71 | // Override execute handler on code cells to copy metadata from kernel into 72 | // cell metadata. 73 | codeCellProto._handle_execute_reply = function(msg) { 74 | originalExecuteReplyHandler.call(this, msg); 75 | 76 | var metadata = msg.metadata; 77 | for (var n in metadata) { 78 | if (n.indexOf('ijava.') === 0) { 79 | this.metadata[n] = metadata[n]; 80 | } 81 | } 82 | } 83 | 84 | // Override select and unselect handlers to toggle display of line numbers. 85 | codeCellProto.select = function() { 86 | if (originalSelectHandler.apply(this)) { 87 | this.code_mirror.setOption('lineNumberFormatter', stringLineFormatter); 88 | return true; 89 | } 90 | return false; 91 | } 92 | codeCellProto.unselect = function() { 93 | if (originalUnselectHandler.apply(this)) { 94 | this.code_mirror.setOption('lineNumberFormatter', hiddenLineFormatter); 95 | return true; 96 | } 97 | return false; 98 | } 99 | }); 100 | 101 | // JSON display support 102 | $(function() { 103 | IPython.OutputArea.display_order.push('application/json'); 104 | 105 | IPython.OutputArea.append_map['application/json'] = function(data, md, element) { 106 | data = JSON.stringify(JSON.parse(data), null, 2); 107 | 108 | var outputElement = this.create_output_subarea(md, 'output_text', 'application/json'); 109 | outputElement.append($('
').text(data));
110 |     element.append(outputElement);
111 | 
112 |     return outputElement;
113 |   }
114 | });
115 | 
116 | // Fix completion requests to include cell text!
117 | $(function() {
118 |   IPython.Kernel.prototype.complete = function(line, cursor_pos, callback) {
119 |     var callbacks;
120 |     if (callback) {
121 |       callbacks = { shell : { reply : callback } };
122 |     }
123 | 
124 |     var cell = IPython.notebook.get_selected_cell();
125 |     var cm = cell.code_mirror;
126 |     var content = {
127 |       cursor_pos : cm.indexFromPos(cm.getCursor()),
128 |       text : cell.get_text(),
129 |       line : '',
130 |       block : ''
131 |     };
132 |     return this.send_shell_message('complete_request', content, callbacks);
133 |   }
134 | });
135 | 
136 | // Add TOC functionality
137 | function setupOutline() {
138 |   var markup = '';
139 |   IPython.toolbar.element.append(markup);
140 | 
141 |   var tocDropDown = $('#tocDropDown');
142 |   tocDropDown.change(function(e) {
143 |     var index = tocDropDown.val();
144 |     if (index.length === '') {
145 |       return false;
146 |     }
147 | 
148 |     var scrollTop = IPython.notebook.get_cell(0).element.position().top -
149 |                     IPython.notebook.get_cell(parseInt(index)).element.position().top;
150 |     IPython.notebook.element.animate({ scrollTop: -scrollTop }, 250, 'easeInOutCubic');
151 | 
152 |     tocDropDown.blur();
153 |     tocDropDown.find('option').get(0).selected = true;
154 | 
155 |     return false;
156 |   });
157 | 
158 |   function createOption(title, value, level) {
159 |     var prefix = level > 1 ? new Array(level + 1).join('  ') : '';
160 |     var text = prefix + IPython.utils.escape_html(title);
161 | 
162 |     return '';
163 |   }
164 | 
165 |   function updateOutline() {
166 |     var content = [];
167 |     content.push(createOption('Table of Contents', '', 0));
168 | 
169 |     var cells = IPython.notebook.get_cells();
170 |     cells.forEach(function(c, i) {
171 |       if ((c.cell_type == 'heading') && (c.level <= 3)) {
172 |         var cell = $(c.element);
173 |         var header = cell.find('h' + c.level);
174 | 
175 |         // Retrieve the title and strip off the trailing paragraph marker
176 |         var title = header.text();
177 |         title = title.substring(-1, title.length - 1);
178 | 
179 |         if (title == 'Type Heading Here') {
180 |           // New cells have this placeholder text in them
181 |           return;
182 |         }
183 | 
184 |         content.push(createOption(title, i, c.level));
185 |       }
186 |     });
187 | 
188 |     var markup = content.join('');
189 |     tocDropDown.html(markup);
190 |   }
191 | 
192 |   updateOutline();
193 |   $([IPython.events]).on('set_dirty.Notebook', function(event, data) {
194 |     updateOutline();
195 |   });
196 |   $([IPython.events]).on('command_mode.Cell', function(event, data) {
197 |     updateOutline();
198 |   });
199 | }
200 | setTimeout(setupOutline, 1000);
201 | 


--------------------------------------------------------------------------------
/bin/ijs.js:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env node
 2 | 
 3 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing).
 4 | // All rights reserved.
 5 | //
 6 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
 7 | // except in compliance with the License. You may obtain a copy of the License at
 8 | // http://www.apache.org/licenses/LICENSE-2.0
 9 | //
10 | // Unless required by applicable law or agreed to in writing, software distributed under the
11 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
12 | // either express or implied. See the License for the specific language governing permissions
13 | // and limitations under the License.
14 | //
15 | // ijs.js
16 | // Launching script installed as executable via npm
17 | //
18 | 
19 | var childProcess = require('child_process'),
20 |     fs = require('fs'),
21 |     path = require('path');
22 | 
23 | var userPath = process.argv[2];
24 | if (userPath) {
25 |   userPath = path.join(process.cwd(), userPath);
26 | }
27 | if (!userPath || !fs.existsSync(userPath) || !fs.statSync(userPath).isDirectory()) {
28 |   console.error('Usage: ijs ');
29 |   process.exit(1);
30 | }
31 | 
32 | var debug = process.argv[3] == 'debug';
33 | 
34 | var notebooksPath = path.join(userPath, 'notebooks');
35 | if (!fs.existsSync(notebooksPath)) {
36 |   fs.mkdirSync(notebooksPath);
37 | }
38 | 
39 | var contentPath = path.join(userPath, 'static');
40 | if (!fs.existsSync(contentPath)) {
41 |   fs.mkdirSync(contentPath);
42 | }
43 | 
44 | var executable = debug ? 'node-debug' : 'node';
45 | var kernelPath = path.join(__dirname, '..', 'src', 'index.js');
46 | var kernelArgs = [
47 |   executable,
48 |   kernelPath,
49 |   '--userPath', userPath,
50 |   '{connection_file}'
51 | ].map(function(arg) { return '"' + arg + '"'; }).join(',');
52 | 
53 | var staticPaths = [
54 |   __dirname,
55 |   contentPath
56 | ].map(function(p) { return '"' + p + '"' }).join(',')
57 | 
58 | var args = [
59 |   'notebook',
60 |   '--KernelManager.kernel_cmd=[' + kernelArgs + ']',
61 |   '--NotebookApp.extra_static_paths=[' + staticPaths + ']',
62 |   '--Session.key=""',
63 |   '--Session.keyfile=""',
64 |   '--notebook-dir=' + notebooksPath,
65 |   '--ip="*"',
66 |   '--port=9999',
67 |   '--matplotlib=inline',
68 |   '--no-mathjax',
69 |   '--no-script'
70 | ];
71 | if (debug) {
72 |   args.push('--NotebookApp.log_level=DEBUG');
73 | }
74 | else {
75 |   args.push('--quiet');
76 | }
77 | var options = {
78 |   stdio: 'inherit'
79 | };
80 | 
81 | var ipython = childProcess.spawn('ipython', args, options);
82 | process.on('SIGINT', function() {
83 |   ipython.emit('SIGINT');
84 | });
85 | 


--------------------------------------------------------------------------------
/bin/require/element.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing).
 2 | // All rights reserved.
 3 | //
 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
 5 | // except in compliance with the License. You may obtain a copy of the License at
 6 | // http://www.apache.org/licenses/LICENSE-2.0
 7 | //
 8 | // Unless required by applicable law or agreed to in writing, software distributed under the
 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
10 | // either express or implied. See the License for the specific language governing permissions
11 | // and limitations under the License.
12 | //
13 | // element.js
14 | // RequireJS plugin to resolve DOM elements.
15 | //
16 | 
17 | define(function() {
18 |   'use strict';
19 | 
20 |   var pendingCallbacks = null;
21 | 
22 |   function domReadyCallback() {
23 |     var callbacks = pendingCallbacks;
24 |     pendingCallbacks = null;
25 | 
26 |     if (callbacks && callbacks.length) {
27 |       for (var i = 0; i < callbacks.length; i += 2) {
28 |         callbacks[i + 1](document.getElementById(callbacks[i]));
29 |       }
30 |     }
31 |   }
32 | 
33 |   function loadElement(name, req, loadCallback, config) {
34 |     if (config.isBuild) {
35 |       loadCallback(null);
36 |     }
37 |     else {
38 |       if (document.readyState != 'complete') {
39 |         if (!pendingCallbacks) {
40 |           pendingCallbacks = [];
41 |           document.addEventListener('DOMContentLoaded', domReadyCallback, false);
42 |         }
43 | 
44 |         pendingCallbacks.push([name, loadCallback]);
45 |       }
46 |       else {
47 |         loadCallback(document.getElementById(name));
48 |       }
49 |     }
50 |   }
51 | 
52 |   return {
53 |     load: loadElement
54 |   }
55 | });
56 | 


--------------------------------------------------------------------------------
/dist/Dockerfile:
--------------------------------------------------------------------------------
 1 | # Copyright 2015 Interactive Computing project
 2 | # (https://github.com/interactivecomputing).
 3 | # All rights reserved.
 4 | #
 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not
 6 | # use this file except in compliance with the License. You may obtain a copy of
 7 | # the License at http://www.apache.org/licenses/LICENSE-2.0
 8 | #
 9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 | # License for the specific language governing permissions and limitations under
13 | # the License.
14 | #
15 | # Installs ijs (along with IPython) to provide a fully packaged setup for
16 | # running javascript notebooks on top of node.js.
17 | #
18 | 
19 | FROM debian:jessie
20 | MAINTAINER Nikhil Kothari
21 | 
22 | RUN apt-get update -y
23 | RUN apt-get install --no-install-recommends -y -q \
24 |     curl g++ make wget unzip git libzmq-dev \
25 |     nodejs-legacy npm ipython ipython-notebook pandoc
26 | 
27 | RUN npm install -g ijs
28 | ADD start.sh /
29 | 
30 | # Container configuration
31 | EXPOSE 9999
32 | VOLUME [ "/data" ]
33 | ENTRYPOINT [ "/start.sh" ]
34 | 
35 | 


--------------------------------------------------------------------------------
/dist/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | 
3 | docker build -t nikhilk/ijs --no-cache .
4 | 


--------------------------------------------------------------------------------
/dist/publish.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | 
3 | docker push nikhilk/ijs .
4 | 
5 | 


--------------------------------------------------------------------------------
/dist/run.sh:
--------------------------------------------------------------------------------
 1 | #!/bin/sh
 2 | 
 3 | if [ "$1" = "" ]; then
 4 |   DATA_DIR=$PWD
 5 | else
 6 |   DATA_DIR=$1
 7 | fi
 8 | 
 9 | docker run -i -p 9999:9999 -v $DATA_DIR:/data -t nikhilk/ijs
10 | 
11 | 


--------------------------------------------------------------------------------
/dist/shell.sh:
--------------------------------------------------------------------------------
 1 | #!/bin/sh
 2 | 
 3 | if [ "$1" = "" ]; then
 4 |   DATA_DIR=$PWD
 5 | else
 6 |   DATA_DIR=$1
 7 | fi
 8 | 
 9 | docker run -i -p 9999:9999 -v $DATA_DIR:/data --entrypoint="/bin/bash" -t nikhilk/ijs
10 | 
11 | 


--------------------------------------------------------------------------------
/dist/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | 
3 | # Sleep to wait for the mounted volume to become available in the container
4 | sleep 5
5 | 
6 | # Run ijs with the /data volume as the directory containing notebooks etc.
7 | ijs /data
8 | 
9 | 


--------------------------------------------------------------------------------
/docs/ClientScript.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/interactivecomputing/ijs/3a29ec136520768a06aaa4671c0829bd80216a04/docs/ClientScript.png


--------------------------------------------------------------------------------
/docs/HTTPRequests.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/interactivecomputing/ijs/3a29ec136520768a06aaa4671c0829bd80216a04/docs/HTTPRequests.png


--------------------------------------------------------------------------------
/docs/HelloWorld.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/interactivecomputing/ijs/3a29ec136520768a06aaa4671c0829bd80216a04/docs/HelloWorld.png


--------------------------------------------------------------------------------
/docs/HelloWorldAsync.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/interactivecomputing/ijs/3a29ec136520768a06aaa4671c0829bd80216a04/docs/HelloWorldAsync.png


--------------------------------------------------------------------------------
/docs/JSON.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/interactivecomputing/ijs/3a29ec136520768a06aaa4671c0829bd80216a04/docs/JSON.png


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "ijs",
 3 |   "version": "0.4.0",
 4 |   "description": "Interactive JavaScript kernel for IPython",
 5 |   "keywords": [
 6 |     "interactive",
 7 |     "javascript",
 8 |     "ijs",
 9 |     "ijavascript",
10 |     "ipython",
11 |     "kernel",
12 |     "notebook",
13 |     "repl",
14 |     "console",
15 |     "ide",
16 |     "editor"
17 |   ],
18 |   "author": {
19 |     "author": "Nikhil Kothari",
20 |     "url": "http://www.nikhilk.net"
21 |   },
22 |   "license": "Apache 2.0",
23 |   "repository": {
24 |     "type": "git",
25 |     "url": "https://github.com/interactivecomputing/ijs"
26 |   },
27 |   "engines": {
28 |     "node": "0.10.x"
29 |   },
30 |   "dependencies": {
31 |     "ijs.runtime": "^0.2.0",
32 |     "node-uuid": "^1.4.2",
33 |     "nomnom": "^1.8.1",
34 |     "npm": "^2.5.0",
35 |     "tern": "^0.9.0",
36 |     "winston": "^0.9.0",
37 |     "zmq": "^2.10.0"
38 |   },
39 |   "bin": {
40 |     "ijs": "bin/ijs.js"
41 |   }
42 | }
43 | 


--------------------------------------------------------------------------------
/samples/notebooks/Async.ipynb:
--------------------------------------------------------------------------------
  1 | {
  2 |  "metadata": {
  3 |   "name": "",
  4 |   "signature": "sha256:36e746ec7c3d9d5ca075f0ec02b6063729f3a5fcfa541593c5c18435190cf66c"
  5 |  },
  6 |  "nbformat": 3,
  7 |  "nbformat_minor": 0,
  8 |  "worksheets": [
  9 |   {
 10 |    "cells": [
 11 |     {
 12 |      "cell_type": "heading",
 13 |      "level": 1,
 14 |      "metadata": {},
 15 |      "source": [
 16 |       "Async"
 17 |      ]
 18 |     },
 19 |     {
 20 |      "cell_type": "markdown",
 21 |      "metadata": {},
 22 |      "source": [
 23 |       "This notebook introduces the support for working with async operations and async APIs in node.js.\n",
 24 |       "\n",
 25 |       "In a typical notebook, one cell completes execution before the next begins. This ensures simple things such as console output from code in a cell is associated with the cell, rather than some other arbitrary cell that is executing. More importantly it ensures that variables defined within one cell are declared and initialized before the next cell executes, where these variables might be referenced.\n",
 26 |       "\n",
 27 |       "Notebooks automatically reference the `ijs.runtime` node module which includes an `async` helper API that allows returning promises representing asynchronously returned results (or errors) when using async APIs such as making HTTP requests.\n",
 28 |       "\n",
 29 |       "In this notebook, a much simpler example is used. A timeout set 3 seconds into the future. Once the timeout is reached, some text is emitted into the notebook, and a result is produced."
 30 |      ]
 31 |     },
 32 |     {
 33 |      "cell_type": "heading",
 34 |      "level": 2,
 35 |      "metadata": {},
 36 |      "source": [
 37 |       "A Simple Example"
 38 |      ]
 39 |     },
 40 |     {
 41 |      "cell_type": "code",
 42 |      "collapsed": false,
 43 |      "input": [
 44 |       "_.async(function(deferred) {\n",
 45 |       "  setTimeout(function() { \n",
 46 |       "    console.log(\"hello!\"); \n",
 47 |       "\n",
 48 |       "    var result = 'done';\n",
 49 |       "    deferred.resolve(result);\n",
 50 |       "  }, 3000);\n",
 51 |       "});"
 52 |      ],
 53 |      "language": "javascript",
 54 |      "metadata": {},
 55 |      "outputs": [
 56 |       {
 57 |        "output_type": "stream",
 58 |        "stream": "stdout",
 59 |        "text": [
 60 |         "hello!\n"
 61 |        ]
 62 |       },
 63 |       {
 64 |        "metadata": {},
 65 |        "output_type": "display_data",
 66 |        "text": [
 67 |         "done"
 68 |        ]
 69 |       }
 70 |      ],
 71 |      "prompt_number": 1
 72 |     },
 73 |     {
 74 |      "cell_type": "heading",
 75 |      "level": 2,
 76 |      "metadata": {},
 77 |      "source": [
 78 |       "Deferred API"
 79 |      ]
 80 |     },
 81 |     {
 82 |      "cell_type": "markdown",
 83 |      "metadata": {},
 84 |      "source": [
 85 |       "The `async` helper method takes a function as an argument that it will invoke and pass in a deferred object. It returns the promise represented by this deferred object, which the notebook will monitor for completion of the function.\n",
 86 |       "\n",
 87 |       "This deferred object has two methods:\n",
 88 |       "\n",
 89 |       "- `resolve(result)`: call this on successful completion of the async work.\n",
 90 |       "\n",
 91 |       "- `reject(error)`: call this when there is an error."
 92 |      ]
 93 |     },
 94 |     {
 95 |      "cell_type": "heading",
 96 |      "level": 2,
 97 |      "metadata": {},
 98 |      "source": [
 99 |       "A Realistic Example"
100 |      ]
101 |     },
102 |     {
103 |      "cell_type": "markdown",
104 |      "metadata": {},
105 |      "source": [
106 |       "Most node.js APIs use the callback pattern to enable code to resume with async results or errors. In the promises world, your callbacks invoke the `resolve` and `reject` methods."
107 |      ]
108 |     },
109 |     {
110 |      "cell_type": "code",
111 |      "collapsed": false,
112 |      "input": [
113 |       "var http = require('http');\n",
114 |       "\n",
115 |       "_.async(function(deferred) {\n",
116 |       "  _.request('http://www.example.org', function(err, response) {\n",
117 |       "    if (err || (response.statusCode != 200)) {\n",
118 |       "      deferred.reject(err || new Error('Failed request'));\n",
119 |       "    }\n",
120 |       "\n",
121 |       "    deferred.resolve('Successful request; status = ' + response.statusCode);\n",
122 |       "  })\n",
123 |       "});"
124 |      ],
125 |      "language": "javascript",
126 |      "metadata": {},
127 |      "outputs": [
128 |       {
129 |        "metadata": {},
130 |        "output_type": "display_data",
131 |        "text": [
132 |         "Successful request; status = 200"
133 |        ]
134 |       }
135 |      ],
136 |      "prompt_number": 2
137 |     },
138 |     {
139 |      "cell_type": "code",
140 |      "collapsed": false,
141 |      "input": [],
142 |      "language": "javascript",
143 |      "metadata": {},
144 |      "outputs": []
145 |     }
146 |    ],
147 |    "metadata": {}
148 |   }
149 |  ]
150 | }


--------------------------------------------------------------------------------
/samples/notebooks/Charting.ipynb:
--------------------------------------------------------------------------------
  1 | {
  2 |  "metadata": {
  3 |   "name": "",
  4 |   "signature": "sha256:78f339cff469d33f761242f624ce232bde0c2312cff516792c23e5e45b058eb1"
  5 |  },
  6 |  "nbformat": 3,
  7 |  "nbformat_minor": 0,
  8 |  "worksheets": [
  9 |   {
 10 |    "cells": [
 11 |     {
 12 |      "cell_type": "heading",
 13 |      "level": 1,
 14 |      "metadata": {},
 15 |      "source": [
 16 |       "Charting"
 17 |      ]
 18 |     },
 19 |     {
 20 |      "cell_type": "markdown",
 21 |      "metadata": {},
 22 |      "source": [
 23 |       "A basic notebook demonstrating use of the charting extension to create charts."
 24 |      ]
 25 |     },
 26 |     {
 27 |      "cell_type": "heading",
 28 |      "level": 2,
 29 |      "metadata": {},
 30 |      "source": [
 31 |       "Charting Extension"
 32 |      ]
 33 |     },
 34 |     {
 35 |      "cell_type": "code",
 36 |      "collapsed": false,
 37 |      "input": [
 38 |       "%extension charting"
 39 |      ],
 40 |      "language": "javascript",
 41 |      "metadata": {},
 42 |      "outputs": [
 43 |       {
 44 |        "javascript": [
 45 |         "require.config({ paths: { \"gchart\": \"https://rawgit.com/interactivecomputing/ijs.ext.charting/master/dist/gchart\" } });"
 46 |        ],
 47 |        "metadata": {},
 48 |        "output_type": "display_data"
 49 |       }
 50 |      ],
 51 |      "prompt_number": 1
 52 |     },
 53 |     {
 54 |      "cell_type": "markdown",
 55 |      "metadata": {},
 56 |      "source": [
 57 |       "This just loaded the node module named `ijs.ext.charting` from npm, and then loaded the module into the notebook. As a result, the %chart command is now available. The charting extension allows creating interactive charts using the Google Charting API."
 58 |      ]
 59 |     },
 60 |     {
 61 |      "cell_type": "heading",
 62 |      "level": 2,
 63 |      "metadata": {},
 64 |      "source": [
 65 |       "Chart Data"
 66 |      ]
 67 |     },
 68 |     {
 69 |      "cell_type": "markdown",
 70 |      "metadata": {},
 71 |      "source": [
 72 |       "Lets first define some data to use to chart."
 73 |      ]
 74 |     },
 75 |     {
 76 |      "cell_type": "code",
 77 |      "collapsed": false,
 78 |      "input": [
 79 |       "%%json --name items\n",
 80 |       "[\n",
 81 |       "  { \"x\": 1, \"y\": 100 },\n",
 82 |       "  { \"x\": 2, \"y\": 50 },\n",
 83 |       "  { \"x\": 3, \"y\": 200 },\n",
 84 |       "  { \"x\": 4, \"y\": 75 },\n",
 85 |       "  { \"x\": 5, \"y\": 57 },\n",
 86 |       "  { \"x\": 6, \"y\": 120 }\n",
 87 |       "]"
 88 |      ],
 89 |      "language": "javascript",
 90 |      "metadata": {},
 91 |      "outputs": [],
 92 |      "prompt_number": 2
 93 |     },
 94 |     {
 95 |      "cell_type": "heading",
 96 |      "level": 2,
 97 |      "metadata": {},
 98 |      "source": [
 99 |       "Table Chart"
100 |      ]
101 |     },
102 |     {
103 |      "cell_type": "code",
104 |      "collapsed": false,
105 |      "input": [
106 |       "%%chart --type table --data items\n",
107 |       "{\n",
108 |       "  \"showRowNumber\": true\n",
109 |       "}"
110 |      ],
111 |      "language": "javascript",
112 |      "metadata": {},
113 |      "outputs": [
114 |       {
115 |        "html": [
116 |         "
" 119 | ], 120 | "metadata": {}, 121 | "output_type": "display_data" 122 | } 123 | ], 124 | "prompt_number": 3 125 | }, 126 | { 127 | "cell_type": "heading", 128 | "level": 2, 129 | "metadata": {}, 130 | "source": [ 131 | "Scatter Chart" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "collapsed": false, 137 | "input": [ 138 | "%%chart --type scatter --data items" 139 | ], 140 | "language": "javascript", 141 | "metadata": {}, 142 | "outputs": [ 143 | { 144 | "html": [ 145 | "
" 148 | ], 149 | "metadata": {}, 150 | "output_type": "display_data" 151 | } 152 | ], 153 | "prompt_number": 4 154 | }, 155 | { 156 | "cell_type": "code", 157 | "collapsed": false, 158 | "input": [], 159 | "language": "javascript", 160 | "metadata": {}, 161 | "outputs": [] 162 | } 163 | ], 164 | "metadata": {} 165 | } 166 | ] 167 | } -------------------------------------------------------------------------------- /samples/notebooks/Client Script.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:a952ef321e73186055e68c2d6e516558f87614ed45539b2efc030a0c88e1c121" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "heading", 13 | "level": 1, 14 | "metadata": {}, 15 | "source": [ 16 | "Client Script" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "The `%%html` and `%%script` commands enable generating HTML into the notebook, and programming against the DOM using client-side JavaScript.\n", 24 | "\n", 25 | "This example demonstrates generating an `` element and then using d3.js to add an SVG element." 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "collapsed": false, 31 | "input": [ 32 | "%%html\n", 33 | "" 34 | ], 35 | "language": "javascript", 36 | "metadata": {}, 37 | "outputs": [ 38 | { 39 | "html": [ 40 | "" 41 | ], 42 | "metadata": {}, 43 | "output_type": "display_data" 44 | } 45 | ], 46 | "prompt_number": 1 47 | }, 48 | { 49 | "cell_type": "code", 50 | "collapsed": false, 51 | "input": [ 52 | "%%script\n", 53 | "require(['d3'], function(d3) {\n", 54 | " d3.select(\"#surface\")\n", 55 | " .append(\"circle\")\n", 56 | " .style(\"stroke\", \"black\")\n", 57 | " .style(\"fill\", \"white\")\n", 58 | " .attr(\"r\", 40)\n", 59 | " .attr(\"cx\", 50)\n", 60 | " .attr(\"cy\", 50)\n", 61 | "});" 62 | ], 63 | "language": "javascript", 64 | "metadata": {}, 65 | "outputs": [ 66 | { 67 | "javascript": [ 68 | "require(['d3'], function(d3) {\n", 69 | " d3.select(\"#surface\")\n", 70 | " .append(\"circle\")\n", 71 | " .style(\"stroke\", \"black\")\n", 72 | " .style(\"fill\", \"white\")\n", 73 | " .attr(\"r\", 40)\n", 74 | " .attr(\"cx\", 50)\n", 75 | " .attr(\"cy\", 50)\n", 76 | "});" 77 | ], 78 | "metadata": {}, 79 | "output_type": "display_data" 80 | } 81 | ], 82 | "prompt_number": 2 83 | } 84 | ], 85 | "metadata": {} 86 | } 87 | ] 88 | } -------------------------------------------------------------------------------- /samples/notebooks/HTTP Extension - HTTP Client.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:2b952b5dcf23811d475c6eedb63e779008c81aa14a109144db35c8035c58f9c5" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "heading", 13 | "level": 1, 14 | "metadata": {}, 15 | "source": [ 16 | "HTTP Extension - HTTP Client" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "This notebook demonstrates the HTTP extension which introduces the `%url` and `%%request` commands." 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "collapsed": false, 29 | "input": [ 30 | "%extension http" 31 | ], 32 | "language": "javascript", 33 | "metadata": {}, 34 | "outputs": [], 35 | "prompt_number": 1 36 | }, 37 | { 38 | "cell_type": "heading", 39 | "level": 2, 40 | "metadata": {}, 41 | "source": [ 42 | "%url Command" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "The `%url` command allows constructing a URL request with query string, headers, or request data pulled from notebook variables." 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "collapsed": false, 55 | "input": [ 56 | "%url --help" 57 | ], 58 | "language": "javascript", 59 | "metadata": {}, 60 | "outputs": [ 61 | { 62 | "output_type": "stream", 63 | "stream": "stdout", 64 | "text": [ 65 | "\n", 66 | "Usage: url [options]\n", 67 | "\n", 68 | "method the HTTP method to use (eg. GET, POST, etc.)\n", 69 | "url the URL to request\n", 70 | "\n", 71 | "Options:\n", 72 | " --query variable the name of the variable containing query data\n", 73 | " --headers variable the name of the variable containing headers\n", 74 | " --data variable the name of the variable containing request content\n", 75 | " --response mode the response display mode (all, status, headers, metadata or body) [all]\n", 76 | "\n", 77 | "Issues the specified HTTP request and displays the resulting response.\n" 78 | ] 79 | } 80 | ], 81 | "prompt_number": 2 82 | }, 83 | { 84 | "cell_type": "code", 85 | "collapsed": false, 86 | "input": [ 87 | "%url get https://www.google.com/images/srpr/logo11w.png --response=body" 88 | ], 89 | "language": "javascript", 90 | "metadata": {}, 91 | "outputs": [ 92 | { 93 | "metadata": {}, 94 | "output_type": "display_data", 95 | "png": "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" 96 | } 97 | ], 98 | "prompt_number": 3 99 | }, 100 | { 101 | "cell_type": "heading", 102 | "level": 2, 103 | "metadata": {}, 104 | "source": [ 105 | "%%request Command" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": {}, 111 | "source": [ 112 | "The %%request command allows specifying a full HTTP request verbatim within the cell." 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "collapsed": false, 118 | "input": [ 119 | "%%request --help" 120 | ], 121 | "language": "javascript", 122 | "metadata": {}, 123 | "outputs": [ 124 | { 125 | "output_type": "stream", 126 | "stream": "stdout", 127 | "text": [ 128 | "\n", 129 | "Usage: request [options]\n", 130 | "\n", 131 | "Options:\n", 132 | " --response mode the response display mode (all, status, headers, metadata or body) [all]\n", 133 | "\n", 134 | "Issues the specified HTTP request and displays the resulting response.\n" 135 | ] 136 | } 137 | ], 138 | "prompt_number": 4 139 | }, 140 | { 141 | "cell_type": "code", 142 | "collapsed": false, 143 | "input": [ 144 | "%%request\n", 145 | "GET http://ip.jsontest.com/" 146 | ], 147 | "language": "javascript", 148 | "metadata": {}, 149 | "outputs": [ 150 | { 151 | "output_type": "stream", 152 | "stream": "stdout", 153 | "text": [ 154 | "HTTP 200 \n" 155 | ] 156 | }, 157 | { 158 | "output_type": "stream", 159 | "stream": "stdout", 160 | "text": [ 161 | "\n" 162 | ] 163 | }, 164 | { 165 | "output_type": "stream", 166 | "stream": "stdout", 167 | "text": [ 168 | "access-control-allow-origin: *\n" 169 | ] 170 | }, 171 | { 172 | "output_type": "stream", 173 | "stream": "stdout", 174 | "text": [ 175 | "content-type: application/json; charset=ISO-8859-1\n" 176 | ] 177 | }, 178 | { 179 | "output_type": "stream", 180 | "stream": "stdout", 181 | "text": [ 182 | "date: Sun, 10 May 2015 13:09:23 GMT\n" 183 | ] 184 | }, 185 | { 186 | "output_type": "stream", 187 | "stream": "stdout", 188 | "text": [ 189 | "server: Google Frontend\n" 190 | ] 191 | }, 192 | { 193 | "output_type": "stream", 194 | "stream": "stdout", 195 | "text": [ 196 | "cache-control: private\n" 197 | ] 198 | }, 199 | { 200 | "output_type": "stream", 201 | "stream": "stdout", 202 | "text": [ 203 | "alternate-protocol: 80:quic,p=1,80:quic,p=1\n" 204 | ] 205 | }, 206 | { 207 | "output_type": "stream", 208 | "stream": "stdout", 209 | "text": [ 210 | "accept-ranges: none\n" 211 | ] 212 | }, 213 | { 214 | "output_type": "stream", 215 | "stream": "stdout", 216 | "text": [ 217 | "vary: Accept-Encoding\n" 218 | ] 219 | }, 220 | { 221 | "output_type": "stream", 222 | "stream": "stdout", 223 | "text": [ 224 | "transfer-encoding: chunked\n" 225 | ] 226 | }, 227 | { 228 | "output_type": "stream", 229 | "stream": "stdout", 230 | "text": [ 231 | "\n" 232 | ] 233 | }, 234 | { 235 | "json": [ 236 | "{\"ip\":\"66.205.143.247\"}" 237 | ], 238 | "metadata": {}, 239 | "output_type": "display_data" 240 | } 241 | ], 242 | "prompt_number": 5 243 | }, 244 | { 245 | "cell_type": "code", 246 | "collapsed": false, 247 | "input": [ 248 | "%%request\n", 249 | "POST http://headers.jsontest.com/\n", 250 | "Foo: Bar\n", 251 | "\n", 252 | "Sample request body" 253 | ], 254 | "language": "javascript", 255 | "metadata": {}, 256 | "outputs": [ 257 | { 258 | "output_type": "stream", 259 | "stream": "stdout", 260 | "text": [ 261 | "HTTP 200 \n" 262 | ] 263 | }, 264 | { 265 | "output_type": "stream", 266 | "stream": "stdout", 267 | "text": [ 268 | "\n" 269 | ] 270 | }, 271 | { 272 | "output_type": "stream", 273 | "stream": "stdout", 274 | "text": [ 275 | "access-control-allow-origin: *\n" 276 | ] 277 | }, 278 | { 279 | "output_type": "stream", 280 | "stream": "stdout", 281 | "text": [ 282 | "content-type: application/json; charset=ISO-8859-1\n" 283 | ] 284 | }, 285 | { 286 | "output_type": "stream", 287 | "stream": "stdout", 288 | "text": [ 289 | "date: Sun, 10 May 2015 13:09:24 GMT\n" 290 | ] 291 | }, 292 | { 293 | "output_type": "stream", 294 | "stream": "stdout", 295 | "text": [ 296 | "server: Google Frontend\n" 297 | ] 298 | }, 299 | { 300 | "output_type": "stream", 301 | "stream": "stdout", 302 | "text": [ 303 | "cache-control: private\n" 304 | ] 305 | }, 306 | { 307 | "output_type": "stream", 308 | "stream": "stdout", 309 | "text": [ 310 | "alternate-protocol: 80:quic,p=1,80:quic,p=1\n" 311 | ] 312 | }, 313 | { 314 | "output_type": "stream", 315 | "stream": "stdout", 316 | "text": [ 317 | "accept-ranges: none\n" 318 | ] 319 | }, 320 | { 321 | "output_type": "stream", 322 | "stream": "stdout", 323 | "text": [ 324 | "vary: Accept-Encoding\n" 325 | ] 326 | }, 327 | { 328 | "output_type": "stream", 329 | "stream": "stdout", 330 | "text": [ 331 | "transfer-encoding: chunked\n" 332 | ] 333 | }, 334 | { 335 | "output_type": "stream", 336 | "stream": "stdout", 337 | "text": [ 338 | "\n" 339 | ] 340 | }, 341 | { 342 | "json": [ 343 | "{\"Host\":\"headers.jsontest.com\",\"Foo\":\"Bar\",\"Content-Length\":\"19\",\"User-Agent\":\"\"}" 344 | ], 345 | "metadata": {}, 346 | "output_type": "display_data" 347 | } 348 | ], 349 | "prompt_number": 6 350 | }, 351 | { 352 | "cell_type": "code", 353 | "collapsed": false, 354 | "input": [], 355 | "language": "javascript", 356 | "metadata": {}, 357 | "outputs": [] 358 | } 359 | ], 360 | "metadata": {} 361 | } 362 | ] 363 | } -------------------------------------------------------------------------------- /samples/notebooks/HTTP Extension - HTTP Server.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:f81a583149641bc02c6887bad8486aecae648628e18880a294496e796c046e15" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "heading", 13 | "level": 1, 14 | "metadata": {}, 15 | "source": [ 16 | "HTTP Extension - HTTP Server" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "This notebook demonstrates the HTTP extension which introduces the `%server`, `%server.static` and `%server.route` commands to implement a HTTP server and handle API requests." 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "collapsed": false, 29 | "input": [ 30 | "%extension http" 31 | ], 32 | "language": "javascript", 33 | "metadata": {}, 34 | "outputs": [], 35 | "prompt_number": 1 36 | }, 37 | { 38 | "cell_type": "heading", 39 | "level": 2, 40 | "metadata": {}, 41 | "source": [ 42 | "Starting the HTTP Server" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "The `%server` command can be used to start a server on a specified port, and it can also be used to stop it. The server is accessible via any process on the local machine." 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "collapsed": false, 55 | "input": [ 56 | "%server start -p 8081" 57 | ], 58 | "language": "javascript", 59 | "metadata": {}, 60 | "outputs": [], 61 | "prompt_number": 2 62 | }, 63 | { 64 | "cell_type": "heading", 65 | "level": 2, 66 | "metadata": {}, 67 | "source": [ 68 | "Static Content" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "Static content can be declared inline within the cell. The name serves as the key for the content, and can be used to update it subsequently. It is also used in creating the URL that serves the content." 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "collapsed": false, 81 | "input": [ 82 | "%%server.static add --name foo --mime text/plain\n", 83 | "Hello World!" 84 | ], 85 | "language": "javascript", 86 | "metadata": {}, 87 | "outputs": [ 88 | { 89 | "html": [ 90 | "foo" 91 | ], 92 | "metadata": {}, 93 | "output_type": "display_data" 94 | } 95 | ], 96 | "prompt_number": 3 97 | }, 98 | { 99 | "cell_type": "markdown", 100 | "metadata": {}, 101 | "source": [ 102 | "Static content can also be associated with the current value of a variable previously defined in the notebook. For example, the next cell defines a variable named `jsondata` to the result of parsing a JSON literal, and the subsequent cell exposes it as static content with the name `json`" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "collapsed": false, 108 | "input": [ 109 | "%%json --name=jsondata\n", 110 | "{\n", 111 | " \"abc\": 123\n", 112 | "}" 113 | ], 114 | "language": "javascript", 115 | "metadata": {}, 116 | "outputs": [], 117 | "prompt_number": 4 118 | }, 119 | { 120 | "cell_type": "code", 121 | "collapsed": false, 122 | "input": [ 123 | "%server.static add --name json --mime application/json --data jsondata" 124 | ], 125 | "language": "javascript", 126 | "metadata": {}, 127 | "outputs": [ 128 | { 129 | "html": [ 130 | "json" 131 | ], 132 | "metadata": {}, 133 | "output_type": "display_data" 134 | } 135 | ], 136 | "prompt_number": 5 137 | }, 138 | { 139 | "cell_type": "heading", 140 | "level": 2, 141 | "metadata": {}, 142 | "source": [ 143 | "Routes and Dynamic Content" 144 | ] 145 | }, 146 | { 147 | "cell_type": "markdown", 148 | "metadata": {}, 149 | "source": [ 150 | "You can declare request handlers as functions and then expose them as route handlers. The next cell defines a function named `helloHandler` that is then exposed as the `/hello` endpoint (by default for the GET HTTP method).\n", 151 | "\n", 152 | "The following cell uses the `%request` command also provided by the HTTP extension to invoke that endpoint. While the example shows the notebook invoking the endpoint also published by the notebook, in reality some other client application could be the one invoking the endpoint." 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "collapsed": false, 158 | "input": [ 159 | "function helloHandler(req, res) {\n", 160 | " res.send('Hello World!');\n", 161 | " res.end();\n", 162 | "}" 163 | ], 164 | "language": "javascript", 165 | "metadata": {}, 166 | "outputs": [], 167 | "prompt_number": 6 168 | }, 169 | { 170 | "cell_type": "code", 171 | "collapsed": false, 172 | "input": [ 173 | "%%server.route add /hello --handler helloHandler" 174 | ], 175 | "language": "javascript", 176 | "metadata": {}, 177 | "outputs": [], 178 | "prompt_number": 7 179 | }, 180 | { 181 | "cell_type": "code", 182 | "collapsed": false, 183 | "input": [ 184 | "%%request\n", 185 | "GET http://localhost:8081/hello" 186 | ], 187 | "language": "javascript", 188 | "metadata": {}, 189 | "outputs": [ 190 | { 191 | "output_type": "stream", 192 | "stream": "stdout", 193 | "text": [ 194 | "HTTP 200 \n" 195 | ] 196 | }, 197 | { 198 | "output_type": "stream", 199 | "stream": "stdout", 200 | "text": [ 201 | "\n" 202 | ] 203 | }, 204 | { 205 | "output_type": "stream", 206 | "stream": "stdout", 207 | "text": [ 208 | "x-powered-by: Express\n" 209 | ] 210 | }, 211 | { 212 | "output_type": "stream", 213 | "stream": "stdout", 214 | "text": [ 215 | "content-type: text/html; charset=utf-8\n" 216 | ] 217 | }, 218 | { 219 | "output_type": "stream", 220 | "stream": "stdout", 221 | "text": [ 222 | "content-length: 12\n" 223 | ] 224 | }, 225 | { 226 | "output_type": "stream", 227 | "stream": "stdout", 228 | "text": [ 229 | "etag: W/\"c-1c291ca3\"\n" 230 | ] 231 | }, 232 | { 233 | "output_type": "stream", 234 | "stream": "stdout", 235 | "text": [ 236 | "date: Thu, 28 May 2015 00:33:25 GMT\n" 237 | ] 238 | }, 239 | { 240 | "output_type": "stream", 241 | "stream": "stdout", 242 | "text": [ 243 | "connection: keep-alive\n" 244 | ] 245 | }, 246 | { 247 | "output_type": "stream", 248 | "stream": "stdout", 249 | "text": [ 250 | "\n" 251 | ] 252 | }, 253 | { 254 | "metadata": {}, 255 | "output_type": "display_data", 256 | "text": [ 257 | "Hello World!" 258 | ] 259 | } 260 | ], 261 | "prompt_number": 8 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "metadata": {}, 266 | "source": [ 267 | "You can also write the script defining the request handler inline when declaring the route. In this case, you do not write the signature of the function. The code you write becomes the body of a generated handler, and the request and response objects exist within scope of the function body." 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "collapsed": false, 273 | "input": [ 274 | "%%server.route add /echo --method POST\n", 275 | "response.json(request.body);\n", 276 | "response.end();" 277 | ], 278 | "language": "javascript", 279 | "metadata": {}, 280 | "outputs": [], 281 | "prompt_number": 9 282 | }, 283 | { 284 | "cell_type": "code", 285 | "collapsed": false, 286 | "input": [ 287 | "%%request\n", 288 | "POST http://localhost:8081/echo\n", 289 | "Content-Type: application/json\n", 290 | "\n", 291 | "{\n", 292 | " \"xyz\": \"xyz\"\n", 293 | "}" 294 | ], 295 | "language": "javascript", 296 | "metadata": {}, 297 | "outputs": [ 298 | { 299 | "output_type": "stream", 300 | "stream": "stdout", 301 | "text": [ 302 | "HTTP 200 \n" 303 | ] 304 | }, 305 | { 306 | "output_type": "stream", 307 | "stream": "stdout", 308 | "text": [ 309 | "\n" 310 | ] 311 | }, 312 | { 313 | "output_type": "stream", 314 | "stream": "stdout", 315 | "text": [ 316 | "x-powered-by: Express\n" 317 | ] 318 | }, 319 | { 320 | "output_type": "stream", 321 | "stream": "stdout", 322 | "text": [ 323 | "content-type: application/json; charset=utf-8\n" 324 | ] 325 | }, 326 | { 327 | "output_type": "stream", 328 | "stream": "stdout", 329 | "text": [ 330 | "content-length: 13\n" 331 | ] 332 | }, 333 | { 334 | "output_type": "stream", 335 | "stream": "stdout", 336 | "text": [ 337 | "etag: W/\"d-7fa24d58\"\n" 338 | ] 339 | }, 340 | { 341 | "output_type": "stream", 342 | "stream": "stdout", 343 | "text": [ 344 | "date: Thu, 28 May 2015 00:33:27 GMT\n" 345 | ] 346 | }, 347 | { 348 | "output_type": "stream", 349 | "stream": "stdout", 350 | "text": [ 351 | "connection: keep-alive\n" 352 | ] 353 | }, 354 | { 355 | "output_type": "stream", 356 | "stream": "stdout", 357 | "text": [ 358 | "\n" 359 | ] 360 | }, 361 | { 362 | "json": [ 363 | "{\"xyz\":\"xyz\"}" 364 | ], 365 | "metadata": {}, 366 | "output_type": "display_data" 367 | } 368 | ], 369 | "prompt_number": 10 370 | }, 371 | { 372 | "cell_type": "markdown", 373 | "metadata": {}, 374 | "source": [ 375 | "Routes are dynamic. You can remove existing routes, or replace the implementation of an existing route." 376 | ] 377 | }, 378 | { 379 | "cell_type": "code", 380 | "collapsed": false, 381 | "input": [ 382 | "%%server.route remove /echo" 383 | ], 384 | "language": "javascript", 385 | "metadata": {}, 386 | "outputs": [], 387 | "prompt_number": 11 388 | }, 389 | { 390 | "cell_type": "code", 391 | "collapsed": false, 392 | "input": [ 393 | "%%request\n", 394 | "POST http://localhost:8081/xyz" 395 | ], 396 | "language": "javascript", 397 | "metadata": {}, 398 | "outputs": [ 399 | { 400 | "output_type": "stream", 401 | "stream": "stderr", 402 | "text": [ 403 | "HTTP 404 \n" 404 | ] 405 | }, 406 | { 407 | "output_type": "stream", 408 | "stream": "stdout", 409 | "text": [ 410 | "\n" 411 | ] 412 | }, 413 | { 414 | "output_type": "stream", 415 | "stream": "stdout", 416 | "text": [ 417 | "x-powered-by: Express\n" 418 | ] 419 | }, 420 | { 421 | "output_type": "stream", 422 | "stream": "stdout", 423 | "text": [ 424 | "x-content-type-options: nosniff\n" 425 | ] 426 | }, 427 | { 428 | "output_type": "stream", 429 | "stream": "stdout", 430 | "text": [ 431 | "content-type: text/html; charset=utf-8\n" 432 | ] 433 | }, 434 | { 435 | "output_type": "stream", 436 | "stream": "stdout", 437 | "text": [ 438 | "content-length: 17\n" 439 | ] 440 | }, 441 | { 442 | "output_type": "stream", 443 | "stream": "stdout", 444 | "text": [ 445 | "date: Thu, 28 May 2015 00:33:29 GMT\n" 446 | ] 447 | }, 448 | { 449 | "output_type": "stream", 450 | "stream": "stdout", 451 | "text": [ 452 | "connection: keep-alive\n" 453 | ] 454 | }, 455 | { 456 | "output_type": "stream", 457 | "stream": "stdout", 458 | "text": [ 459 | "\n" 460 | ] 461 | } 462 | ], 463 | "prompt_number": 12 464 | }, 465 | { 466 | "cell_type": "heading", 467 | "level": 2, 468 | "metadata": {}, 469 | "source": [ 470 | "Stopping the Server" 471 | ] 472 | }, 473 | { 474 | "cell_type": "code", 475 | "collapsed": false, 476 | "input": [ 477 | "%server stop" 478 | ], 479 | "language": "javascript", 480 | "metadata": {}, 481 | "outputs": [], 482 | "prompt_number": 13 483 | }, 484 | { 485 | "cell_type": "code", 486 | "collapsed": false, 487 | "input": [], 488 | "language": "javascript", 489 | "metadata": {}, 490 | "outputs": [] 491 | } 492 | ], 493 | "metadata": {} 494 | } 495 | ] 496 | } -------------------------------------------------------------------------------- /samples/notebooks/HTTP Requests.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:11c6fc59d9ab05e595e7c4d7c34d3aba7a8b1174eb6cb85446c9bee32a84e5e4" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "heading", 13 | "level": 1, 14 | "metadata": {}, 15 | "source": [ 16 | "HTTP Requests" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "HTTP requests can be issued via the built-in `http` and `https` modules, as well as higher-level wrapper modules such as the popular `requests` module. The latter is supported out-of-the-box as a helper in the form of `_.requests` within a notebook.\n", 24 | "\n", 25 | "Note that HTTP requests complete asynchronously, and the `_.async` helper is used to convert callback-style async patterns into promises that the notebook can wait on to know when the cell has completed execution." 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "collapsed": false, 31 | "input": [ 32 | "_.async(function(deferred) {\n", 33 | " _.request('http://www.example.org', function(err, response) {\n", 34 | " console.log('Status Code: %d', response.statusCode);\n", 35 | " console.log('Page Title: %s',\n", 36 | " response.body.match(/\\([^\\<]*)\\<\\/title\\>/)[1]);\n", 37 | "\n", 38 | " deferred.resolve();\n", 39 | " })\n", 40 | "});" 41 | ], 42 | "language": "javascript", 43 | "metadata": {}, 44 | "outputs": [ 45 | { 46 | "output_type": "stream", 47 | "stream": "stdout", 48 | "text": [ 49 | "Status Code: 200\n" 50 | ] 51 | }, 52 | { 53 | "output_type": "stream", 54 | "stream": "stdout", 55 | "text": [ 56 | "Page Title: Example Domain\n" 57 | ] 58 | } 59 | ], 60 | "prompt_number": 1 61 | } 62 | ], 63 | "metadata": {} 64 | } 65 | ] 66 | } -------------------------------------------------------------------------------- /samples/notebooks/Hello World.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:632475789a690560a038095fc033b35cd6727ea64a513981dae3afdfa8f96725" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "heading", 13 | "level": 1, 14 | "metadata": {}, 15 | "source": [ 16 | "Hello World" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "A hello-world style introduction to Interactive JavaScript in a Notebook, along with using node.js, and node modules." 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "collapsed": false, 29 | "input": [ 30 | "var util = require('util')\n", 31 | "var greeting = util.format('Hello %s!', 'Nikhil Kothari');\n", 32 | "\n", 33 | "console.log(greeting);" 34 | ], 35 | "language": "javascript", 36 | "metadata": {}, 37 | "outputs": [ 38 | { 39 | "output_type": "stream", 40 | "stream": "stdout", 41 | "text": [ 42 | "Hello Nikhil Kothari!\n" 43 | ] 44 | } 45 | ], 46 | "prompt_number": 1 47 | }, 48 | { 49 | "cell_type": "heading", 50 | "level": 2, 51 | "metadata": {}, 52 | "source": [ 53 | "Using Node modules" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "Node.js modules can be installed via the `%module` command. Modules are installed using `npm` along with their dependencies, so that they can subsequently loaded using `require()`." 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "collapsed": false, 66 | "input": [ 67 | "%module qr-image" 68 | ], 69 | "language": "javascript", 70 | "metadata": {}, 71 | "outputs": [ 72 | { 73 | "output_type": "stream", 74 | "stream": "stdout", 75 | "text": [ 76 | "qr-image@3.1.0 ../node_modules/qr-image\n", 77 | "`-- pako@0.2.6\n" 78 | ] 79 | } 80 | ], 81 | "prompt_number": 2 82 | }, 83 | { 84 | "cell_type": "code", 85 | "collapsed": false, 86 | "input": [ 87 | "var qr = require('qr-image');\n", 88 | "\n", 89 | "function qrCode(text) {\n", 90 | " var buffer = qr.imageSync(text, { type: 'png' });\n", 91 | " return _.data.image(buffer);\n", 92 | "}\n", 93 | "\n", 94 | "qrCode(greeting);" 95 | ], 96 | "language": "javascript", 97 | "metadata": {}, 98 | "outputs": [ 99 | { 100 | "metadata": {}, 101 | "output_type": "display_data", 102 | "png": "iVBORw0KGgoAAAANSUhEUgAAAKUAAAClCAAAAAAYQGIGAAABaUlEQVR42u3bMZKDMBAEQP7/aTu8BORZBDokmogyrqUJFqRBbJ8Zto2SkpKSkpKSkpKSkvJa5fZ7Oy64c/Tvt5OVKSnnUqaO+/5MSTm1cq//9o627xT1ypSU71G2f6OkpNyC0eJhFUrKlynr13DI+rdRMCXlcGWQQJzcG5zAUFKOVMZx9++TBFni3ek/JeVIZdCT9VtDvR4l5azKNJoozdPqY05KylmVbWp6XXusw8ukpJxfGXiD0WewR0m5kvLwGdiuX5qnXZRtUFI+TJlGgWnfDxlfUlI+R9luzNL7qfRlb/9znJLyicpgmhW0cnv1AyXlSsqgfrIcqOO7DErKuZTd6UXw5AvmfZSUEyr7e7K8AunGVU+UlMOVaWcHTV0KCikpF1GmfZrO7C5fcUtJObMyWKGXhoyUlC9Tphl6kIZQUq6kLB3tSd0pKVdSlr6KSE+clqKknFX5uI2SkpKSkpKSkpKSkvL89gVuT9kpLBq+OwAAAABJRU5ErkJggg==" 103 | } 104 | ], 105 | "prompt_number": 3 106 | }, 107 | { 108 | "cell_type": "heading", 109 | "level": 2, 110 | "metadata": {}, 111 | "source": [ 112 | "Using Async APIs" 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": {}, 118 | "source": [ 119 | "Within a notebook, the expectation is that a cell completes execution before the next cell begins to execute. As a result, some special treatment is required to indicate when async APIs, which are quite prevalent in node.js, have completed.\n", 120 | "\n", 121 | "Async APIs in node.js take an async completion callback. The callback pattern can be converted to a promise, and have the resulting promise be treated as the result of the cell execution, that the notebook waits on to know when the cell has completed execution.\n", 122 | "\n", 123 | "Creating and returning a promise is simplified in notebooks via the helper `_.async()` method as shown below. This helper method accepts a function that is passed in a deferred result. This deferred result can be resolved or rejected when the async work completes successfully or with a failure." 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "collapsed": false, 129 | "input": [ 130 | "_.async(function(deferred) {\n", 131 | " var buffers = [];\n", 132 | " qr.image(greeting)\n", 133 | " .on('end', function() {\n", 134 | " var buffer = Buffer.concat(buffers);\n", 135 | " deferred.resolve(_.data.image(buffer));\n", 136 | " })\n", 137 | " .on('data', function(chunk) {\n", 138 | " buffers.push(chunk);\n", 139 | " });\n", 140 | "});" 141 | ], 142 | "language": "javascript", 143 | "metadata": {}, 144 | "outputs": [ 145 | { 146 | "metadata": {}, 147 | "output_type": "display_data", 148 | "png": "iVBORw0KGgoAAAANSUhEUgAAAKUAAAClCAAAAAAYQGIGAAABaUlEQVR42u3bMZKDMBAEQP7/aTu8BORZBDokmogyrqUJFqRBbJ8Zto2SkpKSkpKSkpKSkvJa5fZ7Oy64c/Tvt5OVKSnnUqaO+/5MSTm1cq//9o627xT1ypSU71G2f6OkpNyC0eJhFUrKlynr13DI+rdRMCXlcGWQQJzcG5zAUFKOVMZx9++TBFni3ek/JeVIZdCT9VtDvR4l5azKNJoozdPqY05KylmVbWp6XXusw8ukpJxfGXiD0WewR0m5kvLwGdiuX5qnXZRtUFI+TJlGgWnfDxlfUlI+R9luzNL7qfRlb/9znJLyicpgmhW0cnv1AyXlSsqgfrIcqOO7DErKuZTd6UXw5AvmfZSUEyr7e7K8AunGVU+UlMOVaWcHTV0KCikpF1GmfZrO7C5fcUtJObMyWKGXhoyUlC9Tphl6kIZQUq6kLB3tSd0pKVdSlr6KSE+clqKknFX5uI2SkpKSkpKSkpKSkvL89gVuT9kpLBq+OwAAAABJRU5ErkJggg==" 149 | } 150 | ], 151 | "prompt_number": 4 152 | } 153 | ], 154 | "metadata": {} 155 | } 156 | ] 157 | } -------------------------------------------------------------------------------- /samples/notebooks/JSON Data.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:92905052dfcb55ca99b8fd0e99725c0b9b022221186d8198a739483919b85f68" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "heading", 13 | "level": 1, 14 | "metadata": {}, 15 | "source": [ 16 | "Working with JSON Data" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "JSON data can be declared, parsed, and assigned to a variable using the " 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "collapsed": false, 29 | "input": [ 30 | "%%json --name data\n", 31 | "{\n", 32 | " \"text\": \"foo bar\",\n", 33 | " \"number\": 123,\n", 34 | " \"flag\": true,\n", 35 | " \"items\": [\n", 36 | " { \"t\": 100, \"value\": 3 },\n", 37 | " { \"t\": 100, \"value\": 4 }\n", 38 | " ]\n", 39 | "}" 40 | ], 41 | "language": "javascript", 42 | "metadata": {}, 43 | "outputs": [], 44 | "prompt_number": 1 45 | }, 46 | { 47 | "cell_type": "code", 48 | "collapsed": false, 49 | "input": [ 50 | "data.flag" 51 | ], 52 | "language": "javascript", 53 | "metadata": {}, 54 | "outputs": [ 55 | { 56 | "metadata": {}, 57 | "output_type": "display_data", 58 | "text": [ 59 | "true" 60 | ] 61 | } 62 | ], 63 | "prompt_number": 2 64 | }, 65 | { 66 | "cell_type": "code", 67 | "collapsed": false, 68 | "input": [ 69 | "data.items" 70 | ], 71 | "language": "javascript", 72 | "metadata": {}, 73 | "outputs": [ 74 | { 75 | "json": [ 76 | "[{\"t\":100,\"value\":3},{\"t\":100,\"value\":4}]" 77 | ], 78 | "metadata": {}, 79 | "output_type": "display_data" 80 | } 81 | ], 82 | "prompt_number": 3 83 | }, 84 | { 85 | "cell_type": "code", 86 | "collapsed": false, 87 | "input": [], 88 | "language": "javascript", 89 | "metadata": {}, 90 | "outputs": [] 91 | } 92 | ], 93 | "metadata": {} 94 | } 95 | ] 96 | } -------------------------------------------------------------------------------- /src/evaluator/commands.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // commands.js 14 | // Implements various commands (aka line and cell magics) available within the shell. 15 | // 16 | 17 | var util = require('util'); 18 | 19 | 20 | // Implements the %inspect command 21 | // This command can be used to inspect a variable, or expression within the shell. 22 | function inspectCommand(shell, args, data, evaluationId) { 23 | if (args.names) { 24 | args.names.forEach(function(n) { 25 | console.log(n + ':'); 26 | console.dir(shell.state[n]); 27 | console.log(); 28 | }); 29 | } 30 | } 31 | inspectCommand.options = function(parser) { 32 | return parser 33 | .help('Allows inspecting variables') 34 | .option('names', { 35 | list: true, 36 | position: 0, 37 | required: true, 38 | help: 'the variables to inspect' 39 | }); 40 | } 41 | 42 | 43 | // Initialize the shell with tne commands defined above, so they are available for use as 44 | // %% magics. 45 | function initialize(shell) { 46 | shell.registerCommand('inspect', inspectCommand); 47 | } 48 | 49 | 50 | module.exports = { 51 | initialize: initialize 52 | }; 53 | -------------------------------------------------------------------------------- /src/evaluator/dataCommands.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // dataCommands.js 14 | // Implements various commands (aka line and cell magics) available within the shell. 15 | // 16 | 17 | var util = require('util'); 18 | 19 | function dataCommand(shell, args, data, evaluationId, dataProcessor) { 20 | var deferred = shell.runtime.q.defer(); 21 | if (args.source) { 22 | shell.runtime.request.get(args.source, function(e, response, body) { 23 | if (e || (response.statusCode != 200)) { 24 | deferred.reject(e || shell.createError('Failed request')); 25 | } 26 | else { 27 | deferred.resolve(dataProcessor(response.body)); 28 | } 29 | }); 30 | } 31 | else { 32 | deferred.resolve(dataProcessor(data)); 33 | } 34 | 35 | return deferred.promise.then(function(data) { 36 | shell.state[args.name] = data; 37 | 38 | // Append a variable declaration for the name we just set to the same json data. 39 | shell.appendCode(util.format('var %s = %s;', args.name, data)); 40 | 41 | return args.show ? data : undefined; 42 | }); 43 | } 44 | 45 | function dataCommandParser(parser, help) { 46 | return parser 47 | .help(help) 48 | .option('name', { 49 | abbr: 'n', 50 | full: 'name', 51 | metavar: 'variable', 52 | type: 'string', 53 | required: true, 54 | help: 'the variable that will be assigned' 55 | }) 56 | .option('source', { 57 | abbr: 's', 58 | full: 'src', 59 | metavar: 'url', 60 | type: 'string', 61 | help: 'the URL to request' 62 | }) 63 | .option('show', { 64 | flag: true, 65 | default: false, 66 | help: 'whether the data should be displayed as well' 67 | }); 68 | } 69 | 70 | 71 | // Implements the %%text command. 72 | // This command can be used to initialize a string variable containing a span of literal text. 73 | // The text does not have to be specified as a string, i.e. quoted and escaped. 74 | function textCommand(shell, args, data, evaluationId) { 75 | return dataCommand(shell, args, data, evaluationId, function(value) { 76 | return value; 77 | }); 78 | } 79 | textCommand.options = function(parser) { 80 | return dataCommandParser(parser, 'Creates a string variable from the specified text.'); 81 | } 82 | 83 | 84 | // Implements the %%json command. 85 | // This command can be used to initialize a variable containing a plain old javascript object 86 | // parsed from JSON text. 87 | function jsonCommand(shell, args, data, evaluationId) { 88 | return dataCommand(shell, args, data, evaluationId, function(value) { 89 | return JSON.parse(value); 90 | }); 91 | } 92 | jsonCommand.options = function(parser) { 93 | return dataCommandParser(parser, 'Creates an object variable from the specified JSON text.'); 94 | } 95 | 96 | 97 | // Initialize the shell with tne commands defined above, so they are available for use as 98 | // %% magics. 99 | function initialize(shell) { 100 | shell.registerCommand('text', textCommand); 101 | shell.registerCommand('json', jsonCommand); 102 | } 103 | 104 | 105 | module.exports = { 106 | initialize: initialize 107 | }; 108 | -------------------------------------------------------------------------------- /src/evaluator/displayCommands.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // displayCommands.js 14 | // Implements various commands (aka line and cell magics) available within the shell. 15 | // 16 | 17 | var util = require('util'); 18 | 19 | 20 | // Implements the %%html command. 21 | // This command simply converts the specified text into an object that is rendered as HTML. 22 | function htmlCommand(shell, args, data, evaluationId) { 23 | return shell.runtime.data.html(data); 24 | } 25 | htmlCommand.options = function(parser) { 26 | return parser.help('Creates and renders HTML markup.'); 27 | } 28 | 29 | 30 | // Implements the %%script command. 31 | // This command can be used to execute script on the client, instead of on the server. 32 | function scriptCommand(shell, args, data, evaluationId) { 33 | return shell.runtime.data.script(data); 34 | } 35 | scriptCommand.options = function(parser) { 36 | return parser.help('Creates a script object that is executed in the browser.'); 37 | } 38 | 39 | 40 | // Initialize the shell with tne commands defined above, so they are available for use as 41 | // %% magics. 42 | function initialize(shell) { 43 | shell.registerCommand('html', htmlCommand); 44 | shell.registerCommand('script', scriptCommand); 45 | } 46 | 47 | 48 | module.exports = { 49 | initialize: initialize 50 | }; 51 | -------------------------------------------------------------------------------- /src/evaluator/error.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // error.js 14 | // A helper to create errors raised by the shell. 15 | // 16 | 17 | var util = require('util'); 18 | 19 | // Create Error objects with their trace suppressed. This is useful for generating error 20 | // message responses within the shell. 21 | function createError() { 22 | var e = new Error(util.format.apply(null, arguments)); 23 | e.trace = false; 24 | 25 | return e; 26 | } 27 | 28 | 29 | module.exports = { 30 | create: createError 31 | }; 32 | -------------------------------------------------------------------------------- /src/evaluator/extensions.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // extensions.js 14 | // This module contains functionality associated with loading shell extensions. 15 | // 16 | 17 | var path = require('path'); 18 | var installer = require('../utils/installer'); 19 | 20 | // Implements the %extension command, that can be used to install a specific named 21 | // module as a shell extension, using npm. 22 | function extensionCommand(shell, args, data, evaluationId) { 23 | var deferred = shell.runtime.q.defer(); 24 | 25 | var name = args.name; 26 | var moduleName = 'ijs.ext.' + name; 27 | var modulePath = args.path || moduleName; 28 | 29 | installer.install(modulePath, shell.config.userPath, /* quiet */ true, function(error) { 30 | if (error) { 31 | deferred.reject(shell.createError('Unable to install extension module "%s"', moduleName)); 32 | } 33 | else { 34 | var extensionPath = path.join(shell.config.userPath, 'node_modules', moduleName); 35 | var extension = require(extensionPath); 36 | 37 | try { 38 | extension.initialize(shell, function(error, result) { 39 | if (error) { 40 | deferred.reject(shell.createError('Error initializing extension')); 41 | } 42 | else { 43 | shell.loadedExtensions[name] = true; 44 | deferred.resolve(result); 45 | } 46 | }); 47 | } 48 | catch(e) { 49 | deferred.reject(shell.createError('Error initializing extension')); 50 | } 51 | } 52 | }) 53 | 54 | return deferred.promise; 55 | } 56 | extensionCommand.options = function(parser) { 57 | return parser 58 | .help('Loads the specified extensions') 59 | .option('name', { 60 | position: 0, 61 | required: true, 62 | help: 'the name of the extension to install' 63 | }) 64 | .option('path', { 65 | abbr: 'p', 66 | full: 'path', 67 | metavar: 'path', 68 | type: 'string', 69 | required: false, 70 | help: 'the local path of the extension' 71 | }); 72 | } 73 | 74 | 75 | // Implements the %extensions command, that can be used to list the names of loaded extensions. 76 | function extensionsCommand(shell, args, data, evaluationId) { 77 | var names = []; 78 | for (var n in shell.loadedExtensions) { 79 | names.push(n); 80 | } 81 | 82 | console.log(names.join('\n')); 83 | } 84 | extensionsCommand.options = function(parser) { 85 | return parser.help('Lists the set of loaded extensions.'); 86 | } 87 | 88 | 89 | // Initializes the shell with extensions related functionality. 90 | function initialize(shell) { 91 | shell.loadedExtensions = {}; 92 | 93 | shell.registerCommand('extension', extensionCommand); 94 | shell.registerCommand('extensions', extensionsCommand); 95 | } 96 | 97 | 98 | module.exports = { 99 | initialize: initialize 100 | }; 101 | -------------------------------------------------------------------------------- /src/evaluator/modules.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // modules.js 14 | // This module contains all the functionality associated with using (installing and loading) 15 | // node modules within the shell. 16 | // 17 | 18 | var path = require('path'); 19 | var installer = require('../utils/installer'); 20 | 21 | // List of built-in and custom modules available from the shell 22 | // without needing to first install. 23 | var _knownModules = { 24 | crypto: 'crypto', 25 | events: 'events', 26 | fs: 'fs', 27 | http: 'http', 28 | https: 'https', 29 | net: 'net', 30 | os: 'os', 31 | path: 'path', 32 | querystring: 'querystring', 33 | stream: 'stream', 34 | url: 'url', 35 | util: 'util', 36 | zlib: 'zlib' 37 | }; 38 | 39 | 40 | // Implements the %module command, that can be used to install a specific named 41 | // module, using npm. The module gets installed into 'node_modules' within the path 42 | // specified in configuration. 43 | function moduleCommand(shell, args, data, evaluationId) { 44 | var deferred = shell.runtime.q.defer(); 45 | 46 | installer.install(args.name, shell.config.userPath, /* quiet */ false, function(error) { 47 | if (error) { 48 | deferred.reject(shell.createError('Could not install module')); 49 | } 50 | else { 51 | shell.installedModules[args.name] = true; 52 | deferred.resolve(); 53 | } 54 | }); 55 | 56 | return deferred.promise; 57 | } 58 | moduleCommand.options = function(parser) { 59 | return parser 60 | .help('Installs the specified module and makes it available for use') 61 | .option('name', { 62 | position: 0, 63 | required: true, 64 | help: 'the name of the module to install' 65 | }); 66 | } 67 | 68 | 69 | // Implements the %modules command, that can be used to list the names of installed modules. 70 | function modulesCommand(shell, args, data, evaluationId) { 71 | var names = []; 72 | for (var n in shell.installedModules) { 73 | names.push(n); 74 | } 75 | 76 | console.log(names.join('\n')); 77 | } 78 | modulesCommand.options = function(parser) { 79 | return parser.help('Lists the set of installed modules.'); 80 | } 81 | 82 | 83 | // The implementation of the 'require' method that is made available within the shell. 84 | // This satisfies required modules using a combination of known modules (standard and other 85 | // modules available via the shell) and custom modules (previously installed using %module). 86 | function customRequire(shell, name) { 87 | var module = shell.requiredModules[name]; 88 | if (module) { 89 | return module; 90 | } 91 | 92 | if (_knownModules[name]) { 93 | module = require(name); 94 | } 95 | else if (shell.installedModules[name]) { 96 | // Directly load up a specified custom module from where it would have been installed 97 | // when using the %module command. 98 | var modulePath = path.join(shell.config.userPath, 'node_modules', name); 99 | module = require(modulePath); 100 | } 101 | 102 | if (module) { 103 | shell.requiredModules[name] = module; 104 | } 105 | else { 106 | throw shell.createError('Unknown module "%s". Make sure it has been installed via %module.', 107 | name); 108 | } 109 | 110 | return module; 111 | }; 112 | 113 | 114 | // Initializes the shell with module related functionality. 115 | // - The required and installed modules are tracked, and an implementation of a shell-scoped 116 | // require is created. 117 | // - The %module and %modules commands are also registered with the shell. 118 | function initialize(shell) { 119 | shell.requiredModules = {}; 120 | shell.installedModules = {}; 121 | 122 | shell.state.require = function(name) { 123 | return customRequire(shell, name); 124 | }; 125 | 126 | shell.registerCommand('module', moduleCommand); 127 | shell.registerCommand('modules', modulesCommand); 128 | } 129 | 130 | 131 | module.exports = { 132 | initialize: initialize 133 | }; 134 | -------------------------------------------------------------------------------- /src/evaluator/shell.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // shell.js 14 | // Implements the shell functionality used by the kernel. This manages evaluation state, and 15 | // executes code against that state. 16 | // 17 | 18 | var nomnom = require('nomnom'), 19 | tern = require('tern'), 20 | vm = require('vm'); 21 | 22 | var ijsrt = require('ijs.runtime'); 23 | 24 | var error = require('./error'), 25 | extensions = require('./extensions'), 26 | modules = require('./modules'); 27 | 28 | 29 | // Creates the global objects variables that serves as the initial state managed by the shell. 30 | function createGlobals(shell) { 31 | var globals = { 32 | Buffer: Buffer, 33 | console: console, 34 | clearImmediate: clearImmediate, 35 | clearInterval: clearInterval, 36 | clearTimeout: clearTimeout, 37 | setImmediate: setImmediate, 38 | setInterval: setInterval, 39 | setTimeout: setTimeout, 40 | _: ijsrt 41 | }; 42 | 43 | globals.global = globals; 44 | 45 | return globals; 46 | } 47 | 48 | // Converts a result/error tuple into a promise, if the result is not already a promise. 49 | function createPromise(result, error) { 50 | // Create a pre-rejected promise for errors. 51 | if (error) { 52 | var deferred = ijsrt.q.defer(); 53 | deferred.reject(error); 54 | 55 | return deferred.promise; 56 | } 57 | 58 | // Create a pre-resolved promise with the result when the result is not a promise. 59 | if ((result === null) || (result === undefined) || 60 | (typeof result != 'object') || 61 | (typeof result.then != 'function')) { 62 | var deferred = ijsrt.q.defer(); 63 | deferred.resolve(result); 64 | 65 | return deferred.promise; 66 | } 67 | 68 | return result; 69 | } 70 | 71 | 72 | // The Shell object to manage configuration, shell functionality and session state. 73 | function Shell(config) { 74 | this.config = config; 75 | this.commands = {}; 76 | this.runtime = ijsrt; 77 | this.state = vm.createContext(createGlobals(this)); 78 | this.code = ''; 79 | 80 | require('../../node_modules/tern/plugin/node.js'); 81 | var ternOptions = { 82 | defs: [require('../../node_modules/tern/defs/ecma5.json')], 83 | plugins: { node: {} } 84 | }; 85 | this.ternServer = new tern.Server(ternOptions); 86 | } 87 | 88 | // Appends code to the shell's code buffer 89 | Shell.prototype.appendCode = function(code) { 90 | // Append the new code to the code buffer if it didn't look like a re-execution of 91 | // previously executed code. 92 | if (this.code.indexOf(code) < 0) { 93 | this.code += '\n' + code; 94 | } 95 | } 96 | 97 | // Creates errors for the purposes of displaying just an error message, without any stack trace. 98 | Shell.prototype.createError = function() { 99 | return error.create.apply(null, arguments); 100 | } 101 | 102 | // Creates traces for errors raised within the shell. It removes the Shell and underlying 103 | // kernel-specific stack frames to provide a user code-only trace. 104 | Shell.prototype.createTrace = function(error) { 105 | if (error.constructor != Error) { 106 | return [ error.toString() ]; 107 | } 108 | 109 | var lines = (error.stack || '').split('\n'); 110 | 111 | var trace = []; 112 | for (var i = 0; i < lines.length; i++) { 113 | if (lines[i].indexOf('Shell.') > 0) { 114 | break; 115 | } 116 | trace.push(lines[i]); 117 | } 118 | 119 | return trace; 120 | } 121 | 122 | // Attempts to find a list of matches at the specified position in the specified text block. 123 | // Returns a set of completions and the text being completed. 124 | Shell.prototype.complete = function(text, position) { 125 | var codeBuffer = this.code + '\n' + text; 126 | position += this.code.length + 1; 127 | 128 | var query = { type: 'completions', file: 'code', end: position }; 129 | var file = { type: 'full', name: 'code', text: codeBuffer }; 130 | 131 | var deferred = ijsrt.q.defer(); 132 | this.ternServer.request({ query: query, files: [file] }, function(error, response) { 133 | if (!error) { 134 | var result = { 135 | completions: response.completions, 136 | prefix: codeBuffer.substr(response.start, response.end) 137 | }; 138 | 139 | deferred.resolve(result); 140 | } 141 | }); 142 | 143 | return deferred.promise; 144 | } 145 | 146 | // Evalutes the user's input in context of the shell's state to produce an evaluation result. 147 | Shell.prototype.evaluate = function(text, evaluationId) { 148 | if (text.charAt(0) === '%') { 149 | return this._evaluateCommand(text, evaluationId); 150 | } 151 | else { 152 | return this._evaluateCode(text, evaluationId); 153 | } 154 | } 155 | 156 | // Evaluates code within the shell. The code executes in context of the shell's state, and 157 | // any side-effects to that state are preserved. The value resulting from the final expression 158 | // within the code is used as the result of the evaluation. 159 | Shell.prototype._evaluateCode = function(code, evaluationId) { 160 | var shell = this; 161 | 162 | var result = undefined; 163 | var error = null; 164 | try { 165 | // Use the evaluation id to identify this code when it shows up in stack traces. 166 | // Turn off automatic display of errors for things like syntax errors. 167 | 168 | var options = { filename: 'code', displayErrors: false }; 169 | options.toString = function() { 170 | return 'code[' + evaluationId + ']'; 171 | }; 172 | 173 | result = vm.runInContext(code, this.state, options); 174 | } 175 | catch(e) { 176 | error = e; 177 | } 178 | 179 | // Convert the result into a promise (if it isn't already one). Both sync and async results 180 | // are handled in the same way. 181 | var promise = createPromise(result, error); 182 | return promise.then(function(result) { 183 | // Append the code, since it successfully completed execution. 184 | shell.appendCode(code); 185 | return result; 186 | }); 187 | } 188 | 189 | // Evaluates as % or %% command (aka line or cell magic). 190 | Shell.prototype._evaluateCommand = function(text, evaluationId) { 191 | var result = undefined; 192 | var error = null; 193 | 194 | try { 195 | var commandInfo = this._parseCommand(text); 196 | if (commandInfo) { 197 | result = commandInfo.command(this, commandInfo.args, commandInfo.data, evaluationId); 198 | } 199 | } 200 | catch(e) { 201 | error = e; 202 | } 203 | 204 | return createPromise(result, error); 205 | } 206 | 207 | // Attempts to parse a % or %% command into a command function along with associated arguments 208 | // and data. 209 | Shell.prototype._parseCommand = function(text) { 210 | // Treat everything after the first line as data. 211 | // TODO: Support for multi-line command lines when the line ends with a '\' terminator. 212 | var data = ''; 213 | var newLine = text.indexOf('\n'); 214 | if (newLine > 0) { 215 | data = text.substr(newLine + 1); 216 | text = text.substr(0, newLine); 217 | } 218 | 219 | // Either %name or %%name followed by a command line (that will be parsed as arguments). 220 | var commandPattern = /^%%?([a-zA-Z0-9\\._]+)(\s+)?(.*)?$/; 221 | var match = commandPattern.exec(text); 222 | if (!match) { 223 | throw error.create('Invalid command syntax.'); 224 | } 225 | 226 | var name = match[1]; 227 | var command = this.commands[name]; 228 | if (!command) { 229 | throw error.create('Unknown command named "%s".', name); 230 | } 231 | 232 | var argsParser = 233 | nomnom().script(name).nocolors().printer(function(s, code) { 234 | if (code) { 235 | throw error.create(s); 236 | } 237 | 238 | console.log(s); 239 | }); 240 | var args = match[3] || ''; 241 | args = args.trim(); 242 | args = args.length ? args.split(' ') : []; 243 | args = command.options(argsParser).parse(args); 244 | 245 | if (args) { 246 | return { 247 | command: command, 248 | args: args, 249 | data: data 250 | }; 251 | } 252 | 253 | // There was an problem parsing arguments (the error itself already printed out by the argparser) 254 | return null; 255 | } 256 | 257 | // Registers a command for use in the shell via a cell magic syntax, i.e. %name or %%name. 258 | Shell.prototype.registerCommand = function(name, command) { 259 | this.commands[name] = command; 260 | } 261 | 262 | 263 | // Creates and initializes a Shell object. 264 | function createShell(config, callback) { 265 | var shell = new Shell(config); 266 | 267 | modules.initialize(shell); 268 | extensions.initialize(shell); 269 | 270 | require('./commands').initialize(shell); 271 | require('./displayCommands').initialize(shell); 272 | require('./dataCommands').initialize(shell); 273 | 274 | process.nextTick(function() { 275 | callback(shell); 276 | }); 277 | } 278 | 279 | 280 | module.exports = { 281 | create: createShell 282 | }; 283 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // index.js 14 | // Entrypoint for the kernel. 15 | // 16 | 17 | var fs = require('fs'), 18 | nomnom = require('nomnom'); 19 | 20 | var Session = require('./protocol/session'), 21 | Shell = require('./evaluator/shell'); 22 | 23 | // The main method which parses input arguments, creates a Shell providing evaluation functionality 24 | // and the Session object to handle the kernel protocol. 25 | function main() { 26 | var parser = nomnom(); 27 | parser.script('ijs') 28 | .nocolors() 29 | .printer(function(s, code) { 30 | console.log(s); 31 | if (code) { 32 | process.exit(code); 33 | } 34 | }) 35 | .option('version', { 36 | abbr: 'v', 37 | flag: true, 38 | help: 'print version and exit', 39 | callback: function() { 40 | console.log('0.1.0'); 41 | process.exit(0); 42 | } 43 | }) 44 | .option('userPath', { 45 | abbr: 'u', 46 | full: 'userPath', 47 | metavar: 'path', 48 | type: 'string', 49 | required: true, 50 | help: 'path that will contain installed node modules', 51 | callback: function(userPath) { 52 | if (!fs.existsSync(userPath) || !fs.statSync(userPath).isDirectory()) { 53 | return 'expected an existing directory for the userPath option'; 54 | } 55 | return null; 56 | } 57 | }) 58 | .option('connectionFile', { 59 | position: 0, 60 | required: true, 61 | help: 'path to file containing kernel connection information' 62 | }); 63 | var options = parser.parse(process.argv.slice(2)); 64 | 65 | if (options) { 66 | var shellConfig = { 67 | userPath: options.userPath 68 | }; 69 | 70 | var connectionConfig = JSON.parse(fs.readFileSync(options.connectionFile, 71 | { encoding: 'utf8' })); 72 | 73 | Shell.create(shellConfig, function(shell) { 74 | Session.run(shell, connectionConfig); 75 | }); 76 | } 77 | } 78 | 79 | main(); 80 | -------------------------------------------------------------------------------- /src/protocol/display.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // display.js 14 | // Implements conversion of values resulting from evaluations into display data for rendering 15 | // within a notebook. 16 | // 17 | 18 | var util = require('util'); 19 | 20 | // Generates display data, an object with key/value pairs, where each key is a mime type. 21 | // Unless the value is null/undefined, a plain text representation is always produced if no other 22 | // specific representation can be generated. 23 | function createDisplayData(value) { 24 | var displayData = {}; 25 | 26 | if ((value === null) || (value === undefined)) { 27 | return displayData; 28 | } 29 | 30 | var useFallbacks = true; 31 | 32 | if (typeof value.toHTML == 'function') { 33 | displayData['text/html'] = value.toHTML(); 34 | useFallbacks = false; 35 | } 36 | 37 | if (typeof value.toScript == 'function') { 38 | displayData['application/javascript'] = value.toScript(); 39 | useFallbacks = false; 40 | } 41 | 42 | if (typeof value.toImage == 'function') { 43 | var buffer = value.toImage(); 44 | if (buffer) { 45 | var data = buffer.toString('base64'); 46 | var mime = buffer.mime || 'image/png'; 47 | 48 | displayData[mime] = data; 49 | useFallbacks = false; 50 | } 51 | } 52 | 53 | if (typeof value.toText == 'function') { 54 | var text = value.toText(); 55 | if (text) { 56 | displayData['text/plain'] = text; 57 | useFallbacks = false; 58 | } 59 | } 60 | 61 | if (useFallbacks) { 62 | if ((value.constructor == Object) || 63 | (value.constructor == Array)) { 64 | displayData['application/json'] = JSON.stringify(value); 65 | } 66 | else if (value.constructor == Buffer) { 67 | var mime = value.mime || 'application/octet-stream'; 68 | displayData[mime] = value.toString('base64'); 69 | } 70 | else { 71 | displayData['text/plain'] = value.toString(); 72 | } 73 | } 74 | 75 | return displayData; 76 | } 77 | 78 | 79 | module.exports = { 80 | data: createDisplayData 81 | }; 82 | -------------------------------------------------------------------------------- /src/protocol/evaluation.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // evaluation.js 14 | // Represents a single evaluation within the kernel. 15 | // 16 | 17 | var streams = require('../utils/streams'); 18 | 19 | var _evaluationCounter = 0; 20 | 21 | // An Evaluation object that serves as context for a given code execution within the kernel. 22 | // It sets up stdout/stderr capturing and restoring as well as async completion of the evaluation. 23 | function Evaluation(evaluator, streamHandler) { 24 | _evaluationCounter++; 25 | this.counter = _evaluationCounter; 26 | 27 | this._evaluator = evaluator; 28 | this._streamHandler = streamHandler; 29 | } 30 | 31 | // Executes the specified text, and returns a promise that is resolved or rejected with the result 32 | // or the error raised during evaluation. 33 | Evaluation.prototype.execute = function(text) { 34 | // Capture the streams 35 | var stdout = streams.stdout(this._streamHandler); 36 | var stderr = streams.stderr(this._streamHandler); 37 | 38 | // Let the evaluator do its thing to produce a result 39 | var promise = this._evaluator.evaluate(text, this.counter); 40 | return promise.fin(function() { 41 | // Ensure that the streams are restored once the promise has been resolved or rejected. 42 | // This implies that the streams remain in captured state during async work if the evaluation 43 | // results in a promise that asynchronously completes. 44 | stdout.restore(); 45 | stderr.restore(); 46 | }); 47 | } 48 | 49 | 50 | // Creates a new Evaluation object given an evaluator and an output handler. 51 | function createEvaluation(evaluator, streamHandler) { 52 | return new Evaluation(evaluator, streamHandler); 53 | } 54 | 55 | 56 | module.exports = { 57 | create: createEvaluation 58 | }; 59 | -------------------------------------------------------------------------------- /src/protocol/handlers.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // handlers.js 14 | // Implements message handlers supported by this kernel to process incoming messages and 15 | // generate out-going messages. 16 | // 17 | 18 | var messages = require('./messages'), 19 | queue = require('./queue'); 20 | 21 | var _session; 22 | var _queue; 23 | 24 | // Handles the kernel info request to produce a kernel info response. 25 | function kernelInfoHandler(message) { 26 | var infoMessage = messages.kernelInfoResponse(message); 27 | messages.write(infoMessage, _session.shell, _session.signer); 28 | } 29 | 30 | // Handles the kernel shutdown message. It simply kills the current process. 31 | function shutdownHandler(message) { 32 | process.exit(0); 33 | } 34 | 35 | // Handles execute requests by queuing them into the proessing queue. 36 | function executeHandler(message) { 37 | _queue.add(message); 38 | } 39 | 40 | function completeHandler(message) { 41 | var promise = _session.evaluator.complete(message.content.text, message.content.cursor_pos); 42 | promise.then(function(result) { 43 | var replyMessage = messages.completions(message, result.prefix, result.completions); 44 | messages.write(replyMessage, _session.shell, _session.signer); 45 | }); 46 | } 47 | 48 | // Creates the message handlers associated with the kernel session. 49 | function createHandlers(session) { 50 | _session = session; 51 | _queue = queue.create(session); 52 | 53 | var handlers = {}; 54 | handlers[messages.names.kernelInfoRequest] = kernelInfoHandler; 55 | handlers[messages.names.shutdownRequest] = shutdownHandler; 56 | handlers[messages.names.executeRequest] = executeHandler; 57 | handlers[messages.names.completeRequest] = completeHandler; 58 | 59 | return handlers; 60 | } 61 | 62 | 63 | module.exports = { 64 | create: createHandlers 65 | }; 66 | -------------------------------------------------------------------------------- /src/protocol/messages.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // messages.js 14 | // This module provides functionality to create new messages as well as the ability to 15 | // read and write messages from and to a transport socket. 16 | // 17 | 18 | var uuid = require('node-uuid'); 19 | 20 | var _messageDelimiter = ''; 21 | var _messageNames = { 22 | kernelInfoRequest: 'kernel_info_request', 23 | kernelInfoResponse: 'kernel_info_reply', 24 | shutdownRequest: 'shutdown_request', 25 | shutdownResponse: 'shutdown_reply', 26 | executeRequest: 'execute_request', 27 | executeResponse: 'execute_reply', 28 | completeRequest: 'complete_request', 29 | completeResponse: 'complete_reply', 30 | status: 'status', 31 | displayData: 'display_data', 32 | stream: 'stream' 33 | }; 34 | 35 | // Helper method to create a message object from its independent pieces. 36 | function createMessage(identities, header, parentHeader, metadata, content) { 37 | return { 38 | identities: identities, 39 | header: header, 40 | parentHeader: parentHeader, 41 | metadata: metadata, 42 | content: content 43 | }; 44 | } 45 | 46 | // Creates a new message of the specified type, in association to a parent (incoming message) 47 | // and optional metadata and content. 48 | function newMessage(type, parentMessage, content, metadata) { 49 | var header = { 50 | msg_type: type, 51 | msg_id: uuid.v4(), 52 | session: parentMessage.header.session, 53 | username: parentMessage.header.username 54 | }; 55 | 56 | metadata = metadata || {}; 57 | content = content || {}; 58 | return createMessage(parentMessage.identities, header, parentMessage.header, 59 | metadata, content); 60 | } 61 | 62 | // Creates a kernel info response message. 63 | function createKernelInfoResponseMessage(parentMessage) { 64 | var content = { 65 | language: 'javascript', 66 | language_version: [1,0], 67 | protocol_version: [4,1] 68 | }; 69 | return newMessage(_messageNames.kernelInfoResponse, parentMessage, content); 70 | } 71 | 72 | // Creates an error execution reply message. 73 | function createExecuteErrorResponseMessage(parentMessage, executionCount, error, traceback) { 74 | var content = { 75 | status: 'error', 76 | execution_count: executionCount, 77 | ename: error.constructor.name, 78 | evalue: error.toString(), 79 | traceback: traceback 80 | }; 81 | 82 | return newMessage(_messageNames.executeResponse, parentMessage, content); 83 | } 84 | 85 | // Creates a success execution reply message. 86 | function createExecuteSuccessResponseMessage(parentMessage, executionCount, metadata) { 87 | var content = { 88 | status: 'ok', 89 | execution_count: executionCount, 90 | payload: [], 91 | user_variables: {}, 92 | user_expressions: {} 93 | }; 94 | 95 | return newMessage(_messageNames.executeResponse, parentMessage, content, metadata); 96 | } 97 | 98 | // Create a completions list reply message 99 | function createCompleteInfoResponseMessage(parentMessage, matchedText, matches, metadata) { 100 | var content = { 101 | status: 'ok', 102 | matched_text: matchedText, 103 | matches: matches 104 | }; 105 | 106 | return newMessage(_messageNames.completeResponse, parentMessage, content, metadata); 107 | } 108 | 109 | // Creates a display data message for sending results of an execution. 110 | function createDataMessage(parentMessage, representations) { 111 | var content = { 112 | data: representations 113 | }; 114 | 115 | return newMessage(_messageNames.displayData, parentMessage, content); 116 | } 117 | 118 | // Creates a stream message for sending text written to stdout/stderr streams. 119 | function createStreamMessage(parentMessage, streamName, data) { 120 | var content = { 121 | name: streamName, 122 | data: data 123 | }; 124 | 125 | return newMessage(_messageNames.stream, parentMessage, content); 126 | } 127 | 128 | // Creates a status message to communicate kernel idle/busy status. 129 | function createStatusMessage(parentMessage, busy) { 130 | var content = { 131 | execution_state: busy ? 'busy' : 'idle' 132 | }; 133 | 134 | return newMessage(_messageNames.status, parentMessage, content); 135 | } 136 | 137 | // Helper to read in a message from incoming message data. 138 | function readMessage(socketData, signer) { 139 | var identities = socketData[0]; 140 | var signature = socketData[2].toString(); 141 | var header = socketData[3]; 142 | var parentHeader = socketData[4]; 143 | var metadata = socketData[5]; 144 | var content = socketData[6]; 145 | 146 | if (!signer.validate(signature, [ header, parentHeader, metadata, content ])) { 147 | return null; 148 | } 149 | 150 | return createMessage(identities, 151 | JSON.parse(header), 152 | JSON.parse(parentHeader), 153 | JSON.parse(metadata), 154 | JSON.parse(content)); 155 | } 156 | 157 | // Helper to write out a message to as outgoing message data. 158 | function writeMessage(message, socket, signer) { 159 | var header = JSON.stringify(message.header); 160 | var parentHeader = JSON.stringify(message.parentHeader); 161 | var metadata = JSON.stringify(message.metadata); 162 | var content = JSON.stringify(message.content); 163 | 164 | var signature = signer.sign([ header, parentHeader, metadata, content ]); 165 | 166 | var socketData = [ 167 | message.identities, 168 | '', 169 | signature, 170 | header, 171 | parentHeader, 172 | metadata, 173 | content 174 | ]; 175 | socket.send(socketData); 176 | } 177 | 178 | 179 | module.exports = { 180 | names: _messageNames, 181 | kernelInfoResponse: createKernelInfoResponseMessage, 182 | status: createStatusMessage, 183 | error: createExecuteErrorResponseMessage, 184 | success: createExecuteSuccessResponseMessage, 185 | completions: createCompleteInfoResponseMessage, 186 | data: createDataMessage, 187 | stream: createStreamMessage, 188 | read: readMessage, 189 | write: writeMessage 190 | }; 191 | -------------------------------------------------------------------------------- /src/protocol/queue.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // queue.js 14 | // Implements the message handling queue. 15 | // 16 | 17 | var display = require('./display'), 18 | evaluation = require('./evaluation'), 19 | messages = require('./messages'); 20 | 21 | var _session; 22 | var _messages; 23 | var _idle; 24 | 25 | // Adds a message to the queue 26 | function addMessage(message) { 27 | var text = message.content ? message.content.code.trim() : ''; 28 | if (!text) { 29 | return; 30 | } 31 | 32 | message.content.code = text; 33 | _messages.push(message); 34 | 35 | // If there is no message already being processed, go ahead and process this message 36 | // immediately. 37 | if (_idle) { 38 | processNextMessage(); 39 | } 40 | } 41 | 42 | // Processes execute requests to produce an execute reply message, as well as related messages 43 | // to send back display data, stdout/stderr streams, and busy/idle messages. 44 | function processMessage(message) { 45 | var complete = false; 46 | function outputHandler(name, str) { 47 | if (!complete) { 48 | var streamMessage = messages.stream(message, name, str); 49 | messages.write(streamMessage, _session.io, _session.signer); 50 | } 51 | } 52 | 53 | var currentEvaluation = evaluation.create(_session.evaluator, outputHandler); 54 | var result = currentEvaluation.execute(message.content.code); 55 | 56 | result.then(function(value) { 57 | // Success ... send the result as display data if there was a result (i.e. don't send back 58 | // data to render null or undefined) 59 | if ((value !== undefined) && (value !== null)) { 60 | var data = display.data(value); 61 | var dataMessage = messages.data(message, data); 62 | messages.write(dataMessage, _session.io, _session.signer); 63 | } 64 | 65 | // Send the execute reply indicating success 66 | var replyMessage = messages.success(message, currentEvaluation.counter); 67 | messages.write(replyMessage, _session.shell, _session.signer); 68 | }) 69 | .fail(function(error) { 70 | var traceback = null; 71 | 72 | if (error) { 73 | // Send a trace for the error 74 | var errorOutput; 75 | if (error.trace === false) { 76 | // If an error has been marked as trace suppress, just use the message. This is to 77 | // communicate error in user's input as opposed to error raised during execution. 78 | errorOutput = error.message; 79 | } 80 | else { 81 | var trace = _session.evaluator.createTrace(error); 82 | errorOutput = trace.join('\n'); 83 | 84 | // The traceback field of the reply should not contain the error message, 85 | // just the stack trace. 86 | traceback = trace.splice(1).map(function(s) { return s.trim().substr(3); }); 87 | } 88 | 89 | var errorMessage = messages.stream(message, 'stderr', errorOutput); 90 | messages.write(errorMessage, _session.io, _session.signer); 91 | } 92 | else { 93 | error = new Error('Error'); 94 | } 95 | 96 | // Send the execute reply indicating error 97 | var replyMessage = messages.error(message, currentEvaluation.counter, error, traceback); 98 | messages.write(replyMessage, _session.shell, _session.signer); 99 | }) 100 | .fin(function() { 101 | // Finally process the next message 102 | processNextMessage(message); 103 | }) 104 | .done(); 105 | } 106 | 107 | function processNextMessage(lastMessage) { 108 | var message = _messages.shift(); 109 | if (message) { 110 | if (_idle) { 111 | // State transitioning from idle to busy 112 | _idle = false; 113 | 114 | // Send the busy message 115 | var busyMessage = messages.status(message, /* busy */ true); 116 | messages.write(busyMessage, _session.io, _session.signer); 117 | } 118 | 119 | processMessage(message); 120 | } 121 | else { 122 | if (!_idle) { 123 | // State transitioning from busy back to idle, because there was no message to be 124 | // processed. 125 | _idle = true; 126 | 127 | // Send the idle message 128 | var idleMessage = messages.status(lastMessage, /* busy */ false); 129 | messages.write(idleMessage, _session.io, _session.signer); 130 | } 131 | } 132 | } 133 | 134 | // Creates the message handling queue associated with the kernel session. 135 | function createQueue(session) { 136 | _session = session; 137 | _messages = []; 138 | _idle = true; 139 | 140 | return { 141 | add: addMessage 142 | }; 143 | } 144 | 145 | 146 | module.exports = { 147 | create: createQueue 148 | }; 149 | -------------------------------------------------------------------------------- /src/protocol/session.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // session.js 14 | // This represents a kernel session. It implements the networking aspects of the kernel protocol 15 | // using ZMQ. 16 | // 17 | 18 | var util = require('util'), 19 | zmq = require('zmq'); 20 | 21 | var signers = require('./signers'), 22 | messages = require('./messages'), 23 | handlers = require('./handlers'); 24 | 25 | var _session = {}; 26 | 27 | // A helper to create a ZMQ socket (of the specified type) and an optional message handler 28 | // to handle incoming messages. 29 | function createSocket(type, ip, port, messageHandler) { 30 | var uri = util.format('tcp://%s:%d', ip, port); 31 | var socket = zmq.createSocket(type); 32 | 33 | socket.bind(uri, function(e) { }); 34 | if (messageHandler) { 35 | socket.on('message', messageHandler); 36 | } 37 | 38 | return socket; 39 | } 40 | 41 | // The heartbeat message handler. It simply echos the incoming message data. 42 | function heartbeatHandler(data) { 43 | _heartbeatSocket.send(data); 44 | } 45 | 46 | // The default message handler for both the Shell and Control sockets. This dispatches the message 47 | // to the appropriate message handler, based on the type of the message. 48 | function messageHandler() { 49 | var message = messages.read(arguments, _session.signer); 50 | if (!message) { 51 | return; 52 | } 53 | 54 | var handler = _session.handlers[message.header.msg_type]; 55 | if (handler) { 56 | handler(message); 57 | } 58 | } 59 | 60 | // Runs the session by creating the sockets, and sets up message handlers. 61 | function runSession(evaluator, config) { 62 | _session.signer = signers.create(config.signature_scheme, config.key); 63 | _session.io = createSocket('pub', config.ip, config.iopub_port); 64 | _session.shell = createSocket('xrep', config.ip, config.shell_port, messageHandler); 65 | _session.control = createSocket('xrep', config.ip, config.control_port, messageHandler); 66 | 67 | _session.evaluator = evaluator; 68 | _session.handlers = handlers.create(_session); 69 | 70 | createSocket('rep', config.ip, config.hb_port, heartbeatHandler); 71 | } 72 | 73 | 74 | module.exports = { 75 | run: runSession 76 | }; 77 | -------------------------------------------------------------------------------- /src/protocol/signers.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // signers.js 14 | // Provides messaging signer implementations as needed for the kernel protocol. 15 | // 16 | 17 | var crypto = require('crypto'); 18 | 19 | // A helper function to compute a signature using an hmac object. 20 | function computeSignature(values, signatureScheme, signatureKey) { 21 | var hmac = crypto.createHmac(signatureScheme, signatureKey); 22 | values.forEach(function(v) { 23 | hmac.update(v); 24 | }); 25 | 26 | return hmac.digest('hex'); 27 | } 28 | 29 | // Creates a signer given the specified schema and key. 30 | function createSigner(signatureScheme, signatureKey) { 31 | if (signatureKey) { 32 | // Create a SHA256-based signer that generates and validates signatures 33 | signatureScheme = signatureScheme || 'sha256'; 34 | if (signatureScheme.indexOf('hmac-') === 0) { 35 | signatureScheme = signatureScheme.substr(5); 36 | } 37 | return { 38 | sign: function(values) { 39 | return computeSignature(values, signatureScheme, signatureKey); 40 | }, 41 | validate: function(signature, values) { 42 | return signature === computeSignature(values, signatureScheme, signatureKey); 43 | } 44 | } 45 | } 46 | else { 47 | // Create a no-op signer 48 | return { 49 | sign: function() { return ''; }, 50 | validate: function() { return true; } 51 | } 52 | } 53 | } 54 | 55 | 56 | module.exports = { 57 | create: createSigner 58 | }; 59 | -------------------------------------------------------------------------------- /src/utils/installer.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // installer.js 14 | // Provides functionality to install modules via npm. 15 | // 16 | 17 | var npm = require('npm'); 18 | var streams = require('./streams'); 19 | 20 | function installModule(module, installPath, quiet, callback) { 21 | var stdout = null; 22 | if (quiet) { 23 | stdout = streams.stdout(function() {}); 24 | } 25 | 26 | var npmOptions = { 27 | // where modules should get installed 28 | prefix: installPath, 29 | 30 | // turn off npm spew, as well as its progress indicator 31 | loglevel: 'silent', 32 | spin: false, 33 | 34 | // make any other output (the list of installed modules) usable within the shell 35 | color: false, 36 | unicode: false 37 | }; 38 | npm.load(npmOptions, function() { 39 | npm.commands.install([ module ], function(error) { 40 | if (quiet) { 41 | stdout.restore(); 42 | } 43 | 44 | callback(error); 45 | }); 46 | }); 47 | } 48 | 49 | module.exports = { 50 | install: installModule 51 | }; 52 | -------------------------------------------------------------------------------- /src/utils/streams.js: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Interactive Computing project (https://github.com/interactivecomputing). 2 | // All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 | // except in compliance with the License. You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the 9 | // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | // either express or implied. See the License for the specific language governing permissions 11 | // and limitations under the License. 12 | // 13 | // streams.js 14 | // Provides functionality to capture a standard output stream. 15 | // 16 | 17 | // Captures a specific stream, and invokes the specified callback with the text written to the 18 | // stream instead. It returns a function that can be used to restore the captured stream. 19 | function captureStream(name, callback) { 20 | var stream = process[name]; 21 | var originalWrite = stream.write; 22 | 23 | stream.write = function(str) { 24 | callback(name, str); 25 | }; 26 | 27 | return { 28 | restore: function() { 29 | stream.write = originalWrite; 30 | } 31 | }; 32 | } 33 | 34 | // Helper to capture the stdout stream. 35 | function captureStdout(callback) { 36 | return captureStream('stdout', callback); 37 | } 38 | 39 | // Helper to capture the stderr stream. 40 | function captureStderr(callback) { 41 | return captureStream('stderr', callback); 42 | } 43 | 44 | 45 | module.exports = { 46 | stderr: captureStderr, 47 | stdout: captureStdout 48 | }; 49 | -------------------------------------------------------------------------------- /tools/ijs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir -p /tmp/notebooks 4 | mkdir -p /tmp/static 5 | mkdir -p /tmp/modules 6 | 7 | BASEDIR=$(cd "$(dirname "$0")/.."; pwd) 8 | 9 | ipython notebook \ 10 | --KernelManager.kernel_cmd="['node', '$BASEDIR/src/index.js', '-m', '/tmp/modules', '{connection_file}']" \ 11 | --NotebookApp.extra_static_paths="['./bin', '/tmp/static']" \ 12 | --notebook-dir=/tmp/notebooks \ 13 | --ip="*" --port=9999 \ 14 | --matplotlib=inline --no-mathjax --no-script --quiet 15 | 16 | -------------------------------------------------------------------------------- /tools/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export PATH=$PATH:/opt/pkgconfig/bin 3 | export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig/ 4 | 5 | --------------------------------------------------------------------------------