├── .gitignore ├── README.md ├── components └── font-awesome │ ├── fonts │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff │ └── package.json ├── ext ├── codemirror.css ├── codemirror.js ├── cssmode.js ├── font-awesome.min.css ├── javascriptmode.js ├── jquery-2.1.4.min.js └── pythonmode.js ├── index.html ├── jupyter-js-services ├── config.d.ts ├── config.js ├── contents.d.ts ├── contents.js ├── ikernel.d.ts ├── ikernel.js ├── index.d.ts ├── index.js ├── isession.d.ts ├── isession.js ├── kernel.d.ts ├── kernel.js ├── package.json ├── serialize.d.ts ├── serialize.js ├── session.d.ts ├── session.js ├── test.d.ts ├── test.js ├── utils.d.ts ├── utils.js ├── validate.d.ts └── validate.js ├── main.py ├── package.json ├── phosphor_demo.gif ├── scripts └── copycss.js ├── src ├── actions.ts ├── cell.ts ├── celltoolbar.ts ├── codecell.ts ├── codemirror-ipython.js ├── codemirror-ipythongfm.js ├── completer.ts ├── contexthint.ts ├── index.css ├── index.ts ├── ipython.min.css ├── keyboard.ts ├── keyboardmanager.ts ├── style.min.css ├── tooltip.ts ├── tsconfig.json └── utils.ts └── typings ├── codemirror └── codemirror.d.ts ├── es6-collections └── es6-collections.d.ts ├── es6-container-shim.d.ts ├── es6-dataview.d.ts ├── es6-promise └── es6-promise.d.ts ├── jquery └── jquery.d.ts ├── requirejs └── r.d.ts └── term.js.d.ts /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | *.user 3 | .DS_Store 4 | node_modules 5 | typings/tsd.d.ts 6 | npm-debug.log 7 | test/build 8 | coverage 9 | lib 10 | dist 11 | build 12 | docs 13 | src/*.js 14 | src/*.d.ts 15 | src/*.js.map 16 | notebook 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | This is a proof on concept demonstration of integrating the refactoring work 3 | on the Jupyter Notebook with Phosphor widgets. It is not meant to be a fully 4 | functional notebook. Future development will occur in jupyter/notebook and 5 | related jupyter repositories. Note, the demo assumes you have no other 6 | notebook servers running. Enjoy! 7 | 8 | 9 | To install: 10 | 11 | ``` 12 | npm install 13 | npm run build 14 | ``` 15 | 16 | Requires the development version of Jupyter Notebook: 17 | 18 | `pip install git+https://github.com/jupyter/notebook` 19 | 20 | To run the demo: 21 | 22 | `python main.py` 23 | 24 | 25 | Demo: 26 | 27 | Phosphor Demo 28 | -------------------------------------------------------------------------------- /components/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blink1073/jupyter_demo/906c18f008cd1ca60c440d07a981b8092fe9f3d9/components/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /components/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blink1073/jupyter_demo/906c18f008cd1ca60c440d07a981b8092fe9f3d9/components/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /components/font-awesome/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "font-awesome", 3 | "description": "The iconic font and CSS framework", 4 | "version": "4.4.0", 5 | "style": "css/font-awesome.css", 6 | "keywords": ["font", "awesome", "fontawesome", "icon", "font", "bootstrap"], 7 | "homepage": "http://fontawesome.io/", 8 | "bugs": { 9 | "url" : "http://github.com/FortAwesome/Font-Awesome/issues" 10 | }, 11 | "author": { 12 | "name": "Dave Gandy", 13 | "email": "dave@fontawesome.io", 14 | "web": "http://twitter.com/davegandy" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/FortAwesome/Font-Awesome.git" 19 | }, 20 | "contributors": [ 21 | { 22 | "name": "Rob Madole", 23 | "web": "http://twitter.com/robmadole" 24 | }, 25 | { 26 | "name": "Geremia Taglialatela", 27 | "web": "http://twitter.com/gtagliala" 28 | }, 29 | { 30 | "name": "Travis Chase", 31 | "web": "http://twitter.com/supercodepoet" 32 | } 33 | ], 34 | "license": "OFL-1.1 AND MIT", 35 | "dependencies": { 36 | }, 37 | "engines" : { 38 | "node" : ">=0.10.3" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ext/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | color: black; 8 | } 9 | 10 | /* PADDING */ 11 | 12 | .CodeMirror-lines { 13 | padding: 4px 0; /* Vertical padding around content */ 14 | } 15 | .CodeMirror pre { 16 | padding: 0 4px; /* Horizontal padding of content */ 17 | } 18 | 19 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 20 | background-color: white; /* The little square between H and V scrollbars */ 21 | } 22 | 23 | /* GUTTER */ 24 | 25 | .CodeMirror-gutters { 26 | border-right: 1px solid #ddd; 27 | background-color: #f7f7f7; 28 | white-space: nowrap; 29 | } 30 | .CodeMirror-linenumbers {} 31 | .CodeMirror-linenumber { 32 | padding: 0 3px 0 5px; 33 | min-width: 20px; 34 | text-align: right; 35 | color: #999; 36 | -moz-box-sizing: content-box; 37 | box-sizing: content-box; 38 | } 39 | 40 | .CodeMirror-guttermarker { color: black; } 41 | .CodeMirror-guttermarker-subtle { color: #999; } 42 | 43 | /* CURSOR */ 44 | 45 | .CodeMirror div.CodeMirror-cursor { 46 | border-left: 1px solid black; 47 | } 48 | /* Shown when moving in bi-directional text */ 49 | .CodeMirror div.CodeMirror-secondarycursor { 50 | border-left: 1px solid silver; 51 | } 52 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursor { 53 | width: auto; 54 | border: 0; 55 | background: #7e7; 56 | } 57 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursors { 58 | z-index: 1; 59 | } 60 | 61 | .cm-animate-fat-cursor { 62 | width: auto; 63 | border: 0; 64 | -webkit-animation: blink 1.06s steps(1) infinite; 65 | -moz-animation: blink 1.06s steps(1) infinite; 66 | animation: blink 1.06s steps(1) infinite; 67 | } 68 | @-moz-keyframes blink { 69 | 0% { background: #7e7; } 70 | 50% { background: none; } 71 | 100% { background: #7e7; } 72 | } 73 | @-webkit-keyframes blink { 74 | 0% { background: #7e7; } 75 | 50% { background: none; } 76 | 100% { background: #7e7; } 77 | } 78 | @keyframes blink { 79 | 0% { background: #7e7; } 80 | 50% { background: none; } 81 | 100% { background: #7e7; } 82 | } 83 | 84 | /* Can style cursor different in overwrite (non-insert) mode */ 85 | div.CodeMirror-overwrite div.CodeMirror-cursor {} 86 | 87 | .cm-tab { display: inline-block; text-decoration: inherit; } 88 | 89 | .CodeMirror-ruler { 90 | border-left: 1px solid #ccc; 91 | position: absolute; 92 | } 93 | 94 | /* DEFAULT THEME */ 95 | 96 | .cm-s-default .cm-keyword {color: #708;} 97 | .cm-s-default .cm-atom {color: #219;} 98 | .cm-s-default .cm-number {color: #164;} 99 | .cm-s-default .cm-def {color: #00f;} 100 | .cm-s-default .cm-variable, 101 | .cm-s-default .cm-punctuation, 102 | .cm-s-default .cm-property, 103 | .cm-s-default .cm-operator {} 104 | .cm-s-default .cm-variable-2 {color: #05a;} 105 | .cm-s-default .cm-variable-3 {color: #085;} 106 | .cm-s-default .cm-comment {color: #a50;} 107 | .cm-s-default .cm-string {color: #a11;} 108 | .cm-s-default .cm-string-2 {color: #f50;} 109 | .cm-s-default .cm-meta {color: #555;} 110 | .cm-s-default .cm-qualifier {color: #555;} 111 | .cm-s-default .cm-builtin {color: #30a;} 112 | .cm-s-default .cm-bracket {color: #997;} 113 | .cm-s-default .cm-tag {color: #170;} 114 | .cm-s-default .cm-attribute {color: #00c;} 115 | .cm-s-default .cm-header {color: blue;} 116 | .cm-s-default .cm-quote {color: #090;} 117 | .cm-s-default .cm-hr {color: #999;} 118 | .cm-s-default .cm-link {color: #00c;} 119 | 120 | .cm-negative {color: #d44;} 121 | .cm-positive {color: #292;} 122 | .cm-header, .cm-strong {font-weight: bold;} 123 | .cm-em {font-style: italic;} 124 | .cm-link {text-decoration: underline;} 125 | .cm-strikethrough {text-decoration: line-through;} 126 | 127 | .cm-s-default .cm-error {color: #f00;} 128 | .cm-invalidchar {color: #f00;} 129 | 130 | /* Default styles for common addons */ 131 | 132 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 133 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 134 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 135 | .CodeMirror-activeline-background {background: #e8f2ff;} 136 | 137 | /* STOP */ 138 | 139 | /* The rest of this file contains styles related to the mechanics of 140 | the editor. You probably shouldn't touch them. */ 141 | 142 | .CodeMirror { 143 | position: relative; 144 | overflow: hidden; 145 | background: white; 146 | } 147 | 148 | .CodeMirror-scroll { 149 | overflow: scroll !important; /* Things will break if this is overridden */ 150 | /* 30px is the magic margin used to hide the element's real scrollbars */ 151 | /* See overflow: hidden in .CodeMirror */ 152 | margin-bottom: -30px; margin-right: -30px; 153 | padding-bottom: 30px; 154 | height: 100%; 155 | outline: none; /* Prevent dragging from highlighting the element */ 156 | position: relative; 157 | -moz-box-sizing: content-box; 158 | box-sizing: content-box; 159 | } 160 | .CodeMirror-sizer { 161 | position: relative; 162 | border-right: 30px solid transparent; 163 | -moz-box-sizing: content-box; 164 | box-sizing: content-box; 165 | } 166 | 167 | /* The fake, visible scrollbars. Used to force redraw during scrolling 168 | before actuall scrolling happens, thus preventing shaking and 169 | flickering artifacts. */ 170 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 171 | position: absolute; 172 | z-index: 6; 173 | display: none; 174 | } 175 | .CodeMirror-vscrollbar { 176 | right: 0; top: 0; 177 | overflow-x: hidden; 178 | overflow-y: scroll; 179 | } 180 | .CodeMirror-hscrollbar { 181 | bottom: 0; left: 0; 182 | overflow-y: hidden; 183 | overflow-x: scroll; 184 | } 185 | .CodeMirror-scrollbar-filler { 186 | right: 0; bottom: 0; 187 | } 188 | .CodeMirror-gutter-filler { 189 | left: 0; bottom: 0; 190 | } 191 | 192 | .CodeMirror-gutters { 193 | position: absolute; left: 0; top: 0; 194 | z-index: 3; 195 | } 196 | .CodeMirror-gutter { 197 | white-space: normal; 198 | height: 100%; 199 | -moz-box-sizing: content-box; 200 | box-sizing: content-box; 201 | display: inline-block; 202 | margin-bottom: -30px; 203 | /* Hack to make IE7 behave */ 204 | *zoom:1; 205 | *display:inline; 206 | } 207 | .CodeMirror-gutter-wrapper { 208 | position: absolute; 209 | z-index: 4; 210 | height: 100%; 211 | } 212 | .CodeMirror-gutter-elt { 213 | position: absolute; 214 | cursor: default; 215 | z-index: 4; 216 | } 217 | .CodeMirror-gutter-wrapper { 218 | -webkit-user-select: none; 219 | -moz-user-select: none; 220 | user-select: none; 221 | } 222 | 223 | .CodeMirror-lines { 224 | cursor: text; 225 | min-height: 1px; /* prevents collapsing before first draw */ 226 | } 227 | .CodeMirror pre { 228 | /* Reset some styles that the rest of the page might have set */ 229 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 230 | border-width: 0; 231 | background: transparent; 232 | font-family: inherit; 233 | font-size: inherit; 234 | margin: 0; 235 | white-space: pre; 236 | word-wrap: normal; 237 | line-height: inherit; 238 | color: inherit; 239 | z-index: 2; 240 | position: relative; 241 | overflow: visible; 242 | -webkit-tap-highlight-color: transparent; 243 | } 244 | .CodeMirror-wrap pre { 245 | word-wrap: break-word; 246 | white-space: pre-wrap; 247 | word-break: normal; 248 | } 249 | 250 | .CodeMirror-linebackground { 251 | position: absolute; 252 | left: 0; right: 0; top: 0; bottom: 0; 253 | z-index: 0; 254 | } 255 | 256 | .CodeMirror-linewidget { 257 | position: relative; 258 | z-index: 2; 259 | overflow: auto; 260 | } 261 | 262 | .CodeMirror-widget {} 263 | 264 | .CodeMirror-code { 265 | outline: none; 266 | } 267 | 268 | .CodeMirror-measure { 269 | position: absolute; 270 | width: 100%; 271 | height: 0; 272 | overflow: hidden; 273 | visibility: hidden; 274 | } 275 | .CodeMirror-measure pre { position: static; } 276 | 277 | .CodeMirror div.CodeMirror-cursor { 278 | position: absolute; 279 | border-right: none; 280 | width: 0; 281 | } 282 | 283 | div.CodeMirror-cursors { 284 | visibility: hidden; 285 | position: relative; 286 | z-index: 3; 287 | } 288 | .CodeMirror-focused div.CodeMirror-cursors { 289 | visibility: visible; 290 | } 291 | 292 | .CodeMirror-selected { background: #d9d9d9; } 293 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 294 | .CodeMirror-crosshair { cursor: crosshair; } 295 | .CodeMirror ::selection { background: #d7d4f0; } 296 | .CodeMirror ::-moz-selection { background: #d7d4f0; } 297 | 298 | .cm-searching { 299 | background: #ffa; 300 | background: rgba(255, 255, 0, .4); 301 | } 302 | 303 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 304 | .CodeMirror span { *vertical-align: text-bottom; } 305 | 306 | /* Used to force a border model for a node */ 307 | .cm-force-border { padding-right: .1px; } 308 | 309 | @media print { 310 | /* Hide the cursor when printing */ 311 | .CodeMirror div.CodeMirror-cursors { 312 | visibility: hidden; 313 | } 314 | } 315 | 316 | /* See issue #2901 */ 317 | .cm-tab-wrap-hack:after { content: ''; } 318 | 319 | /* Help users use markselection to safely style text background */ 320 | span.CodeMirror-selectedtext { background: none; } 321 | -------------------------------------------------------------------------------- /ext/pythonmode.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | function wordRegexp(words) { 15 | return new RegExp("^((" + words.join(")|(") + "))\\b"); 16 | } 17 | 18 | var wordOperators = wordRegexp(["and", "or", "not", "is"]); 19 | var commonKeywords = ["as", "assert", "break", "class", "continue", 20 | "def", "del", "elif", "else", "except", "finally", 21 | "for", "from", "global", "if", "import", 22 | "lambda", "pass", "raise", "return", 23 | "try", "while", "with", "yield", "in"]; 24 | var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr", 25 | "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod", 26 | "enumerate", "eval", "filter", "float", "format", "frozenset", 27 | "getattr", "globals", "hasattr", "hash", "help", "hex", "id", 28 | "input", "int", "isinstance", "issubclass", "iter", "len", 29 | "list", "locals", "map", "max", "memoryview", "min", "next", 30 | "object", "oct", "open", "ord", "pow", "property", "range", 31 | "repr", "reversed", "round", "set", "setattr", "slice", 32 | "sorted", "staticmethod", "str", "sum", "super", "tuple", 33 | "type", "vars", "zip", "__import__", "NotImplemented", 34 | "Ellipsis", "__debug__"]; 35 | var py2 = {builtins: ["apply", "basestring", "buffer", "cmp", "coerce", "execfile", 36 | "file", "intern", "long", "raw_input", "reduce", "reload", 37 | "unichr", "unicode", "xrange", "False", "True", "None"], 38 | keywords: ["exec", "print"]}; 39 | var py3 = {builtins: ["ascii", "bytes", "exec", "print"], 40 | keywords: ["nonlocal", "False", "True", "None"]}; 41 | 42 | CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins)); 43 | 44 | function top(state) { 45 | return state.scopes[state.scopes.length - 1]; 46 | } 47 | 48 | CodeMirror.defineMode("python", function(conf, parserConf) { 49 | var ERRORCLASS = "error"; 50 | 51 | var singleDelimiters = parserConf.singleDelimiters || new RegExp("^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]"); 52 | var doubleOperators = parserConf.doubleOperators || new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))"); 53 | var doubleDelimiters = parserConf.doubleDelimiters || new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))"); 54 | var tripleDelimiters = parserConf.tripleDelimiters || new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))"); 55 | 56 | if (parserConf.version && parseInt(parserConf.version, 10) == 3){ 57 | // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator 58 | var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!@]"); 59 | var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*"); 60 | } else { 61 | var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!]"); 62 | var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); 63 | } 64 | 65 | var hangingIndent = parserConf.hangingIndent || conf.indentUnit; 66 | 67 | var myKeywords = commonKeywords, myBuiltins = commonBuiltins; 68 | if(parserConf.extra_keywords != undefined){ 69 | myKeywords = myKeywords.concat(parserConf.extra_keywords); 70 | } 71 | if(parserConf.extra_builtins != undefined){ 72 | myBuiltins = myBuiltins.concat(parserConf.extra_builtins); 73 | } 74 | if (parserConf.version && parseInt(parserConf.version, 10) == 3) { 75 | myKeywords = myKeywords.concat(py3.keywords); 76 | myBuiltins = myBuiltins.concat(py3.builtins); 77 | var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i"); 78 | } else { 79 | myKeywords = myKeywords.concat(py2.keywords); 80 | myBuiltins = myBuiltins.concat(py2.builtins); 81 | var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i"); 82 | } 83 | var keywords = wordRegexp(myKeywords); 84 | var builtins = wordRegexp(myBuiltins); 85 | 86 | // tokenizers 87 | function tokenBase(stream, state) { 88 | // Handle scope changes 89 | if (stream.sol() && top(state).type == "py") { 90 | var scopeOffset = top(state).offset; 91 | if (stream.eatSpace()) { 92 | var lineOffset = stream.indentation(); 93 | if (lineOffset > scopeOffset) 94 | pushScope(stream, state, "py"); 95 | else if (lineOffset < scopeOffset && dedent(stream, state)) 96 | state.errorToken = true; 97 | return null; 98 | } else { 99 | var style = tokenBaseInner(stream, state); 100 | if (scopeOffset > 0 && dedent(stream, state)) 101 | style += " " + ERRORCLASS; 102 | return style; 103 | } 104 | } 105 | return tokenBaseInner(stream, state); 106 | } 107 | 108 | function tokenBaseInner(stream, state) { 109 | if (stream.eatSpace()) return null; 110 | 111 | var ch = stream.peek(); 112 | 113 | // Handle Comments 114 | if (ch == "#") { 115 | stream.skipToEnd(); 116 | return "comment"; 117 | } 118 | 119 | // Handle Number Literals 120 | if (stream.match(/^[0-9\.]/, false)) { 121 | var floatLiteral = false; 122 | // Floats 123 | if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; } 124 | if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; } 125 | if (stream.match(/^\.\d+/)) { floatLiteral = true; } 126 | if (floatLiteral) { 127 | // Float literals may be "imaginary" 128 | stream.eat(/J/i); 129 | return "number"; 130 | } 131 | // Integers 132 | var intLiteral = false; 133 | // Hex 134 | if (stream.match(/^0x[0-9a-f]+/i)) intLiteral = true; 135 | // Binary 136 | if (stream.match(/^0b[01]+/i)) intLiteral = true; 137 | // Octal 138 | if (stream.match(/^0o[0-7]+/i)) intLiteral = true; 139 | // Decimal 140 | if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { 141 | // Decimal literals may be "imaginary" 142 | stream.eat(/J/i); 143 | // TODO - Can you have imaginary longs? 144 | intLiteral = true; 145 | } 146 | // Zero by itself with no other piece of number. 147 | if (stream.match(/^0(?![\dx])/i)) intLiteral = true; 148 | if (intLiteral) { 149 | // Integer literals may be "long" 150 | stream.eat(/L/i); 151 | return "number"; 152 | } 153 | } 154 | 155 | // Handle Strings 156 | if (stream.match(stringPrefixes)) { 157 | state.tokenize = tokenStringFactory(stream.current()); 158 | return state.tokenize(stream, state); 159 | } 160 | 161 | // Handle operators and Delimiters 162 | if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) 163 | return null; 164 | 165 | if (stream.match(doubleOperators) 166 | || stream.match(singleOperators) 167 | || stream.match(wordOperators)) 168 | return "operator"; 169 | 170 | if (stream.match(singleDelimiters)) 171 | return null; 172 | 173 | if (stream.match(keywords)) 174 | return "keyword"; 175 | 176 | if (stream.match(builtins)) 177 | return "builtin"; 178 | 179 | if (stream.match(/^(self|cls)\b/)) 180 | return "variable-2"; 181 | 182 | if (stream.match(identifiers)) { 183 | if (state.lastToken == "def" || state.lastToken == "class") 184 | return "def"; 185 | return "variable"; 186 | } 187 | 188 | // Handle non-detected items 189 | stream.next(); 190 | return ERRORCLASS; 191 | } 192 | 193 | function tokenStringFactory(delimiter) { 194 | while ("rub".indexOf(delimiter.charAt(0).toLowerCase()) >= 0) 195 | delimiter = delimiter.substr(1); 196 | 197 | var singleline = delimiter.length == 1; 198 | var OUTCLASS = "string"; 199 | 200 | function tokenString(stream, state) { 201 | while (!stream.eol()) { 202 | stream.eatWhile(/[^'"\\]/); 203 | if (stream.eat("\\")) { 204 | stream.next(); 205 | if (singleline && stream.eol()) 206 | return OUTCLASS; 207 | } else if (stream.match(delimiter)) { 208 | state.tokenize = tokenBase; 209 | return OUTCLASS; 210 | } else { 211 | stream.eat(/['"]/); 212 | } 213 | } 214 | if (singleline) { 215 | if (parserConf.singleLineStringErrors) 216 | return ERRORCLASS; 217 | else 218 | state.tokenize = tokenBase; 219 | } 220 | return OUTCLASS; 221 | } 222 | tokenString.isString = true; 223 | return tokenString; 224 | } 225 | 226 | function pushScope(stream, state, type) { 227 | var offset = 0, align = null; 228 | if (type == "py") { 229 | while (top(state).type != "py") 230 | state.scopes.pop(); 231 | } 232 | offset = top(state).offset + (type == "py" ? conf.indentUnit : hangingIndent); 233 | if (type != "py" && !stream.match(/^(\s|#.*)*$/, false)) 234 | align = stream.column() + 1; 235 | state.scopes.push({offset: offset, type: type, align: align}); 236 | } 237 | 238 | function dedent(stream, state) { 239 | var indented = stream.indentation(); 240 | while (top(state).offset > indented) { 241 | if (top(state).type != "py") return true; 242 | state.scopes.pop(); 243 | } 244 | return top(state).offset != indented; 245 | } 246 | 247 | function tokenLexer(stream, state) { 248 | var style = state.tokenize(stream, state); 249 | var current = stream.current(); 250 | 251 | // Handle '.' connected identifiers 252 | if (current == ".") { 253 | style = stream.match(identifiers, false) ? null : ERRORCLASS; 254 | if (style == null && state.lastStyle == "meta") { 255 | // Apply 'meta' style to '.' connected identifiers when 256 | // appropriate. 257 | style = "meta"; 258 | } 259 | return style; 260 | } 261 | 262 | // Handle decorators 263 | if (current == "@"){ 264 | if(parserConf.version && parseInt(parserConf.version, 10) == 3){ 265 | return stream.match(identifiers, false) ? "meta" : "operator"; 266 | } else { 267 | return stream.match(identifiers, false) ? "meta" : ERRORCLASS; 268 | } 269 | } 270 | 271 | if ((style == "variable" || style == "builtin") 272 | && state.lastStyle == "meta") 273 | style = "meta"; 274 | 275 | // Handle scope changes. 276 | if (current == "pass" || current == "return") 277 | state.dedent += 1; 278 | 279 | if (current == "lambda") state.lambda = true; 280 | if (current == ":" && !state.lambda && top(state).type == "py") 281 | pushScope(stream, state, "py"); 282 | 283 | var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1; 284 | if (delimiter_index != -1) 285 | pushScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1)); 286 | 287 | delimiter_index = "])}".indexOf(current); 288 | if (delimiter_index != -1) { 289 | if (top(state).type == current) state.scopes.pop(); 290 | else return ERRORCLASS; 291 | } 292 | if (state.dedent > 0 && stream.eol() && top(state).type == "py") { 293 | if (state.scopes.length > 1) state.scopes.pop(); 294 | state.dedent -= 1; 295 | } 296 | 297 | return style; 298 | } 299 | 300 | var external = { 301 | startState: function(basecolumn) { 302 | return { 303 | tokenize: tokenBase, 304 | scopes: [{offset: basecolumn || 0, type: "py", align: null}], 305 | lastStyle: null, 306 | lastToken: null, 307 | lambda: false, 308 | dedent: 0 309 | }; 310 | }, 311 | 312 | token: function(stream, state) { 313 | var addErr = state.errorToken; 314 | if (addErr) state.errorToken = false; 315 | var style = tokenLexer(stream, state); 316 | 317 | state.lastStyle = style; 318 | 319 | var current = stream.current(); 320 | if (current && style) 321 | state.lastToken = current; 322 | 323 | if (stream.eol() && state.lambda) 324 | state.lambda = false; 325 | return addErr ? style + " " + ERRORCLASS : style; 326 | }, 327 | 328 | indent: function(state, textAfter) { 329 | if (state.tokenize != tokenBase) 330 | return state.tokenize.isString ? CodeMirror.Pass : 0; 331 | 332 | var scope = top(state); 333 | var closing = textAfter && textAfter.charAt(0) == scope.type; 334 | if (scope.align != null) 335 | return scope.align - (closing ? 1 : 0); 336 | else if (closing && state.scopes.length > 1) 337 | return state.scopes[state.scopes.length - 2].offset; 338 | else 339 | return scope.offset; 340 | }, 341 | 342 | lineComment: "#", 343 | fold: "indent" 344 | }; 345 | return external; 346 | }); 347 | 348 | CodeMirror.defineMIME("text/x-python", "python"); 349 | 350 | var words = function(str) { return str.split(" "); }; 351 | 352 | CodeMirror.defineMIME("text/x-cython", { 353 | name: "python", 354 | extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+ 355 | "extern gil include nogil property public"+ 356 | "readonly struct union DEF IF ELIF ELSE") 357 | }); 358 | 359 | }); 360 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jupyter Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /jupyter-js-services/config.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Configurable data section. 3 | */ 4 | export declare class ConfigSection { 5 | /** 6 | * Create a config section. 7 | */ 8 | constructor(sectionName: string, baseUrl: string); 9 | /** 10 | * Get the data for this section. 11 | */ 12 | data: any; 13 | /** 14 | * Promose fullfilled when the config section is first loaded. 15 | */ 16 | onLoaded: Promise; 17 | /** 18 | * Retrieve the data for this section. 19 | */ 20 | load(): Promise; 21 | /** 22 | * Modify the config values stored. Update the local data immediately, 23 | * send the change to the server, and use the updated data from the server 24 | * when the reply comes. 25 | */ 26 | update(newdata: any): Promise; 27 | /** 28 | * Handle a finished load, fulfilling the onLoaded promise on the first call. 29 | */ 30 | private _loadDone(); 31 | private _url; 32 | private _data; 33 | private _loaded; 34 | private _oneLoadFinished; 35 | private _finishFirstLoad; 36 | } 37 | /** 38 | * Configurable object with defaults. 39 | */ 40 | export declare class ConfigWithDefaults { 41 | /** 42 | * Create a new config with defaults. 43 | */ 44 | constructor(section: ConfigSection, defaults: any, classname?: string); 45 | /** 46 | * Wait for config to have loaded, then get a value or the default. 47 | * 48 | * Note: section.load() must be called somewhere else. 49 | */ 50 | get(key: string): Promise; 51 | /** 52 | * Return a config value. If config is not yet loaded, return the default 53 | * instead of waiting for it to load. 54 | */ 55 | getSync(key: string): any; 56 | /** 57 | * Set a config value. Send the update to the server, and change our 58 | * local copy of the data immediately. 59 | */ 60 | set(key: string, value: any): Promise; 61 | /** 62 | * Get data from the Section with our classname, if available. 63 | * If we have no classname, get all of the data in the Section 64 | */ 65 | private _classData(); 66 | private _section; 67 | private _defaults; 68 | private _className; 69 | } 70 | -------------------------------------------------------------------------------- /jupyter-js-services/config.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | var utils = require('./utils'); 4 | /** 5 | * The url for the config service. 6 | */ 7 | var SERVICE_CONFIG_URL = 'api/config'; 8 | /** 9 | * Configurable data section. 10 | */ 11 | var ConfigSection = (function () { 12 | /** 13 | * Create a config section. 14 | */ 15 | function ConfigSection(sectionName, baseUrl) { 16 | var _this = this; 17 | this._url = "unknown"; 18 | this._data = {}; 19 | this._loaded = null; 20 | this._oneLoadFinished = false; 21 | this._finishFirstLoad = null; 22 | this._url = utils.urlJoinEncode(baseUrl, SERVICE_CONFIG_URL, sectionName); 23 | this._loaded = new Promise(function (resolve, reject) { 24 | _this._finishFirstLoad = resolve; 25 | }); 26 | } 27 | Object.defineProperty(ConfigSection.prototype, "data", { 28 | /** 29 | * Get the data for this section. 30 | */ 31 | get: function () { 32 | return this._data; 33 | }, 34 | enumerable: true, 35 | configurable: true 36 | }); 37 | Object.defineProperty(ConfigSection.prototype, "onLoaded", { 38 | /** 39 | * Promose fullfilled when the config section is first loaded. 40 | */ 41 | get: function () { 42 | return this._loaded; 43 | }, 44 | enumerable: true, 45 | configurable: true 46 | }); 47 | /** 48 | * Retrieve the data for this section. 49 | */ 50 | ConfigSection.prototype.load = function () { 51 | var _this = this; 52 | return utils.ajaxRequest(this._url, { 53 | method: "GET", 54 | dataType: "json", 55 | }).then(function (success) { 56 | if (success.xhr.status !== 200) { 57 | throw Error('Invalid Status: ' + success.xhr.status); 58 | } 59 | _this._data = success.data; 60 | _this._loadDone(); 61 | return _this._data; 62 | }); 63 | }; 64 | /** 65 | * Modify the config values stored. Update the local data immediately, 66 | * send the change to the server, and use the updated data from the server 67 | * when the reply comes. 68 | */ 69 | ConfigSection.prototype.update = function (newdata) { 70 | var _this = this; 71 | this._data = utils.extend(this._data, newdata); 72 | return utils.ajaxRequest(this._url, { 73 | method: "PATCH", 74 | data: JSON.stringify(newdata), 75 | dataType: "json", 76 | contentType: 'application/json', 77 | }).then(function (success) { 78 | if (success.xhr.status !== 200) { 79 | throw Error('Invalid Status: ' + success.xhr.status); 80 | } 81 | _this._data = success.data; 82 | _this._loadDone(); 83 | return _this._data; 84 | }); 85 | }; 86 | /** 87 | * Handle a finished load, fulfilling the onLoaded promise on the first call. 88 | */ 89 | ConfigSection.prototype._loadDone = function () { 90 | if (!this._oneLoadFinished) { 91 | this._oneLoadFinished = true; 92 | this._finishFirstLoad(this._data); 93 | } 94 | }; 95 | return ConfigSection; 96 | })(); 97 | exports.ConfigSection = ConfigSection; 98 | /** 99 | * Configurable object with defaults. 100 | */ 101 | var ConfigWithDefaults = (function () { 102 | /** 103 | * Create a new config with defaults. 104 | */ 105 | function ConfigWithDefaults(section, defaults, classname) { 106 | this._section = null; 107 | this._defaults = null; 108 | this._className = "unknown"; 109 | this._section = section; 110 | this._defaults = defaults; 111 | this._className = classname; 112 | } 113 | /** 114 | * Wait for config to have loaded, then get a value or the default. 115 | * 116 | * Note: section.load() must be called somewhere else. 117 | */ 118 | ConfigWithDefaults.prototype.get = function (key) { 119 | var _this = this; 120 | var that = this; 121 | return this._section.onLoaded.then(function () { 122 | return _this._classData()[key] || _this._defaults[key]; 123 | }); 124 | }; 125 | /** 126 | * Return a config value. If config is not yet loaded, return the default 127 | * instead of waiting for it to load. 128 | */ 129 | ConfigWithDefaults.prototype.getSync = function (key) { 130 | return this._classData()[key] || this._defaults[key]; 131 | }; 132 | /** 133 | * Set a config value. Send the update to the server, and change our 134 | * local copy of the data immediately. 135 | */ 136 | ConfigWithDefaults.prototype.set = function (key, value) { 137 | var d = {}; 138 | d[key] = value; 139 | if (this._className) { 140 | var d2 = {}; 141 | d2[this._className] = d; 142 | return this._section.update(d2); 143 | } 144 | else { 145 | return this._section.update(d); 146 | } 147 | }; 148 | /** 149 | * Get data from the Section with our classname, if available. 150 | * If we have no classname, get all of the data in the Section 151 | */ 152 | ConfigWithDefaults.prototype._classData = function () { 153 | if (this._className) { 154 | return this._section.data[this._className] || {}; 155 | } 156 | else { 157 | return this._section.data; 158 | } 159 | }; 160 | return ConfigWithDefaults; 161 | })(); 162 | exports.ConfigWithDefaults = ConfigWithDefaults; 163 | //# sourceMappingURL=config.js.map -------------------------------------------------------------------------------- /jupyter-js-services/contents.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Options for a contents object. 3 | */ 4 | export interface IContentsOpts { 5 | type?: string; 6 | format?: string; 7 | content?: any; 8 | ext?: string; 9 | name?: string; 10 | } 11 | /** 12 | * Contents model. 13 | */ 14 | export interface IContentsModel { 15 | name: string; 16 | path: string; 17 | type: string; 18 | writable?: boolean; 19 | created: string; 20 | last_modified: string; 21 | mimetype: string; 22 | content: string; 23 | format: string; 24 | } 25 | /** 26 | * Checkpoint model. 27 | */ 28 | export interface ICheckpointModel { 29 | id: string; 30 | last_modified: string; 31 | } 32 | /** 33 | * Interface that a content manager should implement. 34 | **/ 35 | export interface IContents { 36 | get(path: string, type: string, options: IContentsOpts): Promise; 37 | newUntitled(path: string, options: IContentsOpts): Promise; 38 | delete(path: string): Promise; 39 | rename(path: string, newPath: string): Promise; 40 | save(path: string, model: any): Promise; 41 | listContents(path: string): Promise; 42 | copy(path: string, toDir: string): Promise; 43 | createCheckpoint(path: string): Promise; 44 | restoreCheckpoint(path: string, checkpointID: string): Promise; 45 | deleteCheckpoint(path: string, checkpointID: string): Promise; 46 | listCheckpoints(path: string): Promise; 47 | } 48 | /** 49 | * A contents handle passing file operations to the back-end. 50 | * This includes checkpointing with the normal file operations. 51 | */ 52 | export declare class Contents implements IContents { 53 | /** 54 | * Create a new contents object. 55 | */ 56 | constructor(baseUrl: string); 57 | /** 58 | * Get a file or directory. 59 | */ 60 | get(path: string, options: IContentsOpts): Promise; 61 | /** 62 | * Create a new untitled file or directory in the specified directory path. 63 | */ 64 | newUntitled(path: string, options?: IContentsOpts): Promise; 65 | /** 66 | * Delete a file. 67 | */ 68 | delete(path: string): Promise; 69 | /** 70 | * Rename a file. 71 | */ 72 | rename(path: string, newPath: string): Promise; 73 | /** 74 | * Save a file. 75 | */ 76 | save(path: string, model: IContentsOpts): Promise; 77 | /** 78 | * Copy a file into a given directory via POST 79 | * The server will select the name of the copied file. 80 | */ 81 | copy(fromFile: string, toDir: string): Promise; 82 | /** 83 | * Create a checkpoint for a file. 84 | */ 85 | createCheckpoint(path: string): Promise; 86 | /** 87 | * List available checkpoints for a file. 88 | */ 89 | listCheckpoints(path: string): Promise; 90 | /** 91 | * Restore a file to a known checkpoint state. 92 | */ 93 | restoreCheckpoint(path: string, checkpointID: string): Promise; 94 | /** 95 | * Delete a checkpoint for a file. 96 | */ 97 | deleteCheckpoint(path: string, checkpointID: string): Promise; 98 | /** 99 | * List notebooks and directories at a given path. 100 | */ 101 | listContents(path: string): Promise; 102 | /** 103 | * Get an REST url for this file given a path. 104 | */ 105 | private _getUrl(...args); 106 | private _apiUrl; 107 | } 108 | -------------------------------------------------------------------------------- /jupyter-js-services/contents.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | var utils = require('./utils'); 4 | /** 5 | * The url for the contents service. 6 | */ 7 | var SERVICE_CONTENTS_URL = 'api/contents'; 8 | /** 9 | * A contents handle passing file operations to the back-end. 10 | * This includes checkpointing with the normal file operations. 11 | */ 12 | var Contents = (function () { 13 | /** 14 | * Create a new contents object. 15 | */ 16 | function Contents(baseUrl) { 17 | this._apiUrl = "unknown"; 18 | this._apiUrl = utils.urlPathJoin(baseUrl, SERVICE_CONTENTS_URL); 19 | } 20 | /** 21 | * Get a file or directory. 22 | */ 23 | Contents.prototype.get = function (path, options) { 24 | var settings = { 25 | method: "GET", 26 | dataType: "json", 27 | }; 28 | var url = this._getUrl(path); 29 | var params = {}; 30 | if (options.type) { 31 | params.type = options.type; 32 | } 33 | if (options.format) { 34 | params.format = options.format; 35 | } 36 | if (options.content === false) { 37 | params.content = '0'; 38 | } 39 | url = url + utils.jsonToQueryString(params); 40 | return utils.ajaxRequest(url, settings).then(function (success) { 41 | if (success.xhr.status !== 200) { 42 | throw Error('Invalid Status: ' + success.xhr.status); 43 | } 44 | validateContentsModel(success.data); 45 | return success.data; 46 | }); 47 | }; 48 | /** 49 | * Create a new untitled file or directory in the specified directory path. 50 | */ 51 | Contents.prototype.newUntitled = function (path, options) { 52 | var settings = { 53 | method: "POST", 54 | dataType: "json", 55 | }; 56 | if (options) { 57 | var data = JSON.stringify({ 58 | ext: options.ext, 59 | type: options.type 60 | }); 61 | settings.data = data; 62 | settings.contentType = 'application/json'; 63 | } 64 | var url = this._getUrl(path); 65 | return utils.ajaxRequest(url, settings).then(function (success) { 66 | if (success.xhr.status !== 201) { 67 | throw Error('Invalid Status: ' + success.xhr.status); 68 | } 69 | validateContentsModel(success.data); 70 | return success.data; 71 | }); 72 | }; 73 | /** 74 | * Delete a file. 75 | */ 76 | Contents.prototype.delete = function (path) { 77 | var settings = { 78 | method: "DELETE", 79 | dataType: "json", 80 | }; 81 | var url = this._getUrl(path); 82 | return utils.ajaxRequest(url, settings).then(function (success) { 83 | if (success.xhr.status !== 204) { 84 | throw Error('Invalid Status: ' + success.xhr.status); 85 | } 86 | }, // Translate certain errors to more specific ones. 87 | function (error) { 88 | // TODO: update IPEP27 to specify errors more precisely, so 89 | // that error types can be detected here with certainty. 90 | if (error.xhr.status === 400) { 91 | throw new Error('Directory not found'); 92 | } 93 | throw error; 94 | }); 95 | }; 96 | /** 97 | * Rename a file. 98 | */ 99 | Contents.prototype.rename = function (path, newPath) { 100 | var data = { path: newPath }; 101 | var settings = { 102 | method: "PATCH", 103 | data: JSON.stringify(data), 104 | dataType: "json", 105 | contentType: 'application/json', 106 | }; 107 | var url = this._getUrl(path); 108 | return utils.ajaxRequest(url, settings).then(function (success) { 109 | if (success.xhr.status !== 200) { 110 | throw Error('Invalid Status: ' + success.xhr.status); 111 | } 112 | validateContentsModel(success.data); 113 | return success.data; 114 | }); 115 | }; 116 | /** 117 | * Save a file. 118 | */ 119 | Contents.prototype.save = function (path, model) { 120 | var settings = { 121 | method: "PUT", 122 | dataType: "json", 123 | data: JSON.stringify(model), 124 | contentType: 'application/json', 125 | }; 126 | var url = this._getUrl(path); 127 | return utils.ajaxRequest(url, settings).then(function (success) { 128 | // will return 200 for an existing file and 201 for a new file 129 | if (success.xhr.status !== 200 && success.xhr.status !== 201) { 130 | throw Error('Invalid Status: ' + success.xhr.status); 131 | } 132 | validateContentsModel(success.data); 133 | return success.data; 134 | }); 135 | }; 136 | /** 137 | * Copy a file into a given directory via POST 138 | * The server will select the name of the copied file. 139 | */ 140 | Contents.prototype.copy = function (fromFile, toDir) { 141 | var settings = { 142 | method: "POST", 143 | data: JSON.stringify({ copy_from: fromFile }), 144 | contentType: 'application/json', 145 | dataType: "json", 146 | }; 147 | var url = this._getUrl(toDir); 148 | return utils.ajaxRequest(url, settings).then(function (success) { 149 | if (success.xhr.status !== 201) { 150 | throw Error('Invalid Status: ' + success.xhr.status); 151 | } 152 | validateContentsModel(success.data); 153 | return success.data; 154 | }); 155 | }; 156 | /** 157 | * Create a checkpoint for a file. 158 | */ 159 | Contents.prototype.createCheckpoint = function (path) { 160 | var settings = { 161 | method: "POST", 162 | dataType: "json", 163 | }; 164 | var url = this._getUrl(path, 'checkpoints'); 165 | return utils.ajaxRequest(url, settings).then(function (success) { 166 | if (success.xhr.status !== 201) { 167 | throw Error('Invalid Status: ' + success.xhr.status); 168 | } 169 | validateCheckpointModel(success.data); 170 | return success.data; 171 | }); 172 | }; 173 | /** 174 | * List available checkpoints for a file. 175 | */ 176 | Contents.prototype.listCheckpoints = function (path) { 177 | var settings = { 178 | method: "GET", 179 | dataType: "json", 180 | }; 181 | var url = this._getUrl(path, 'checkpoints'); 182 | return utils.ajaxRequest(url, settings).then(function (success) { 183 | if (success.xhr.status !== 200) { 184 | throw Error('Invalid Status: ' + success.xhr.status); 185 | } 186 | if (!Array.isArray(success.data)) { 187 | throw Error('Invalid Checkpoint list'); 188 | } 189 | for (var i = 0; i < success.data.length; i++) { 190 | validateCheckpointModel(success.data[i]); 191 | } 192 | return success.data; 193 | }); 194 | }; 195 | /** 196 | * Restore a file to a known checkpoint state. 197 | */ 198 | Contents.prototype.restoreCheckpoint = function (path, checkpointID) { 199 | var settings = { 200 | method: "POST", 201 | dataType: "json", 202 | }; 203 | var url = this._getUrl(path, 'checkpoints', checkpointID); 204 | return utils.ajaxRequest(url, settings).then(function (success) { 205 | if (success.xhr.status !== 204) { 206 | throw Error('Invalid Status: ' + success.xhr.status); 207 | } 208 | }); 209 | }; 210 | /** 211 | * Delete a checkpoint for a file. 212 | */ 213 | Contents.prototype.deleteCheckpoint = function (path, checkpointID) { 214 | var settings = { 215 | method: "DELETE", 216 | dataType: "json", 217 | }; 218 | var url = this._getUrl(path, 'checkpoints', checkpointID); 219 | return utils.ajaxRequest(url, settings).then(function (success) { 220 | if (success.xhr.status !== 204) { 221 | throw Error('Invalid Status: ' + success.xhr.status); 222 | } 223 | }); 224 | }; 225 | /** 226 | * List notebooks and directories at a given path. 227 | */ 228 | Contents.prototype.listContents = function (path) { 229 | return this.get(path, { type: 'directory' }); 230 | }; 231 | /** 232 | * Get an REST url for this file given a path. 233 | */ 234 | Contents.prototype._getUrl = function () { 235 | var args = []; 236 | for (var _i = 0; _i < arguments.length; _i++) { 237 | args[_i - 0] = arguments[_i]; 238 | } 239 | var url_parts = [this._apiUrl].concat(Array.prototype.slice.apply(args)); 240 | return utils.urlPathJoin.apply(null, url_parts); 241 | }; 242 | return Contents; 243 | })(); 244 | exports.Contents = Contents; 245 | /** 246 | * Validate a Contents Model. 247 | */ 248 | function validateContentsModel(model) { 249 | var err = new Error('Invalid Contents Model'); 250 | if (!model.hasOwnProperty('name') || typeof model.name !== 'string') { 251 | throw err; 252 | } 253 | if (!model.hasOwnProperty('path') || typeof model.path !== 'string') { 254 | throw err; 255 | } 256 | if (!model.hasOwnProperty('type') || typeof model.type !== 'string') { 257 | throw err; 258 | } 259 | if (!model.hasOwnProperty('created') || typeof model.created !== 'string') { 260 | throw err; 261 | } 262 | if (!model.hasOwnProperty('last_modified') || 263 | typeof model.last_modified !== 'string') { 264 | throw err; 265 | } 266 | if (!model.hasOwnProperty('mimetype')) { 267 | throw err; 268 | } 269 | if (!model.hasOwnProperty('content')) { 270 | throw err; 271 | } 272 | if (!model.hasOwnProperty('format')) { 273 | throw err; 274 | } 275 | } 276 | /** 277 | * Validate a Checkpoint model. 278 | */ 279 | function validateCheckpointModel(model) { 280 | var err = new Error('Invalid Checkpoint Model'); 281 | if (!model.hasOwnProperty('id') || typeof model.id !== 'string') { 282 | throw err; 283 | } 284 | if (!model.hasOwnProperty('last_modified') || 285 | typeof model.last_modified !== 'string') { 286 | throw err; 287 | } 288 | } 289 | //# sourceMappingURL=contents.js.map 290 | -------------------------------------------------------------------------------- /jupyter-js-services/ikernel.d.ts: -------------------------------------------------------------------------------- 1 | import { IDisposable } from 'phosphor-disposable'; 2 | import { ISignal } from 'phosphor-signaling'; 3 | export interface IKernelOptions { 4 | name: string; 5 | baseUrl: string; 6 | wsUrl?: string; 7 | username?: string; 8 | clientId?: string; 9 | } 10 | /** 11 | * Kernel identification specification. 12 | */ 13 | export interface IKernelId { 14 | id: string; 15 | name: string; 16 | } 17 | /** 18 | * Kernel message header content. 19 | */ 20 | export interface IKernelMessageHeader { 21 | username: string; 22 | version: string; 23 | session: string; 24 | msg_id: string; 25 | msg_type: string; 26 | } 27 | /** 28 | * Kernel message specification. 29 | */ 30 | export interface IKernelMessage { 31 | header: IKernelMessageHeader; 32 | parent_header: IKernelMessageHeader | {}; 33 | metadata: any; 34 | content: any; 35 | channel: string; 36 | buffers: (ArrayBuffer | ArrayBufferView)[]; 37 | } 38 | /** 39 | * Kernel information specification. 40 | * http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info 41 | */ 42 | export interface IKernelInfo { 43 | protocol_version: string; 44 | implementation: string; 45 | implementation_version: string; 46 | language_info: IKernelLanguageInfo; 47 | banner: string; 48 | help_links: { 49 | [key: string]: string; 50 | }; 51 | } 52 | /** 53 | * Kernel language information specification. 54 | */ 55 | export interface IKernelLanguageInfo { 56 | name: string; 57 | version: string; 58 | mimetype: string; 59 | file_extension: string; 60 | pygments_lexer: string; 61 | codemirror_mode: string | {}; 62 | nbconverter_exporter: string; 63 | } 64 | export declare enum KernelStatus { 65 | Unknown = 0, 66 | Starting = 1, 67 | Idle = 2, 68 | Busy = 3, 69 | Restarting = 4, 70 | Dead = 5, 71 | } 72 | /** 73 | * Contents of a 'complete_request' message. 74 | */ 75 | export interface ICompleteRequest { 76 | code: string; 77 | cursor_pos: number; 78 | } 79 | /** 80 | * Contents of a 'complete_reply' message. 81 | */ 82 | export interface ICompleteReply { 83 | matches: string[]; 84 | cursor_start: number; 85 | cursor_end: number; 86 | metadata: any; 87 | status: string; 88 | } 89 | /** 90 | * Contents of an 'inspect_request' message. 91 | */ 92 | export interface IInspectRequest { 93 | code: string; 94 | cursor_pos: number; 95 | detail_level: number; 96 | } 97 | /** 98 | * Contents of an 'inspect_reply' message. 99 | */ 100 | export interface IInspectReply { 101 | status: string; 102 | data: any; 103 | metadata: any; 104 | } 105 | /** 106 | * Contents of an 'is_complete_request' message. 107 | */ 108 | export interface IIsCompleteRequest { 109 | code: string; 110 | } 111 | /** 112 | * Contents of an 'is_complete_reply' message. 113 | */ 114 | export interface IIsCompleteReply { 115 | status: string; 116 | indent: string; 117 | } 118 | /** 119 | * Contents of an 'execute_request' message. 120 | */ 121 | export interface IExecuteRequest { 122 | code: string; 123 | silent?: boolean; 124 | store_history?: boolean; 125 | user_expressions?: any; 126 | allow_stdin?: boolean; 127 | stop_on_error?: boolean; 128 | } 129 | /** 130 | * Contents of an 'execute_reply' message. 131 | */ 132 | export interface IExecuteReply { 133 | execution_count: number; 134 | data: any; 135 | metadata: any; 136 | } 137 | /** 138 | * Contents of an 'input_reply' message. 139 | */ 140 | export interface IInputReply { 141 | value: string; 142 | } 143 | /** 144 | * Contents of a 'comm_info_request' message. 145 | */ 146 | export interface ICommInfoRequest { 147 | target?: string; 148 | } 149 | /** 150 | * Contents of `comm_info_reply` message. 151 | */ 152 | export interface ICommInfoReply { 153 | /** 154 | * Mapping of comm ids to target names. 155 | */ 156 | comms: { 157 | [id: string]: string; 158 | }; 159 | } 160 | /** 161 | * Contents of a `comm_open` message. 162 | */ 163 | export interface ICommOpen { 164 | comm_id: string; 165 | target_name: string; 166 | data: any; 167 | target_module?: string; 168 | } 169 | /** 170 | * Contents of a `comm_msg` message. 171 | */ 172 | export interface ICommMsg { 173 | comm_id: string; 174 | data: any; 175 | } 176 | /** 177 | * Contents of a `comm_close` message. 178 | */ 179 | export interface ICommClose { 180 | comm_id: string; 181 | data: any; 182 | } 183 | /** 184 | * Options for an IKernelMessage. 185 | */ 186 | export interface IKernelMessageOptions { 187 | msgType: string; 188 | channel: string; 189 | session: string; 190 | username?: string; 191 | msgId?: string; 192 | } 193 | /** 194 | * Interface of a kernel object. 195 | */ 196 | export interface IKernel { 197 | /** 198 | * The status changed signal for the kernel. 199 | */ 200 | statusChanged: ISignal; 201 | /** 202 | * The unhandled message signal for the kernel. 203 | */ 204 | unhandledMessage: ISignal; 205 | /** 206 | * An unhandled comm_open message received from the client. 207 | */ 208 | commOpened: ISignal; 209 | /** 210 | * The id of the server-side kernel. 211 | */ 212 | id: string; 213 | /** 214 | * The name of the server-side kernel. 215 | */ 216 | name: string; 217 | /** 218 | * The client username. 219 | * 220 | * Read-only 221 | */ 222 | username: string; 223 | /** 224 | * The client unique id. 225 | * 226 | * Read-only 227 | */ 228 | clientId: string; 229 | /** 230 | * The current status of the kernel. 231 | * 232 | * Read-only 233 | */ 234 | status: KernelStatus; 235 | /** 236 | * Send a shell message to the kernel. 237 | * 238 | * The future object will yield the result when available. 239 | */ 240 | sendShellMessage(msg: IKernelMessage, expectReply: boolean): IKernelFuture; 241 | /** 242 | * Interrupt a kernel via API: POST /kernels/{kernel_id}/interrupt 243 | */ 244 | interrupt(): Promise; 245 | /** 246 | * Restart a kernel via API: POST /kernels/{kernel_id}/restart 247 | * 248 | * It is assumed that the API call does not mutate the kernel id or name. 249 | */ 250 | restart(): Promise; 251 | /** 252 | * Delete a kernel via API: DELETE /kernels/{kernel_id} 253 | * 254 | * If the given kernel id corresponds to an Kernel object, that 255 | * object is disposed and its websocket connection is cleared. 256 | * 257 | * Any further calls to `sendMessage` for that Kernel will throw 258 | * an exception. 259 | */ 260 | shutdown(): Promise; 261 | /** 262 | * Send a "kernel_info_request" message. 263 | * 264 | * See https://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info 265 | */ 266 | kernelInfo(): Promise; 267 | /** 268 | * Send a "complete_request" message. 269 | * 270 | * See https://ipython.org/ipython-doc/dev/development/messaging.html#completion 271 | */ 272 | complete(contents: ICompleteRequest): Promise; 273 | /** 274 | * Send an "inspect_request" message. 275 | * 276 | * See https://ipython.org/ipython-doc/dev/development/messaging.html#introspection 277 | */ 278 | inspect(contents: IInspectRequest): Promise; 279 | /** 280 | * Send an "execute_request" message. 281 | * 282 | * See https://ipython.org/ipython-doc/dev/development/messaging.html#execute 283 | */ 284 | execute(contents: IExecuteRequest): IKernelFuture; 285 | /** 286 | * Send an "is_complete_request" message. 287 | * 288 | * See https://ipython.org/ipython-doc/dev/development/messaging.html#code-completeness 289 | */ 290 | isComplete(contents: IIsCompleteRequest): Promise; 291 | /** 292 | * Send a 'comm_info_request', and return the contents of the 293 | * 'comm_info_reply'. 294 | */ 295 | commInfo(contents: ICommInfoRequest): Promise; 296 | /** 297 | * Send an "input_reply" message. 298 | * 299 | * https://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-stdin-router-dealer-sockets 300 | */ 301 | sendInputReply(contents: IInputReply): void; 302 | /** 303 | * Connect to a comm, or create a new one. 304 | * 305 | * If a client-side comm already exists, it is returned. 306 | */ 307 | connectToComm(targetName: string, commId?: string): IComm; 308 | } 309 | /** 310 | * Object providing a Future interface for message callbacks. 311 | * 312 | * The future will self-dispose after `isDone` is 313 | * set and the registered `onDone` handler is called. 314 | * 315 | * If a `reply is expected, the Future is considered done when 316 | * both a `reply` message and a an `idle` iopub status message have 317 | * been received. Otherwise, it is considered done when the `idle` status is 318 | * received. 319 | */ 320 | export interface IKernelFuture extends IDisposable { 321 | /** 322 | * Test whether the future is done. 323 | * 324 | * Read-only. 325 | */ 326 | isDone: boolean; 327 | /** 328 | * The reply handler for the kernel future. 329 | */ 330 | onReply: (msg: IKernelMessage) => void; 331 | /** 332 | * The stdin handler for the kernel future. 333 | */ 334 | onStdin: (msg: IKernelMessage) => void; 335 | /** 336 | * The iopub handler for the kernel future. 337 | */ 338 | onIOPub: (msg: IKernelMessage) => void; 339 | /** 340 | * The done handler for the kernel future. 341 | */ 342 | onDone: (msg: IKernelMessage) => void; 343 | } 344 | /** 345 | * KernelSpec help link interface. 346 | */ 347 | export interface IKernelSpecHelpLink { 348 | text: string; 349 | url: string; 350 | } 351 | /** 352 | * KernelSpec interface. 353 | */ 354 | export interface IKernelSpec { 355 | language: string; 356 | argv: string[]; 357 | display_name: string; 358 | env: any; 359 | codemirror_mode?: string; 360 | help_links?: IKernelSpecHelpLink[]; 361 | } 362 | /** 363 | * KernelSpecId interface. 364 | */ 365 | export interface IKernelSpecId { 366 | name: string; 367 | spec: IKernelSpec; 368 | resources: { 369 | [key: string]: string; 370 | }; 371 | } 372 | /** 373 | * KernelSpecInfo interface 374 | */ 375 | export interface IKernelSpecIds { 376 | default: string; 377 | kernelspecs: { 378 | [key: string]: IKernelSpecId; 379 | }; 380 | } 381 | /** 382 | * A client side Comm interface. 383 | */ 384 | export interface IComm { 385 | /** 386 | * The uuid for the comm channel. 387 | * 388 | * Read-only 389 | */ 390 | commId: string; 391 | /** 392 | * The target name for the comm channel. 393 | * 394 | * Read-only 395 | */ 396 | targetName: string; 397 | /** 398 | * The onClose handler. 399 | */ 400 | onClose: (data?: any) => void; 401 | /** 402 | * The onMsg handler. 403 | */ 404 | onMsg: (data: any) => void; 405 | /** 406 | * Open a comm with optional data. 407 | */ 408 | open(data?: any, metadata?: any): IKernelFuture; 409 | /** 410 | * Send a comm message to the kernel. 411 | */ 412 | send(data: any, metadata?: any, buffers?: (ArrayBuffer | ArrayBufferView)[]): IKernelFuture; 413 | /** 414 | * Close the comm. 415 | */ 416 | close(data?: any, metadata?: any): IKernelFuture; 417 | } 418 | -------------------------------------------------------------------------------- /jupyter-js-services/ikernel.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 'use strict'; 4 | (function (KernelStatus) { 5 | KernelStatus[KernelStatus["Unknown"] = 0] = "Unknown"; 6 | KernelStatus[KernelStatus["Starting"] = 1] = "Starting"; 7 | KernelStatus[KernelStatus["Idle"] = 2] = "Idle"; 8 | KernelStatus[KernelStatus["Busy"] = 3] = "Busy"; 9 | KernelStatus[KernelStatus["Restarting"] = 4] = "Restarting"; 10 | KernelStatus[KernelStatus["Dead"] = 5] = "Dead"; 11 | })(exports.KernelStatus || (exports.KernelStatus = {})); 12 | var KernelStatus = exports.KernelStatus; 13 | //# sourceMappingURL=ikernel.js.map -------------------------------------------------------------------------------- /jupyter-js-services/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './config'; 2 | export * from './contents'; 3 | export * from './ikernel'; 4 | export * from './isession'; 5 | export * from './kernel'; 6 | export * from './session'; 7 | -------------------------------------------------------------------------------- /jupyter-js-services/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 'use strict'; 4 | function __export(m) { 5 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; 6 | } 7 | __export(require('./config')); 8 | __export(require('./contents')); 9 | __export(require('./ikernel')); 10 | __export(require('./isession')); 11 | __export(require('./kernel')); 12 | __export(require('./session')); 13 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /jupyter-js-services/isession.d.ts: -------------------------------------------------------------------------------- 1 | import { ISignal } from 'phosphor-signaling'; 2 | import { IKernel, IKernelId } from './ikernel'; 3 | /** 4 | * Notebook Identification specification. 5 | */ 6 | export interface INotebookId { 7 | path: string; 8 | } 9 | /** 10 | * Session Identification specification. 11 | */ 12 | export interface ISessionId { 13 | id: string; 14 | notebook: INotebookId; 15 | kernel: IKernelId; 16 | } 17 | /** 18 | * Session initialization options. 19 | */ 20 | export interface ISessionOptions { 21 | notebookPath: string; 22 | kernelName: string; 23 | baseUrl: string; 24 | wsUrl?: string; 25 | username?: string; 26 | clientId?: string; 27 | } 28 | /** 29 | * Interface of a notebook session object. 30 | */ 31 | export interface INotebookSession { 32 | /** 33 | * Get the session died signal. 34 | */ 35 | sessionDied: ISignal; 36 | /** 37 | * Unique id of the session. 38 | * 39 | * Read only. 40 | */ 41 | id: string; 42 | /** 43 | * The path to the notebook. 44 | * 45 | * Read only. 46 | */ 47 | notebookPath: string; 48 | /** 49 | * The kernel. 50 | * 51 | * Read only. 52 | */ 53 | kernel: IKernel; 54 | /** 55 | * Rename the notebook. 56 | */ 57 | renameNotebook(path: string): Promise; 58 | /** 59 | * Kill the kernel and shutdown the session. 60 | */ 61 | shutdown(): Promise; 62 | } 63 | -------------------------------------------------------------------------------- /jupyter-js-services/isession.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 'use strict'; 4 | //# sourceMappingURL=isession.js.map -------------------------------------------------------------------------------- /jupyter-js-services/kernel.d.ts: -------------------------------------------------------------------------------- 1 | import { IKernel, IKernelId, IKernelMessage, IKernelMessageOptions, IKernelOptions, IKernelSpecIds } from './ikernel'; 2 | /** 3 | * Fetch the kernel specs via API: GET /kernelspecs 4 | */ 5 | export declare function getKernelSpecs(baseUrl: string): Promise; 6 | /** 7 | * Fetch the running kernels via API: GET /kernels 8 | */ 9 | export declare function listRunningKernels(baseUrl: string): Promise; 10 | /** 11 | * Start a new kernel via API: POST /kernels 12 | * 13 | * Wrap the result in an Kernel object. The promise is fulfilled 14 | * when the kernel is fully ready to send the first message. If 15 | * the kernel fails to become ready, the promise is rejected. 16 | */ 17 | export declare function startNewKernel(options: IKernelOptions): Promise; 18 | /** 19 | * Connect to a running kernel. 20 | * 21 | * If the kernel was already started via `startNewKernel`, the existing 22 | * Kernel object is used as the fulfillment value. 23 | * 24 | * Otherwise, if `options` are given, we attempt to connect to the existing 25 | * kernel. The promise is fulfilled when the kernel is fully ready to send 26 | * the first message. If the kernel fails to become ready, the promise is 27 | * rejected. 28 | * 29 | * If the kernel was not already started and no `options` are given, 30 | * the promise is rejected. 31 | */ 32 | export declare function connectToKernel(id: string, options?: IKernelOptions): Promise; 33 | /** 34 | * Create a well-formed Kernel Message. 35 | */ 36 | export declare function createKernelMessage(options: IKernelMessageOptions, content?: any, metadata?: any, buffers?: (ArrayBuffer | ArrayBufferView)[]): IKernelMessage; 37 | -------------------------------------------------------------------------------- /jupyter-js-services/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jupyter-js-services", 3 | "version": "0.0.1", 4 | "description": "Client APIs for the Jupyter services REST APIs", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "dependencies": { 8 | "phosphor-disposable": "^1.0.2", 9 | "phosphor-signaling": "^1.1.0" 10 | }, 11 | "devDependencies": { 12 | }, 13 | "scripts": { 14 | "postinstall": "npm dedupe" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/jupyter/jupyter-js-services" 19 | }, 20 | "keywords": [ 21 | "jupyter", 22 | "services", 23 | "notebook" 24 | ], 25 | "files": [ 26 | "lib/index.js", 27 | "lib/index.d.ts" 28 | ], 29 | "author": "Project Jupyter", 30 | "license": "BSD-3-Clause", 31 | "bugs": { 32 | "url": "https://github.com/jupyter/jupyter-js-services/issues" 33 | }, 34 | "homepage": "https://github.com/jupyter/jupyter-js-services" 35 | } 36 | -------------------------------------------------------------------------------- /jupyter-js-services/serialize.d.ts: -------------------------------------------------------------------------------- 1 | import { IKernelMessage } from './ikernel'; 2 | /** 3 | * Deserialize and return the unpacked message. 4 | */ 5 | export declare function deserialize(data: ArrayBuffer | string): IKernelMessage; 6 | /** 7 | * Serialize a kernel message for transport. 8 | */ 9 | export declare function serialize(msg: IKernelMessage): string | ArrayBuffer; 10 | -------------------------------------------------------------------------------- /jupyter-js-services/serialize.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 'use strict'; 4 | /** 5 | * Deserialize and return the unpacked message. 6 | */ 7 | function deserialize(data) { 8 | var value; 9 | if (typeof data === "string") { 10 | value = JSON.parse(data); 11 | } 12 | else { 13 | value = deserializeBinary(data); 14 | } 15 | return value; 16 | } 17 | exports.deserialize = deserialize; 18 | /** 19 | * Serialize a kernel message for transport. 20 | */ 21 | function serialize(msg) { 22 | var value; 23 | if (msg.buffers && msg.buffers.length) { 24 | value = serializeBinary(msg); 25 | } 26 | else { 27 | value = JSON.stringify(msg); 28 | } 29 | return value; 30 | } 31 | exports.serialize = serialize; 32 | /** 33 | * Deserialize a binary message to a Kernel Message. 34 | */ 35 | function deserializeBinary(buf) { 36 | var data = new DataView(buf); 37 | // read the header: 1 + nbufs 32b integers 38 | var nbufs = data.getUint32(0); 39 | var offsets = []; 40 | if (nbufs < 2) { 41 | throw new Error("Invalid incoming Kernel Message"); 42 | } 43 | for (var i = 1; i <= nbufs; i++) { 44 | offsets.push(data.getUint32(i * 4)); 45 | } 46 | var json_bytes = new Uint8Array(buf.slice(offsets[0], offsets[1])); 47 | var msg = JSON.parse((new TextDecoder('utf8')).decode(json_bytes)); 48 | // the remaining chunks are stored as DataViews in msg.buffers 49 | msg.buffers = []; 50 | for (var i = 1; i < nbufs; i++) { 51 | var start = offsets[i]; 52 | var stop = offsets[i + 1] || buf.byteLength; 53 | msg.buffers.push(new DataView(buf.slice(start, stop))); 54 | } 55 | return msg; 56 | } 57 | /** 58 | * Implement the binary serialization protocol. 59 | * Serialize Kernel message to ArrayBuffer. 60 | */ 61 | function serializeBinary(msg) { 62 | var offsets = []; 63 | var buffers = []; 64 | var encoder = new TextEncoder('utf8'); 65 | var json_utf8 = encoder.encode(JSON.stringify(msg, replace_buffers)); 66 | buffers.push(json_utf8.buffer); 67 | for (var i = 0; i < msg.buffers.length; i++) { 68 | // msg.buffers elements could be either views or ArrayBuffers 69 | // buffers elements are ArrayBuffers 70 | var b = msg.buffers[i]; 71 | buffers.push(b instanceof ArrayBuffer ? b : b.buffer); 72 | } 73 | var nbufs = buffers.length; 74 | offsets.push(4 * (nbufs + 1)); 75 | for (i = 0; i + 1 < buffers.length; i++) { 76 | offsets.push(offsets[offsets.length - 1] + buffers[i].byteLength); 77 | } 78 | var msg_buf = new Uint8Array(offsets[offsets.length - 1] + buffers[buffers.length - 1].byteLength); 79 | // use DataView.setUint32 for network byte-order 80 | var view = new DataView(msg_buf.buffer); 81 | // write nbufs to first 4 bytes 82 | view.setUint32(0, nbufs); 83 | // write offsets to next 4 * nbufs bytes 84 | for (i = 0; i < offsets.length; i++) { 85 | view.setUint32(4 * (i + 1), offsets[i]); 86 | } 87 | // write all the buffers at their respective offsets 88 | for (i = 0; i < buffers.length; i++) { 89 | msg_buf.set(new Uint8Array(buffers[i]), offsets[i]); 90 | } 91 | return msg_buf.buffer; 92 | } 93 | /** 94 | * Filter "buffers" key for JSON.stringify 95 | */ 96 | function replace_buffers(key, value) { 97 | if (key === "buffers") { 98 | return undefined; 99 | } 100 | return value; 101 | } 102 | //# sourceMappingURL=serialize.js.map -------------------------------------------------------------------------------- /jupyter-js-services/session.d.ts: -------------------------------------------------------------------------------- 1 | import { INotebookSession, ISessionId, ISessionOptions } from './isession'; 2 | /** 3 | * Fetch the running sessions via API: GET /sessions 4 | */ 5 | export declare function listRunningSessions(baseUrl: string): Promise; 6 | /** 7 | * Start a new session via API: POST /kernels 8 | * 9 | * Wrap the result in an NotebookSession object. The promise is fulfilled 10 | * when the session is fully ready to send the first message. If 11 | * the session fails to become ready, the promise is rejected. 12 | */ 13 | export declare function startNewSession(options: ISessionOptions): Promise; 14 | /** 15 | * Connect to a running notebook session. 16 | * 17 | * If the session was already started via `startNewSession`, the existing 18 | * NotebookSession object is used as the fulfillment value. 19 | * 20 | * Otherwise, if `options` are given, we attempt to connect to the existing 21 | * session. The promise is fulfilled when the session is fully ready to send 22 | * the first message. If the session fails to become ready, the promise is 23 | * rejected. 24 | * 25 | * If the session was not already started and no `options` are given, 26 | * the promise is rejected. 27 | */ 28 | export declare function connectToSession(id: string, options?: ISessionOptions): Promise; 29 | -------------------------------------------------------------------------------- /jupyter-js-services/session.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 'use strict'; 4 | var phosphor_signaling_1 = require('phosphor-signaling'); 5 | var ikernel_1 = require('./ikernel'); 6 | var kernel_1 = require('./kernel'); 7 | var utils = require('./utils'); 8 | var validate = require('./validate'); 9 | /** 10 | * The url for the session service. 11 | */ 12 | var SESSION_SERVICE_URL = 'api/sessions'; 13 | /** 14 | * Fetch the running sessions via API: GET /sessions 15 | */ 16 | function listRunningSessions(baseUrl) { 17 | var url = utils.urlPathJoin(baseUrl, SESSION_SERVICE_URL); 18 | return utils.ajaxRequest(url, { 19 | method: "GET", 20 | dataType: "json" 21 | }).then(function (success) { 22 | if (success.xhr.status !== 200) { 23 | throw Error('Invalid Status: ' + success.xhr.status); 24 | } 25 | if (!Array.isArray(success.data)) { 26 | throw Error('Invalid Session list'); 27 | } 28 | for (var i = 0; i < success.data.length; i++) { 29 | validate.validateSessionId(success.data[i]); 30 | } 31 | return success.data; 32 | }, onSessionError); 33 | } 34 | exports.listRunningSessions = listRunningSessions; 35 | /** 36 | * Start a new session via API: POST /kernels 37 | * 38 | * Wrap the result in an NotebookSession object. The promise is fulfilled 39 | * when the session is fully ready to send the first message. If 40 | * the session fails to become ready, the promise is rejected. 41 | */ 42 | function startNewSession(options) { 43 | var url = utils.urlPathJoin(options.baseUrl, SESSION_SERVICE_URL); 44 | var model = { 45 | kernel: { name: options.kernelName }, 46 | notebook: { path: options.notebookPath } 47 | }; 48 | return utils.ajaxRequest(url, { 49 | method: "POST", 50 | dataType: "json", 51 | data: JSON.stringify(model), 52 | contentType: 'application/json' 53 | }).then(function (success) { 54 | if (success.xhr.status !== 201) { 55 | throw Error('Invalid Status: ' + success.xhr.status); 56 | } 57 | var sessionId = success.data; 58 | validate.validateSessionId(success.data); 59 | return createSession(sessionId, options); 60 | }, onSessionError); 61 | } 62 | exports.startNewSession = startNewSession; 63 | /** 64 | * Connect to a running notebook session. 65 | * 66 | * If the session was already started via `startNewSession`, the existing 67 | * NotebookSession object is used as the fulfillment value. 68 | * 69 | * Otherwise, if `options` are given, we attempt to connect to the existing 70 | * session. The promise is fulfilled when the session is fully ready to send 71 | * the first message. If the session fails to become ready, the promise is 72 | * rejected. 73 | * 74 | * If the session was not already started and no `options` are given, 75 | * the promise is rejected. 76 | */ 77 | function connectToSession(id, options) { 78 | var session = runningSessions.get(id); 79 | if (session) { 80 | return Promise.resolve(session); 81 | } 82 | if (options === void 0) { 83 | return Promise.reject(new Error('Please specify session options')); 84 | } 85 | return new Promise(function (resolve, reject) { 86 | listRunningSessions(options.baseUrl).then(function (sessionIds) { 87 | var sessionIds = sessionIds.filter(function (k) { return k.id === id; }); 88 | if (!sessionIds.length) { 89 | reject(new Error('No running session with id: ' + id)); 90 | } 91 | createSession(sessionIds[0], options).then(function (session) { 92 | resolve(session); 93 | }); 94 | }); 95 | }); 96 | } 97 | exports.connectToSession = connectToSession; 98 | /** 99 | * Create a Promise for a NotebookSession object. 100 | * 101 | * Fulfilled when the NotebookSession is Starting, or rejected if Dead. 102 | */ 103 | function createSession(sessionId, options) { 104 | return new Promise(function (resolve, reject) { 105 | options.notebookPath = sessionId.notebook.path; 106 | var kernelOptions = { 107 | name: sessionId.kernel.name, 108 | baseUrl: options.baseUrl, 109 | wsUrl: options.wsUrl, 110 | username: options.username, 111 | clientId: options.clientId 112 | }; 113 | var kernelPromise = kernel_1.connectToKernel(sessionId.kernel.id, kernelOptions); 114 | kernelPromise.then(function (kernel) { 115 | var session = new NotebookSession(options, sessionId.id, kernel); 116 | runningSessions.set(session.id, session); 117 | resolve(session); 118 | }).catch(function () { 119 | reject(new Error('Session failed to start')); 120 | }); 121 | }); 122 | } 123 | /** 124 | * A module private store for running sessions. 125 | */ 126 | var runningSessions = new Map(); 127 | /** 128 | * Session object for accessing the session REST api. The session 129 | * should be used to start kernels and then shut them down -- for 130 | * all other operations, the kernel object should be used. 131 | **/ 132 | var NotebookSession = (function () { 133 | /** 134 | * Construct a new session. 135 | */ 136 | function NotebookSession(options, id, kernel) { 137 | this._id = ""; 138 | this._notebookPath = ""; 139 | this._kernel = null; 140 | this._url = ''; 141 | this._isDead = false; 142 | this._id = id; 143 | this._notebookPath = options.notebookPath; 144 | this._kernel = kernel; 145 | this._url = utils.urlPathJoin(options.baseUrl, SESSION_SERVICE_URL, this._id); 146 | this._kernel.statusChanged.connect(this._kernelStatusChanged, this); 147 | } 148 | Object.defineProperty(NotebookSession.prototype, "sessionDied", { 149 | /** 150 | * Get the session died signal. 151 | */ 152 | get: function () { 153 | return NotebookSession.sessionDiedSignal.bind(this); 154 | }, 155 | enumerable: true, 156 | configurable: true 157 | }); 158 | Object.defineProperty(NotebookSession.prototype, "id", { 159 | /** 160 | * Get the session id. 161 | */ 162 | get: function () { 163 | return this._id; 164 | }, 165 | enumerable: true, 166 | configurable: true 167 | }); 168 | Object.defineProperty(NotebookSession.prototype, "kernel", { 169 | /** 170 | * Get the session kernel object. 171 | */ 172 | get: function () { 173 | return this._kernel; 174 | }, 175 | enumerable: true, 176 | configurable: true 177 | }); 178 | Object.defineProperty(NotebookSession.prototype, "notebookPath", { 179 | /** 180 | * Get the notebook path. 181 | */ 182 | get: function () { 183 | return this._notebookPath; 184 | }, 185 | enumerable: true, 186 | configurable: true 187 | }); 188 | /** 189 | * Rename the notebook. 190 | */ 191 | NotebookSession.prototype.renameNotebook = function (path) { 192 | var _this = this; 193 | if (this._isDead) { 194 | return Promise.reject(new Error('Session is dead')); 195 | } 196 | var model = { 197 | kernel: { name: this._kernel.name, id: this._kernel.id }, 198 | notebook: { path: path } 199 | }; 200 | return utils.ajaxRequest(this._url, { 201 | method: "PATCH", 202 | dataType: "json", 203 | data: JSON.stringify(model), 204 | contentType: 'application/json' 205 | }).then(function (success) { 206 | if (success.xhr.status !== 200) { 207 | throw Error('Invalid Status: ' + success.xhr.status); 208 | } 209 | var data = success.data; 210 | validate.validateSessionId(data); 211 | _this._notebookPath = data.notebook.path; 212 | }, onSessionError); 213 | }; 214 | /** 215 | * DELETE /api/sessions/[:session_id] 216 | * 217 | * Kill the kernel and shutdown the session. 218 | */ 219 | NotebookSession.prototype.shutdown = function () { 220 | var _this = this; 221 | if (this._isDead) { 222 | return Promise.reject(new Error('Session is dead')); 223 | } 224 | this._isDead = true; 225 | return utils.ajaxRequest(this._url, { 226 | method: "DELETE", 227 | dataType: "json" 228 | }).then(function (success) { 229 | if (success.xhr.status !== 204) { 230 | throw Error('Invalid Status: ' + success.xhr.status); 231 | } 232 | _this.sessionDied.emit(void 0); 233 | _this.kernel.shutdown(); 234 | }, function (rejected) { 235 | _this._isDead = false; 236 | if (rejected.xhr.status === 410) { 237 | throw Error('The kernel was deleted but the session was not'); 238 | } 239 | onSessionError(rejected); 240 | }); 241 | }; 242 | /** 243 | * React to changes in the Kernel status. 244 | */ 245 | NotebookSession.prototype._kernelStatusChanged = function (sender, state) { 246 | if (state == ikernel_1.KernelStatus.Dead) { 247 | this.shutdown(); 248 | } 249 | }; 250 | /** 251 | * A signal emitted when the session dies. 252 | */ 253 | NotebookSession.sessionDiedSignal = new phosphor_signaling_1.Signal(); 254 | return NotebookSession; 255 | })(); 256 | /** 257 | * Handle an error on a session Ajax call. 258 | */ 259 | function onSessionError(error) { 260 | console.error("API request failed (" + error.statusText + "): "); 261 | throw Error(error.statusText); 262 | } 263 | //# sourceMappingURL=session.js.map -------------------------------------------------------------------------------- /jupyter-js-services/test.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blink1073/jupyter_demo/906c18f008cd1ca60c440d07a981b8092fe9f3d9/jupyter-js-services/test.d.ts -------------------------------------------------------------------------------- /jupyter-js-services/test.js: -------------------------------------------------------------------------------- 1 | var index_1 = require('./index'); 2 | var BASEURL = 'http://localhost:8888'; 3 | var WSURL = 'ws://localhost:8888'; 4 | index_1.getKernelSpecs(BASEURL).then(function (kernelSpecs) { 5 | return index_1.startNewKernel({ 6 | baseUrl: BASEURL, 7 | wsUrl: WSURL, 8 | name: kernelSpecs.default, 9 | }); 10 | }).then(function (kernel) { 11 | index_1.getConfigSection('notebook', BASEURL).then(function (section) { 12 | var defaults = { default_cell_type: 'code' }; 13 | var config = new index_1.ConfigWithDefaults(section, defaults, 'Notebook'); 14 | console.log(config.get('default_cell_type')); // 'code' 15 | config.set('foo', 'bar').then(function (data) { 16 | console.log(data.foo); // 'bar' 17 | }); 18 | }); 19 | }); 20 | //# sourceMappingURL=test.js.map -------------------------------------------------------------------------------- /jupyter-js-services/utils.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copy the contents of one object to another, recursively. 3 | * 4 | * http://stackoverflow.com/questions/12317003/something-like-jquery-extend-but-standalone 5 | */ 6 | export declare function extend(target: any, source: any): any; 7 | /** 8 | * Get a random 128b hex string (not a formal UUID) 9 | */ 10 | export declare function uuid(): string; 11 | /** 12 | * Join a sequence of url components with '/'. 13 | */ 14 | export declare function urlPathJoin(...paths: string[]): string; 15 | /** 16 | * Encode just the components of a multi-segment uri, 17 | * leaving '/' separators. 18 | */ 19 | export declare function encodeURIComponents(uri: string): string; 20 | /** 21 | * Join a sequence of url components with '/', 22 | * encoding each component with encodeURIComponent. 23 | */ 24 | export declare function urlJoinEncode(...args: string[]): string; 25 | /** 26 | * Return a serialized object string suitable for a query. 27 | * 28 | * http://stackoverflow.com/a/30707423 29 | */ 30 | export declare function jsonToQueryString(json: any): string; 31 | /** 32 | * Input settings for an AJAX request. 33 | */ 34 | export interface IAjaxSettings { 35 | method: string; 36 | dataType: string; 37 | contentType?: string; 38 | data?: any; 39 | } 40 | /** 41 | * Success handler for AJAX request. 42 | */ 43 | export interface IAjaxSuccess { 44 | data: any; 45 | statusText: string; 46 | xhr: XMLHttpRequest; 47 | } 48 | /** 49 | * Error handler for AJAX request. 50 | */ 51 | export interface IAjaxError { 52 | xhr: XMLHttpRequest; 53 | statusText: string; 54 | error: ErrorEvent; 55 | } 56 | /** 57 | * Asynchronous XMLHTTPRequest handler. 58 | * 59 | * http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest 60 | */ 61 | export declare function ajaxRequest(url: string, settings: IAjaxSettings): Promise; 62 | /** 63 | * A Promise that can be resolved or rejected by another object. 64 | */ 65 | export declare class PromiseDelegate { 66 | /** 67 | * Construct a new Promise delegate. 68 | */ 69 | constructor(); 70 | /** 71 | * Get the underlying Promise. 72 | */ 73 | promise: Promise; 74 | /** 75 | * Resolve the underlying Promise with an optional value or another Promise. 76 | */ 77 | resolve(value?: T | Thenable): void; 78 | /** 79 | * Reject the underlying Promise with an optional reason. 80 | */ 81 | reject(reason?: any): void; 82 | private _promise; 83 | private _resolve; 84 | private _reject; 85 | } 86 | -------------------------------------------------------------------------------- /jupyter-js-services/utils.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 'use strict'; 4 | /** 5 | * Copy the contents of one object to another, recursively. 6 | * 7 | * http://stackoverflow.com/questions/12317003/something-like-jquery-extend-but-standalone 8 | */ 9 | function extend(target, source) { 10 | target = target || {}; 11 | for (var prop in source) { 12 | if (typeof source[prop] === 'object') { 13 | target[prop] = extend(target[prop], source[prop]); 14 | } 15 | else { 16 | target[prop] = source[prop]; 17 | } 18 | } 19 | return target; 20 | } 21 | exports.extend = extend; 22 | /** 23 | * Get a random 128b hex string (not a formal UUID) 24 | */ 25 | function uuid() { 26 | var s = []; 27 | var hexDigits = "0123456789abcdef"; 28 | var nChars = hexDigits.length; 29 | for (var i = 0; i < 32; i++) { 30 | s[i] = hexDigits.charAt(Math.floor(Math.random() * nChars)); 31 | } 32 | return s.join(""); 33 | } 34 | exports.uuid = uuid; 35 | /** 36 | * Join a sequence of url components with '/'. 37 | */ 38 | function urlPathJoin() { 39 | var paths = []; 40 | for (var _i = 0; _i < arguments.length; _i++) { 41 | paths[_i - 0] = arguments[_i]; 42 | } 43 | var url = ''; 44 | for (var i = 0; i < paths.length; i++) { 45 | var path = paths[i]; 46 | if (path === '') { 47 | continue; 48 | } 49 | if (i > 0) { 50 | path = path.replace(/\/\/+/, '/'); 51 | } 52 | if (url.length > 0 && url.charAt(url.length - 1) != '/') { 53 | url = url + '/' + paths[i]; 54 | } 55 | else { 56 | url = url + paths[i]; 57 | } 58 | } 59 | return url; 60 | } 61 | exports.urlPathJoin = urlPathJoin; 62 | /** 63 | * Encode just the components of a multi-segment uri, 64 | * leaving '/' separators. 65 | */ 66 | function encodeURIComponents(uri) { 67 | return uri.split('/').map(encodeURIComponent).join('/'); 68 | } 69 | exports.encodeURIComponents = encodeURIComponents; 70 | /** 71 | * Join a sequence of url components with '/', 72 | * encoding each component with encodeURIComponent. 73 | */ 74 | function urlJoinEncode() { 75 | var args = []; 76 | for (var _i = 0; _i < arguments.length; _i++) { 77 | args[_i - 0] = arguments[_i]; 78 | } 79 | return encodeURIComponents(urlPathJoin.apply(null, args)); 80 | } 81 | exports.urlJoinEncode = urlJoinEncode; 82 | /** 83 | * Return a serialized object string suitable for a query. 84 | * 85 | * http://stackoverflow.com/a/30707423 86 | */ 87 | function jsonToQueryString(json) { 88 | return '?' + Object.keys(json).map(function (key) { 89 | return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]); 90 | }).join('&'); 91 | } 92 | exports.jsonToQueryString = jsonToQueryString; 93 | /** 94 | * Asynchronous XMLHTTPRequest handler. 95 | * 96 | * http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest 97 | */ 98 | function ajaxRequest(url, settings) { 99 | return new Promise(function (resolve, reject) { 100 | var req = new XMLHttpRequest(); 101 | req.open(settings.method, url); 102 | if (settings.contentType) { 103 | req.setRequestHeader('Content-Type', settings.contentType); 104 | } 105 | req.onload = function () { 106 | var response = req.response; 107 | if (settings.dataType === 'json' && req.response) { 108 | response = JSON.parse(req.response); 109 | } 110 | resolve({ data: response, statusText: req.statusText, xhr: req }); 111 | }; 112 | req.onerror = function (err) { 113 | reject({ xhr: req, statusText: req.statusText, error: err }); 114 | }; 115 | if (settings.data) { 116 | req.send(settings.data); 117 | } 118 | else { 119 | req.send(); 120 | } 121 | }); 122 | } 123 | exports.ajaxRequest = ajaxRequest; 124 | /** 125 | * A Promise that can be resolved or rejected by another object. 126 | */ 127 | var PromiseDelegate = (function () { 128 | /** 129 | * Construct a new Promise delegate. 130 | */ 131 | function PromiseDelegate() { 132 | var _this = this; 133 | this._promise = new Promise(function (resolve, reject) { 134 | _this._resolve = resolve; 135 | _this._reject = reject; 136 | }); 137 | } 138 | Object.defineProperty(PromiseDelegate.prototype, "promise", { 139 | /** 140 | * Get the underlying Promise. 141 | */ 142 | get: function () { 143 | return this._promise; 144 | }, 145 | enumerable: true, 146 | configurable: true 147 | }); 148 | /** 149 | * Resolve the underlying Promise with an optional value or another Promise. 150 | */ 151 | PromiseDelegate.prototype.resolve = function (value) { 152 | // Note: according to the Promise spec, and the `this` context for resolve 153 | // and reject are ignored 154 | this._resolve(value); 155 | }; 156 | /** 157 | * Reject the underlying Promise with an optional reason. 158 | */ 159 | PromiseDelegate.prototype.reject = function (reason) { 160 | // Note: according to the Promise spec, and the `this` context for resolve 161 | // and reject are ignored 162 | this._reject(reason); 163 | }; 164 | return PromiseDelegate; 165 | })(); 166 | exports.PromiseDelegate = PromiseDelegate; 167 | //# sourceMappingURL=utils.js.map -------------------------------------------------------------------------------- /jupyter-js-services/validate.d.ts: -------------------------------------------------------------------------------- 1 | import { IKernelId, IKernelMessage, IKernelSpecId } from './ikernel'; 2 | import { INotebookId, ISessionId } from './isession'; 3 | /** 4 | * Validate an Kernel Message as being a valid Comm Message. 5 | */ 6 | export declare function validateCommMessage(msg: IKernelMessage): boolean; 7 | /** 8 | * Validate an object as being of IKernelMessage type. 9 | */ 10 | export declare function validateKernelMessage(msg: IKernelMessage): void; 11 | /** 12 | * Validate an object as being of IKernelID type 13 | */ 14 | export declare function validateKernelId(info: IKernelId): void; 15 | /** 16 | * Validate an object as being of ISessionId type. 17 | */ 18 | export declare function validateSessionId(info: ISessionId): void; 19 | /** 20 | * Validate an object as being of INotebookId type. 21 | */ 22 | export declare function validateNotebookId(model: INotebookId): void; 23 | /** 24 | * Validate an object as being of IKernelSpecID type. 25 | */ 26 | export declare function validateKernelSpec(info: IKernelSpecId): void; 27 | -------------------------------------------------------------------------------- /jupyter-js-services/validate.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 'use strict'; 4 | var COMM_FIELDS = ['comm_id', 'data']; 5 | var HEADER_FIELDS = ['username', 'version', 'session', 'msg_id', 'msg_type']; 6 | var MESSAGE_FIELDS = ['header', 'parent_header', 'metadata', 'content', 7 | 'channel', 'buffers']; 8 | /** 9 | * Validate an Kernel Message as being a valid Comm Message. 10 | */ 11 | function validateCommMessage(msg) { 12 | for (var i = 0; i < COMM_FIELDS.length; i++) { 13 | if (!msg.content.hasOwnProperty(COMM_FIELDS[i])) { 14 | console.log('*****invalid', COMM_FIELDS[i]); 15 | return false; 16 | } 17 | } 18 | if (msg.header.msg_type === 'comm_open') { 19 | if (!msg.content.hasOwnProperty('target_name') || 20 | typeof msg.content.target_name !== 'string') { 21 | console.log('***TARGET NAME'); 22 | return false; 23 | } 24 | if (msg.content.hasOwnProperty('target_module') && 25 | msg.content.target_module !== null && 26 | typeof msg.content.target_module !== 'string') { 27 | return false; 28 | } 29 | } 30 | if (typeof msg.content.comm_id !== 'string') { 31 | console.log("COMM_ID"); 32 | return false; 33 | } 34 | return true; 35 | } 36 | exports.validateCommMessage = validateCommMessage; 37 | function validateKernelHeader(header) { 38 | for (var i = 0; i < HEADER_FIELDS.length; i++) { 39 | if (!header.hasOwnProperty(HEADER_FIELDS[i])) { 40 | throw Error('Invalid Kernel message'); 41 | } 42 | if (typeof header[HEADER_FIELDS[i]] !== 'string') { 43 | throw Error('Invalid Kernel message'); 44 | } 45 | } 46 | } 47 | /** 48 | * Validate an object as being of IKernelMessage type. 49 | */ 50 | function validateKernelMessage(msg) { 51 | for (var i = 0; i < MESSAGE_FIELDS.length; i++) { 52 | if (!msg.hasOwnProperty(MESSAGE_FIELDS[i])) { 53 | throw Error('Invalid Kernel message'); 54 | } 55 | } 56 | validateKernelHeader(msg.header); 57 | if (Object.keys(msg.parent_header).length > 0) { 58 | validateKernelHeader(msg.parent_header); 59 | } 60 | if (typeof msg.channel !== 'string') { 61 | throw Error('Invalid Kernel message'); 62 | } 63 | if (!Array.isArray(msg.buffers)) { 64 | throw Error('Invalid Kernel message'); 65 | } 66 | } 67 | exports.validateKernelMessage = validateKernelMessage; 68 | /** 69 | * Validate an object as being of IKernelID type 70 | */ 71 | function validateKernelId(info) { 72 | if (!info.hasOwnProperty('name') || !info.hasOwnProperty('id')) { 73 | throw Error('Invalid kernel id'); 74 | } 75 | if ((typeof info.id !== 'string') || (typeof info.name !== 'string')) { 76 | throw Error('Invalid kernel id'); 77 | } 78 | } 79 | exports.validateKernelId = validateKernelId; 80 | /** 81 | * Validate an object as being of ISessionId type. 82 | */ 83 | function validateSessionId(info) { 84 | if (!info.hasOwnProperty('id') || 85 | !info.hasOwnProperty('notebook') || 86 | !info.hasOwnProperty('kernel')) { 87 | throw Error('Invalid Session Model'); 88 | } 89 | validateKernelId(info.kernel); 90 | if (typeof info.id !== 'string') { 91 | throw Error('Invalid Session Model'); 92 | } 93 | validateNotebookId(info.notebook); 94 | } 95 | exports.validateSessionId = validateSessionId; 96 | /** 97 | * Validate an object as being of INotebookId type. 98 | */ 99 | function validateNotebookId(model) { 100 | if ((!model.hasOwnProperty('path')) || (typeof model.path !== 'string')) { 101 | throw Error('Invalid Notebook Model'); 102 | } 103 | } 104 | exports.validateNotebookId = validateNotebookId; 105 | /** 106 | * Validate an object as being of IKernelSpecID type. 107 | */ 108 | function validateKernelSpec(info) { 109 | var err = new Error("Invalid KernelSpec Model"); 110 | if (!info.hasOwnProperty('name') || typeof info.name !== 'string') { 111 | throw err; 112 | } 113 | if (!info.hasOwnProperty('spec') || !info.hasOwnProperty('resources')) { 114 | throw err; 115 | } 116 | var spec = info.spec; 117 | if (!spec.hasOwnProperty('language') || typeof spec.language !== 'string') { 118 | throw err; 119 | } 120 | if (!spec.hasOwnProperty('display_name') || 121 | typeof spec.display_name !== 'string') { 122 | throw err; 123 | } 124 | if (!spec.hasOwnProperty('argv') || !Array.isArray(spec.argv)) { 125 | throw err; 126 | } 127 | } 128 | exports.validateKernelSpec = validateKernelSpec; 129 | //# sourceMappingURL=validate.js.map -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2015 Phosphor Contributors 3 | Distributed under the terms of the BSD 3-Clause License. 4 | The full license is in the file LICENSE, distributed with this software. 5 | """ 6 | import subprocess 7 | import sys 8 | 9 | import webbrowser 10 | import tornado.web 11 | 12 | 13 | class MainPageHandler(tornado.web.RequestHandler): 14 | 15 | def get(self): 16 | return self.render("index.html", static=self.static_url) 17 | 18 | 19 | def main(argv): 20 | 21 | url = "http://localhost:8765" 22 | 23 | handlers = [ 24 | (r"/", MainPageHandler), 25 | (r'/(.*)', tornado.web.StaticFileHandler, 26 | {'path': '.'}), 27 | (r'/components/font-awesome/fonts/(.*)', tornado.web.StaticFileHandler, 28 | {'path': './components/font-awesome/fonts/'}), 29 | ] 30 | 31 | nb_command = [sys.executable, '-m', 'notebook', '--no-browser', 32 | '--NotebookApp.allow_origin="%s"' % url] 33 | nb_server = subprocess.Popen(nb_command, stderr=subprocess.STDOUT, 34 | stdout=subprocess.PIPE) 35 | 36 | # wait for notebook server to start up 37 | while 1: 38 | line = nb_server.stdout.readline().decode('utf-8').strip() 39 | if not line: 40 | continue 41 | print(line) 42 | if 'The IPython Notebook is running at: http://localhost:8888/': 43 | break 44 | if 'Control-C' in line: 45 | raise ValueError( 46 | 'The port 8888 was already taken, kill running notebook servers' 47 | ) 48 | 49 | app = tornado.web.Application(handlers, static_path='build', 50 | template_path='.') 51 | 52 | app.listen(8765, 'localhost') 53 | loop = tornado.ioloop.IOLoop.instance() 54 | print('Browse to http://localhost:8765') 55 | #loop.add_callback(webbrowser.open, url) 56 | try: 57 | loop.start() 58 | except KeyboardInterrupt: 59 | print(" Shutting down on SIGINT") 60 | finally: 61 | nb_server.kill() 62 | loop.close() 63 | 64 | if __name__ == '__main__': 65 | main(sys.argv) 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "clean": "rimraf build", 4 | "build:src": "tsc --project src", 5 | "build:css": "node scripts/copycss.js", 6 | "build:browser": "browserify -t browserify-css build/index.js -o build/bundle.js", 7 | "build": "npm run build:src && npm run build:css && npm run build:browser", 8 | "postinstall": "npm dedupe && cd jupyter-js-services && npm install" 9 | }, 10 | "dependencies": { 11 | "phosphor-arrays": "^1.0.4", 12 | "phosphor-boxpanel": "^0.9.3", 13 | "phosphor-disposable": "^1.0.2", 14 | "phosphor-dockpanel": "^0.9.1", 15 | "phosphor-domutil": "^0.9.4", 16 | "phosphor-keymap": "^0.1.0", 17 | "phosphor-menus": "^0.9.4", 18 | "phosphor-properties": "^1.1.0", 19 | "phosphor-splitpanel": "^0.9.4", 20 | "phosphor-stackedpanel": "^0.9.4", 21 | "phosphor-tabs": "^0.9.5", 22 | "phosphor-widget": "^0.9.8", 23 | "term.js": "^0.0.7" 24 | }, 25 | "devDependencies": { 26 | "browserify": "^11.0.1", 27 | "browserify-css": "^0.6.1", 28 | "browserify-istanbul": "^0.2.1", 29 | "coveralls": "^2.11.4", 30 | "expect.js": "^0.3.1", 31 | "glob-copy": "^0.1.0", 32 | "mocha": "^2.2.5", 33 | "rimraf": "^2.4.2", 34 | "glob": "^5.0.14", 35 | "less": "~2", 36 | "jquery": "^2.1.4", 37 | "jupyter-js-output-area": "^0.0.5", 38 | "jupyter-js-services": "git://github.com/jupyter/jupyter-js-services.git", 39 | "mkdirp": "^0.5.1", 40 | "requirejs": "^2.1.20", 41 | "typedoc": "git://github.com/phosphorjs/typedoc.git", 42 | "typescript": "^1.6.2", 43 | "underscore": "^1.8.3" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /phosphor_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blink1073/jupyter_demo/906c18f008cd1ca60c440d07a981b8092fe9f3d9/phosphor_demo.gif -------------------------------------------------------------------------------- /scripts/copycss.js: -------------------------------------------------------------------------------- 1 | var cp = require('glob-copy'); 2 | cp.sync('src/*.css', 'build'); 3 | -------------------------------------------------------------------------------- /src/celltoolbar.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | "use strict"; 4 | 5 | export 6 | declare var CellToolbar: any; 7 | 8 | 9 | CellToolbar = function (options) { 10 | /** 11 | * Constructor 12 | * 13 | * Parameters: 14 | * options: dictionary 15 | * Dictionary of keyword arguments. 16 | * events: $(Events) instance 17 | * cell: Cell instance 18 | * notebook: Notebook instance 19 | * 20 | * TODO: This leaks, when cell are deleted 21 | * There is still a reference to each celltoolbars. 22 | */ 23 | CellToolbar._instances.push(this); 24 | this.notebook = options.notebook; 25 | this.cell = options.cell; 26 | this.create_element(); 27 | this.rebuild(); 28 | return this; 29 | }; 30 | 31 | 32 | CellToolbar.prototype.create_element = function () { 33 | this.inner_element = $('
').addClass('celltoolbar'); 34 | this.element = $('
').addClass('ctb_hideshow') 35 | .append(this.inner_element); 36 | }; 37 | 38 | 39 | // The default css style for the outer celltoolbar div 40 | // (ctb_hideshow) is display: none. 41 | // To show the cell toolbar, *both* of the following conditions must be met: 42 | // - A parent container has class `ctb_global_show` 43 | // - The celltoolbar has the class `ctb_show` 44 | // This allows global show/hide, as well as per-cell show/hide. 45 | 46 | CellToolbar.global_hide = function () { 47 | $('body').removeClass('ctb_global_show'); 48 | }; 49 | 50 | 51 | CellToolbar.global_show = function () { 52 | $('body').addClass('ctb_global_show'); 53 | }; 54 | 55 | 56 | CellToolbar.prototype.hide = function () { 57 | this.element.removeClass('ctb_show'); 58 | }; 59 | 60 | 61 | CellToolbar.prototype.show = function () { 62 | this.element.addClass('ctb_show'); 63 | }; 64 | 65 | 66 | /** 67 | * Class variable that should contain a dict of all available callback 68 | * we need to think of wether or not we allow nested namespace 69 | * @property _callback_dict 70 | * @private 71 | * @static 72 | * @type Dict 73 | */ 74 | CellToolbar._callback_dict = {}; 75 | 76 | 77 | /** 78 | * Class variable that should contain the reverse order list of the button 79 | * to add to the toolbar of each cell 80 | * @property _ui_controls_list 81 | * @private 82 | * @static 83 | * @type List 84 | */ 85 | CellToolbar._ui_controls_list = []; 86 | 87 | 88 | /** 89 | * Class variable that should contain the CellToolbar instances for each 90 | * cell of the notebook 91 | * 92 | * @private 93 | * @property _instances 94 | * @static 95 | * @type List 96 | */ 97 | CellToolbar._instances = []; 98 | 99 | 100 | /** 101 | * keep a list of all the available presets for the toolbar 102 | * @private 103 | * @property _presets 104 | * @static 105 | * @type Dict 106 | */ 107 | CellToolbar._presets = {}; 108 | 109 | 110 | // this is by design not a prototype. 111 | /** 112 | * Register a callback to create an UI element in a cell toolbar. 113 | * @method register_callback 114 | * @param name {String} name to use to refer to the callback. It is advised to use a prefix with the name 115 | * for easier sorting and avoid collision 116 | * @param callback {function(div, cell)} callback that will be called to generate the ui element 117 | * @param [cell_types] {List_of_String|undefined} optional list of cell types. If present the UI element 118 | * will be added only to cells of types in the list. 119 | * 120 | * 121 | * The callback will receive the following element : 122 | * 123 | * * a div in which to add element. 124 | * * the cell it is responsible from 125 | * 126 | * @example 127 | * 128 | * Example that create callback for a button that toggle between `true` and `false` label, 129 | * with the metadata under the key 'foo' to reflect the status of the button. 130 | * 131 | * // first param reference to a DOM div 132 | * // second param reference to the cell. 133 | * var toggle = function(div, cell) { 134 | * var button_container = $(div) 135 | * 136 | * // let's create a button that show the current value of the metadata 137 | * var button = $('
').button({label:String(cell.metadata.foo)}); 138 | * 139 | * // On click, change the metadata value and update the button label 140 | * button.click(function(){ 141 | * var v = cell.metadata.foo; 142 | * cell.metadata.foo = !v; 143 | * button.button("option", "label", String(!v)); 144 | * }) 145 | * 146 | * // add the button to the DOM div. 147 | * button_container.append(button); 148 | * } 149 | * 150 | * // now we register the callback under the name `foo` to give the 151 | * // user the ability to use it later 152 | * CellToolbar.register_callback('foo', toggle); 153 | */ 154 | CellToolbar.register_callback = function(name, callback, cell_types) { 155 | // Overwrite if it already exists. 156 | CellToolbar._callback_dict[name] = cell_types ? {callback: callback, cell_types: cell_types} : callback; 157 | }; 158 | 159 | 160 | /** 161 | * Register a preset of UI element in a cell toolbar. 162 | * Not supported Yet. 163 | * @method register_preset 164 | * @param name {String} name to use to refer to the preset. It is advised to use a prefix with the name 165 | * for easier sorting and avoid collision 166 | * @param preset_list {List_of_String} reverse order of the button in the toolbar. Each String of the list 167 | * should correspond to a name of a registerd callback. 168 | * 169 | * @private 170 | * @example 171 | * 172 | * CellToolbar.register_callback('foo.c1', function(div, cell){...}); 173 | * CellToolbar.register_callback('foo.c2', function(div, cell){...}); 174 | * CellToolbar.register_callback('foo.c3', function(div, cell){...}); 175 | * CellToolbar.register_callback('foo.c4', function(div, cell){...}); 176 | * CellToolbar.register_callback('foo.c5', function(div, cell){...}); 177 | * 178 | * CellToolbar.register_preset('foo.foo_preset1', ['foo.c1', 'foo.c2', 'foo.c5']) 179 | * CellToolbar.register_preset('foo.foo_preset2', ['foo.c4', 'foo.c5']) 180 | */ 181 | CellToolbar.register_preset = function(name, preset_list, notebook) { 182 | CellToolbar._presets[name] = preset_list; 183 | //events.trigger('preset_added.CellToolbar', {name: name}); 184 | // When "register_callback" is called by a custom extension, it may be executed after notebook is loaded. 185 | // In that case, activate the preset if needed. 186 | if (notebook && notebook.metadata && notebook.metadata.celltoolbar === name){ 187 | CellToolbar.activate_preset(name); 188 | } 189 | }; 190 | 191 | /** 192 | * unregister the selected preset, 193 | * 194 | * return true if preset successfully unregistered 195 | * false otherwise 196 | * 197 | **/ 198 | CellToolbar.unregister_preset = function(name){ 199 | if(CellToolbar._presets[name]){ 200 | delete CellToolbar._presets[name]; 201 | //events.trigger('unregistered_preset.CellToolbar', {name: name}); 202 | return true 203 | } 204 | return false 205 | } 206 | 207 | 208 | /** 209 | * List the names of the presets that are currently registered. 210 | * 211 | * @method list_presets 212 | * @static 213 | */ 214 | CellToolbar.list_presets = function() { 215 | var keys = []; 216 | for (var k in CellToolbar._presets) { 217 | keys.push(k); 218 | } 219 | return keys; 220 | }; 221 | 222 | 223 | /** 224 | * Activate an UI preset from `register_preset` 225 | * 226 | * This does not update the selection UI. 227 | * 228 | * @method activate_preset 229 | * @param preset_name {String} string corresponding to the preset name 230 | * 231 | * @static 232 | * @private 233 | * @example 234 | * 235 | * CellToolbar.activate_preset('foo.foo_preset1'); 236 | */ 237 | CellToolbar.activate_preset = function(preset_name){ 238 | var preset = CellToolbar._presets[preset_name]; 239 | 240 | if(preset !== undefined){ 241 | CellToolbar._ui_controls_list = preset; 242 | CellToolbar.rebuild_all(); 243 | } 244 | 245 | //events.trigger('preset_activated.CellToolbar', {name: preset_name}); 246 | }; 247 | 248 | 249 | /** 250 | * This should be called on the class and not on a instance as it will trigger 251 | * rebuild of all the instances. 252 | * @method rebuild_all 253 | * @static 254 | * 255 | */ 256 | CellToolbar.rebuild_all = function(){ 257 | for(var i=0; i < CellToolbar._instances.length; i++){ 258 | CellToolbar._instances[i].rebuild(); 259 | } 260 | }; 261 | 262 | /** 263 | * Rebuild all the button on the toolbar to update its state. 264 | * @method rebuild 265 | */ 266 | CellToolbar.prototype.rebuild = function(){ 267 | /** 268 | * strip evrything from the div 269 | * which is probably inner_element 270 | * or this.element. 271 | */ 272 | this.inner_element.empty(); 273 | this.ui_controls_list = []; 274 | 275 | var callbacks = CellToolbar._callback_dict; 276 | var preset = CellToolbar._ui_controls_list; 277 | // Yes we iterate on the class variable, not the instance one. 278 | for (var i=0; i < preset.length; i++) { 279 | var key = preset[i]; 280 | var callback = callbacks[key]; 281 | if (!callback) continue; 282 | 283 | if (typeof callback === 'object') { 284 | if (callback.cell_types.indexOf(this.cell.cell_type) === -1) continue; 285 | callback = callback.callback; 286 | } 287 | 288 | var local_div = $('
').addClass('button_container'); 289 | try { 290 | callback(local_div, this.cell, this); 291 | this.ui_controls_list.push(key); 292 | } catch (e) { 293 | console.log("Error in cell toolbar callback " + key, e); 294 | continue; 295 | } 296 | // only append if callback succeeded. 297 | this.inner_element.append(local_div); 298 | } 299 | 300 | // If there are no controls or the cell is a rendered TextCell hide the toolbar. 301 | if (!this.ui_controls_list.length) { 302 | this.hide(); 303 | } else { 304 | this.show(); 305 | } 306 | }; 307 | 308 | 309 | CellToolbar.utils = {}; 310 | 311 | 312 | /** 313 | * A utility function to generate bindings between a checkbox and cell/metadata 314 | * @method utils.checkbox_ui_generator 315 | * @static 316 | * 317 | * @param name {string} Label in front of the checkbox 318 | * @param setter {function( cell, newValue )} 319 | * A setter method to set the newValue 320 | * @param getter {function( cell )} 321 | * A getter methods which return the current value. 322 | * 323 | * @return callback {function( div, cell )} Callback to be passed to `register_callback` 324 | * 325 | * @example 326 | * 327 | * An exmple that bind the subkey `slideshow.isSectionStart` to a checkbox with a `New Slide` label 328 | * 329 | * var newSlide = CellToolbar.utils.checkbox_ui_generator('New Slide', 330 | * // setter 331 | * function(cell, value){ 332 | * // we check that the slideshow namespace exist and create it if needed 333 | * if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}} 334 | * // set the value 335 | * cell.metadata.slideshow.isSectionStart = value 336 | * }, 337 | * //geter 338 | * function(cell){ var ns = cell.metadata.slideshow; 339 | * // if the slideshow namespace does not exist return `undefined` 340 | * // (will be interpreted as `false` by checkbox) otherwise 341 | * // return the value 342 | * return (ns == undefined)? undefined: ns.isSectionStart 343 | * } 344 | * ); 345 | * 346 | * CellToolbar.register_callback('newSlide', newSlide); 347 | * 348 | */ 349 | CellToolbar.utils.checkbox_ui_generator = function(name, setter, getter){ 350 | return function(div, cell, celltoolbar) { 351 | var button_container = $(div); 352 | 353 | var chkb = $('').attr('type', 'checkbox'); 354 | var lbl = $('