diff --git a/jupyter_notebook/static/notebook/js/notebook.js b/jupyter_notebook/static/notebook/js/notebook.js index db3519e546..81d6a8f4e2 100644 --- a/jupyter_notebook/static/notebook/js/notebook.js +++ b/jupyter_notebook/static/notebook/js/notebook.js @@ -1578,7 +1578,7 @@ define(function (require) { notebook_path: this.notebook_path, notebook_name: this.notebook_name, kernel_name: kernel_name, - notebook: this + events: this.events }; var success = $.proxy(this._session_started, this); diff --git a/jupyter_notebook/static/phosphor-notebook/.bowerrc b/jupyter_notebook/static/phosphor-notebook/.bowerrc new file mode 100644 index 0000000000..333544ec7a --- /dev/null +++ b/jupyter_notebook/static/phosphor-notebook/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory" : "components" +} \ No newline at end of file diff --git a/jupyter_notebook/static/phosphor-notebook/.gitignore b/jupyter_notebook/static/phosphor-notebook/.gitignore new file mode 100644 index 0000000000..4ff37d9b43 --- /dev/null +++ b/jupyter_notebook/static/phosphor-notebook/.gitignore @@ -0,0 +1,2 @@ +components +typings \ No newline at end of file diff --git a/jupyter_notebook/static/phosphor-notebook/bower.json b/jupyter_notebook/static/phosphor-notebook/bower.json new file mode 100644 index 0000000000..b00ddbca26 --- /dev/null +++ b/jupyter_notebook/static/phosphor-notebook/bower.json @@ -0,0 +1,26 @@ +{ + "name": "phosphor-notebook", + "main": "index.js", + "version": "0.0.1", + "authors": [ + "Jason Grout " + ], + "description": "Phosphor-based IPython notebook", + "moduleType": [ + "amd" + ], + "license": "BSD", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "jupyter_notebook/static/components", + "test", + "tests" + ], + "dependencies": { + "phosphor": "~0.2.0", + "codemirror": "~5.2.0", + "marked": "~0.3.3" + } +} diff --git a/jupyter_notebook/static/phosphor-notebook/gulpfile.js b/jupyter_notebook/static/phosphor-notebook/gulpfile.js new file mode 100644 index 0000000000..28d6e14b3d --- /dev/null +++ b/jupyter_notebook/static/phosphor-notebook/gulpfile.js @@ -0,0 +1,69 @@ +// based on Chris Colbert's gulpfile.js at https://github.com/phosphorjs/phosphor/blob/master/gulpfile.js +// (which is licensed BSD 3-clause) + +"use strict"; +var gulp = require("gulp"); +var typescript = require("gulp-typescript"); +var concat = require('gulp-concat'); +var header = require('gulp-header'); +var stream = require('event-stream'); +var rename = require('gulp-rename'); +var del = require('del'); + + +var typings = ["./typings/tsd.d.ts", "./components/phosphor/dist/phosphor.d.ts"]; + +var tsSources = [ + "index", + "app", + "NotebookComponent", + "nbformat", + "demodata", + "mathjaxutils", +].map(function(name) {return "./src/" + name + ".ts"; }); + + +gulp.task('clean', function(cb) { + del(['./dist'], cb); +}); + + +gulp.task('src', function() { + var project = typescript.createProject({ + declarationFiles: true, + noImplicitAny: true, + target: 'ES5', + module: 'amd' + }); + + var src = gulp.src(typings.concat(tsSources)) + .pipe(typescript(project)); + + var dts = src.dts.pipe(concat('phosphor-notebook.d.ts')) + .pipe(gulp.dest('./dist')); + + var js = src//.pipe(concat('phosphor-notebook.js')) + .pipe(header('"use strict";\n')) + .pipe(gulp.dest('./dist')); + + return stream.merge(dts, js); +}); + + +gulp.task('build', ['src']); + + +gulp.task('dist', ['build'], function() { + return gulp.src('./dist/phosphor-notebook.js') + //.pipe(uglify()) + .pipe(rename('phosphor-notebook.min.js')) + .pipe(gulp.dest('./dist')); +}); + + +gulp.task('watch', function() { + gulp.watch(tsSources, ['src']); +}); + + +gulp.task('default', ['dist']); diff --git a/jupyter_notebook/static/phosphor-notebook/index.html b/jupyter_notebook/static/phosphor-notebook/index.html new file mode 100644 index 0000000000..2e26b79aa6 --- /dev/null +++ b/jupyter_notebook/static/phosphor-notebook/index.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/jupyter_notebook/static/phosphor-notebook/package.json b/jupyter_notebook/static/phosphor-notebook/package.json new file mode 100644 index 0000000000..b93f6ad2b8 --- /dev/null +++ b/jupyter_notebook/static/phosphor-notebook/package.json @@ -0,0 +1,37 @@ +{ + "name": "phosphor-notebook", + "version": "0.1.0", + "description": "Jupyter Notebook nodejs dependencies", + "author": "Jupyter Developers", + "license": "BSD-3-Clause", + "repository": { + "type": "git", + "url": "https://github.com/jupyter/jupyter_notebook.git" + }, + "devDependencies": { + "bower": "*", + "gulp": "^3.8.11", + "gulp-less": "^3.0.3", + "gulp-minify-css": "^1.1.1", + "gulp-rename": "^1.2.2", + "gulp-sourcemaps": "^1.5.2", + "less": "~2", + "source-map": "^0.4.2" + }, + "bugs": { + "url": "https://github.com/jupyter/jupyter_notebook/issues" + }, + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "gulp": "~3.8.11", + "gulp-concat": "~2.5.2", + "gulp-header": "~1.2.2", + "event-stream": "~3.3.1", + "gulp-rename": "~1.2.2", + "del": "~1.1.1", + "gulp-typescript": "~2.7.3" + } +} diff --git a/jupyter_notebook/static/phosphor-notebook/src/NotebookComponent.ts b/jupyter_notebook/static/phosphor-notebook/src/NotebookComponent.ts new file mode 100644 index 0000000000..65ff127bbf --- /dev/null +++ b/jupyter_notebook/static/phosphor-notebook/src/NotebookComponent.ts @@ -0,0 +1,208 @@ +/// + +/* TODO: + +For executing, need: + +* kernelselector +* session +* kernel +* comm + + +*/ + + +import nbformat = require("./nbformat"); +import mathjaxutils = require("./mathjaxutils"); +import DOM = phosphor.virtualdom.dom; +import Component = phosphor.virtualdom.Component; +import BaseComponent = phosphor.virtualdom.BaseComponent; +import Elem = phosphor.virtualdom.Elem; +import createFactory = phosphor.virtualdom.createFactory; +import render = phosphor.virtualdom.render; +import IMessage = phosphor.core.IMessage; + +var div = DOM.div; +var pre = DOM.pre; +var img = DOM.img; + +class MimeBundleComponent extends Component { + render() { + // possible optimization: iterate through + var x: string | string[]; + if (x = this.data["image/png"]) { + return img({src:"data:image/png;base64,"+x}) + } else if (x = this.data["image/jpg"]) { + return img({src:"data:image/jpg;base64,"+x}) + } else if (x = this.data["text/plain"]) { + return pre(x); + } + } +} +export var MimeBundle = createFactory(MimeBundleComponent); + +class ExecuteResultComponent extends Component { + render() { + return MimeBundle(this.data.data); + } +} +export var ExecuteResult = createFactory(ExecuteResultComponent); + +class DisplayDataComponent extends Component { + render() { + return MimeBundle(this.data.data); + } +} +export var DisplayData = createFactory(DisplayDataComponent); + +class StreamComponent extends Component { + render() { + return pre(this.data.text); + } +} +export var Stream = createFactory(StreamComponent); + +class JupyterErrorComponent extends Component { + render() { + var o = this.data; + return pre(o.ename+'\n'+o.evalue+'\n'+(o.traceback.join('\n'))); + } +} +export var JupyterError = createFactory(JupyterErrorComponent) + +// customized renderer example from marked.js readme +var renderer = new (marked).Renderer(); +renderer.heading = function (text: string, level: number) { + var escapedText = text.toLowerCase().replace(/[^\w]+/g, '-'); + return `${text}ΒΆ`; +} + +renderer.unescape = function(html: string): string { + // from https://github.com/chjj/marked/blob/2b5802f258c5e23e48366f2377fbb4c807f47658/lib/marked.js#L1085 + return html.replace(/&([#\w]+);/g, function(_, n) { + n = n.toLowerCase(); + if (n === 'colon') return ':'; + if (n.charAt(0) === '#') { + return n.charAt(1) === 'x' + ? String.fromCharCode(parseInt(n.substring(2), 16)) + : String.fromCharCode(+n.substring(1)); + } + return ''; + }); +} + +renderer.check_url = function(href: string): boolean { + try { + var prot = decodeURIComponent(this.unescape(href)) + .replace(/[^\w:]/g, '') + .toLowerCase(); + } catch (e) { + return false; + } + if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) { + return false; + } + return true; +}; + +renderer.link = function(href: string, title: string, text: string) { + //modified from the mark.js source to open all urls in new tabs + if (this.options.sanitize && !this.check_url(href)) { + return ''; + } + return `${text}`; +}; + +class MarkdownCellComponent extends BaseComponent { + onUpdateRequest(msg: IMessage): void { + // replace the innerHTML of the node with the rendered markdown + var t = mathjaxutils.remove_math(this.data.source); + marked(t.html, { sanitize: true, renderer: renderer}, (err: any, html: string) => { + this.node.innerHTML = mathjaxutils.replace_math(html, t.math); + // TODO: do some serious sanitization, using, for example, the caja sanitizer + MathJax.Hub.Queue(["Typeset", MathJax.Hub, this.node]); + }); + } +} +export var MarkdownCell = createFactory(MarkdownCellComponent) + + +/** + * We inherit from BaseComponent so that we can explicitly control the rendering. We want to use the virtual dom to render + * the output, but we want to explicitly manage the code editor. +*/ +class CodeCellComponent extends BaseComponent { + + constructor(data: nbformat.CodeCell, children: Elem[]) { + super(data, children); + this.editor_node = document.createElement('div'); + this.editor_node.classList.add("ipy-input") + this.output_node = document.createElement('div'); + this.node.appendChild(this.editor_node); + this.node.appendChild(this.output_node); + + this._editor = CodeMirror(this.editor_node, { + mode: 'python', + value: this.data.source, + lineNumbers: true}) + } + + protected onUpdateRequest(msg: IMessage): void { + // we could call setValue on the editor itself, but the dts file doesn't recognize it. + this._editor.getDoc().setValue(this.data.source); + // we may want to save the refs at some point + render(this.renderOutput(), this.output_node); + } + + protected onAfterAttach(msg: IMessage): void { + this._editor.refresh(); + } + renderOutput(): Elem[] { + var r: Elem[] = []; + var outputs: nbformat.Output[] = this.data.outputs; + for(var i = 0; i < outputs.length; i++) { + var x = outputs[i]; + switch(x.output_type) { + case "execute_result": + r.push(ExecuteResult(x)); + break; + case "display_data": + r.push(DisplayData(x)); + break; + case "stream": + r.push(Stream(x)); + break; + case "error": + r.push(JupyterError(x)); + break; + } + } + return r; + } + + editor_node: HTMLElement; + output_node: HTMLElement; + _editor: CodeMirror.Editor; +} +export var CodeCell = createFactory(CodeCellComponent); + +class NotebookComponent extends Component { + render() { + var cells = this.data.cells; + var r: Elem[] = []; + for(var i = 0; i < cells.length; i++) { + var c = cells[i]; + switch(c.cell_type) { + case "code": + r.push(CodeCell(c)); + break; + case "markdown": + r.push(MarkdownCell(c)); + break; + } + } + return r; + } +} +export var Notebook = createFactory(NotebookComponent); diff --git a/jupyter_notebook/static/phosphor-notebook/src/app.ts b/jupyter_notebook/static/phosphor-notebook/src/app.ts new file mode 100644 index 0000000000..3071267a67 --- /dev/null +++ b/jupyter_notebook/static/phosphor-notebook/src/app.ts @@ -0,0 +1,12 @@ +import Bootstrapper = phosphor.shell.Bootstrapper; + +export +class NotebookApplication extends Bootstrapper { + configurePlugins(): Promise { + return this.pluginList.add([ + // Notebook + ]); + } +} + +console.log('loaded app'); diff --git a/jupyter_notebook/static/phosphor-notebook/src/demodata.ts b/jupyter_notebook/static/phosphor-notebook/src/demodata.ts new file mode 100644 index 0000000000..cd36224aa5 --- /dev/null +++ b/jupyter_notebook/static/phosphor-notebook/src/demodata.ts @@ -0,0 +1,196 @@ +import nbformat = require("./nbformat"); + +var multiToString = function(x: string | string[]): string { + return (typeof x === "string") ? x : x.join(""); +} + +var diskToMemory = function(disknb: any): nbformat.Notebook { + // Make a copy + var nb = JSON.parse(JSON.stringify(disknb)); + // Convert multiline strings that are arrays to just strings + var cells = nb.cells; + for (var i = 0; ihi\n", + "\n", + "Math formulas like $e^{\\pi i} + 1 = 0$ and the following work too. $$\\int x^2\\, dx$$\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello World\n" + ] + } + ], + "source": [ + "print \"Hello World\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Populating the interactive namespace from numpy and matplotlib\n" + ] + }, + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\ngKo6CVyY5OImhxRUcQa4Cbgn4dK255HUbYsk948C7wWe2+Px1wBPzNz+NnD0kHNpF/bv0rgk/Mqq\nP3vB/k+ctwLfq6pTSbb2Wzp3e9dTcJLcMXPzRFWdWGBG7XQc2GLSv3v+uzQwk732J34d3nY9XLry\n32Huu7kD1wI3THv1I8ArkvxFVd0ys+Y7wCUzt49O73uBqrpj1UE1UUUl3Ao8lPBgFfe1PZOkJtVZ\n4G3AY8DPw53fXeVZ9q1lqup9VXVJVV0K3Az809zGDnA/cAtAkmuAp6rq9CrDaDH279LwJBxJuJPJ\nnvph4MYqVt5LD0ru82oyRG4DqKq7q+qBJMeSfAv4IZOzOrRmVZycHgifSbiuimfanknSahKuAj7J\nJK3/0mE29eef0ytU+8vPn5H6LeEI8EEmofh24LPTU59n1niF6uh4/rvUX9O0/q/A5UzS+mfmN/ZD\nPb/Jvf/8/BmpPxZJ6zvXm9xHy/PfpX5Yd1rf8Vom92Gwf5e6a9m0vvNnTe6jZv8uddMm0/qO1zW5\nD4v9u9QNh0nrO5/H5C7s36UuaCut75jB5D489u9SO5pK6zuf0+SuKft3afO6kNZ3zGNyHy77d2n9\n1pHWdz6/yV1z7N+l9epaWp9lch84+3epeetO6ztfy+SuXdi/S83qclqfZXIfCft36XA2mdZ3vq7J\nXfuwf5dW15e0PsvkPiL279Jy2krrO2cwuesA9u/S4vqY1meZ3EfI/l3aWxfS+s55TO5akP27tLu+\np/VZJveRsn+XzulaWp9lctdS7N+liSGl9Vkm95Gzf9dYdTmtzzK5ayX27xqjoab1WSZ32b9rNPqS\n1meZ3LUy+3eNwRjS+iyTu55n/64h6mNan2Vy16HZv2toxpbWZ5nctYP9u4ag72l9lsldjbB/V9+N\nOa3PMrlrV/bv6pshpfVZJnc1yv5dfWJafyGTu/Zk/66uG2pan2VyV+Ps39VlpvX9mdx1IPt3dckY\n0vosk7vWxv5dXWFaX5zJXQuxf1ebxpbWZ5nctVb272qLaX01Jnctxf5dmzLmtD7L5K6NsH/XJpjW\nD8/krqXZv2tdTOsvZHLXxti/ax1M680yuWtl9u9qgml9fyZ3bZz9uw7LtL4+Jncdiv27VmFaX5zJ\nXa2wf9eyTOubYXJXI+zfdRDT+mpM7mqV/bv2Y1rfvAM39yRHkpxM8nCSR5PcucuarSRPJzk1/Xr/\n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\nNED/D64LHKfgo8K1AAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%pylab inline\n", + "\n", + "plot([1,2,3],[5,3,4])" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'x' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;31m# error\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[1;32mprint\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;31mNameError\u001b[0m: name 'x' is not defined" + ] + } + ], + "source": [ + "# error\n", + "print x" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1+2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.9" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} + +export var notebook: nbformat.Notebook = diskToMemory(notebook_disk); diff --git a/jupyter_notebook/static/phosphor-notebook/src/index.ts b/jupyter_notebook/static/phosphor-notebook/src/index.ts new file mode 100644 index 0000000000..6d8c6777c4 --- /dev/null +++ b/jupyter_notebook/static/phosphor-notebook/src/index.ts @@ -0,0 +1,43 @@ +import NotebookApp = require("./app"); +import NotebookComponent = require("./NotebookComponent"); +import render = phosphor.virtualdom.render; +import demo = require("./demodata") +import mathjaxutils = require("./mathjaxutils"); +export function main(): void { + // var notebook = new NotebookApp.NotebookApplication; + // notebook.run(); + var test = document.getElementById('test'); + mathjaxutils.init(); + render(NotebookComponent.Notebook(demo.notebook), test); +}; + + +/* + + this.session = new session.Session(options); + this.session.start(success, failure); + + Notebook.prototype._session_started = function (){ + this._session_starting = false; + this.kernel = this.session.kernel; + var ncells = this.ncells(); + for (var i=0; i by named entities. +// For IE, put
at the ends of comments since IE removes \n. +// Clear the current math positions and store the index of the +// math, then push the math string onto the storage array. +// The preProcess function is called on all blocks if it has been passed in +var process_math = function (i: number, j: number, pre_process: (x: string)=>string, math: string[], blocks: string[]) { + var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&") // use HTML entity for & + .replace(//g, ">") // use HTML entity for > + ; + /* + if (utils.browser === 'msie') { + block = block.replace(/(%[^\n]*)\n/g, "$1
\n"); + } + */ + while (j > i) { + blocks[j] = ""; + j--; + } + blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later + if (pre_process){ + block = pre_process(block); + } + math.push(block); + return blocks; +}; + +// Break up the text into its component parts and search +// through them for math delimiters, braces, linebreaks, etc. +// Math delimiters must match and braces must balance. +// Don't allow math to pass through a double linebreak +// (which will be a paragraph). +// +export var remove_math = function (text: string): {html: string; math: string[]} { + var math: string[] = []; // stores math strings for later + var start: number; + var end: string; + var last: number; + var braces: number; + + // Except for extreme edge cases, this should catch precisely those pieces of the markdown + // source that will later be turned into code spans. While MathJax will not TeXify code spans, + // we still have to consider them at this point; the following issue has happened several times: + // + // `$foo` and `$bar` are varibales. --> $foo ` and `$bar are variables. + + var hasCodeSpans = /`/.test(text); + var de_tilde: (text: string) => string; + de_tilde; + if (hasCodeSpans) { + text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function (wholematch) { + return wholematch.replace(/\$/g, "~D"); + }); + de_tilde = function (text) { + return text.replace(/~([TD])/g, function(wholematch, character) { + switch (character) { + case "T": return "~"; + case "D": return "$"; + } + }); + }; + } else { + de_tilde = function (text) { return text; }; + } + + // it seems that with recent browsers (IE9, FF 35, Chrome 42, for example), the built-in browser split + // function passes all the tests that the old IPython utils.regex_split function claimed it fixed + // see http://web.archive.org/web/20130803042736/http://stevenlevithan.com/demo/split.cfm for the tests + + var blocks = text.replace(/\r\n?/g, "\n").split(MATHSPLIT); + + for (var i = 1, m = blocks.length; i < m; i += 2) { + var block = blocks[i]; + if (block.charAt(0) === "@") { + // + // Things that look like our math markers will get + // stored and then retrieved along with the math. + // + blocks[i] = "@@" + math.length + "@@"; + math.push(block); + } + else if (start) { + // + // If we are in math, look for the end delimiter, + // but don't go past double line breaks, and + // and balance braces within the math. + // + if (block === end) { + if (braces) { + last = i; + } + else { + blocks = process_math(start, i, de_tilde, math, blocks); + start = null; + end = null; + last = null; + } + } + else if (block.match(/\n.*\n/)) { + if (last) { + i = last; + blocks = process_math(start, i, de_tilde, math, blocks); + } + start = null; + end = null; + last = null; + braces = 0; + } + else if (block === "{") { + braces++; + } + else if (block === "}" && braces) { + braces--; + } + } + else { + // + // Look for math start delimiters and when + // found, set up the end delimiter. + // + if (block === inline || block === "$$") { + start = i; + end = block; + braces = 0; + } + else if (block.substr(1, 5) === "begin") { + start = i; + end = "\\end" + block.substr(6); + braces = 0; + } + } + } + if (last) { + blocks = process_math(start, last, de_tilde, math, blocks); + start = null; + end = null; + last = null; + } + return { html: de_tilde(blocks.join("")), math: math }; +}; + +// +// Put back the math strings that were saved, +// and clear the math array (no need to keep it around). +// +export var replace_math = function (text: string, math: string[]) { + text = text.replace(/@@(\d+)@@/g, function (match, n) { + return math[n]; + }); + return text; +}; \ No newline at end of file diff --git a/jupyter_notebook/static/phosphor-notebook/src/nbformat.ts b/jupyter_notebook/static/phosphor-notebook/src/nbformat.ts new file mode 100644 index 0000000000..f1290cf825 --- /dev/null +++ b/jupyter_notebook/static/phosphor-notebook/src/nbformat.ts @@ -0,0 +1,115 @@ +// Notebook format interfaces + +// In the notebook format *disk* representation, this would be string | string[] +export type multilineString = string; + +export +interface MimeBundle { + // values are always multilineString if we pretend that the application/json key doesn't exist + // in fact, the in-memory representation always is a string + [key: string]: multilineString; + + // we fudge the standard a bit here by not telling Typescript about the application/json + // key, which will be a Javascript object if it exists. If we want to tell, then uncomment below: + //"application/json": {}; +} + +export +interface ExecuteResult { + output_type: string; // "execute_result" + execution_count: number; + data: MimeBundle; + metadata: {}; +} + +export +interface DisplayData { + output_type: string; // "display_data" + data: MimeBundle; + metadata: {}; +} + +export +interface Stream { + output_type: string; // "stream" + name: string; + text: multilineString; +} + +export +interface JupyterError { + output_type: string; // "error" + ename: string; + evalue: string; + traceback: string[]; +} + +export +type Output = ExecuteResult | DisplayData | Stream | JupyterError; + +export +type Cell = BaseCell | RawCell | MarkdownCell | CodeCell; + +export +interface BaseCell { + cell_type: string; + metadata: { + name?: string; + tags?: string[]; + } +} + +export +interface RawCell extends BaseCell { + cell_type: string; /*"raw"*/ + source: multilineString; + metadata: { + format?: string; + } +} + +export +interface MarkdownCell extends BaseCell { + cell_type: string; /*"markdown"*/ + source: multilineString; +} + +export +interface CodeCell extends BaseCell { + cell_type: string; /*"code"*/ + source: multilineString; + metadata: { + collapsed?: boolean; + scrolled?: boolean | string; + } + outputs: Output[]; + execution_count: number; +} + +export +interface Notebook { + metadata: { + kernelspec: { + name: string; + display_name: string; + }; + language_info: { + name: string; + codemirror_mode?: string | {}; + file_extension?: string; + mimetype?: string; + pygments_lexer?: string + }; + orig_nbformat?: number; + } + nbformat_minor: number; + nbformat: number; + cells: Cell[]; +} + +export +interface NBData { + content: Notebook; + name: string; + path: string; +} diff --git a/jupyter_notebook/static/phosphor-notebook/tsconfig.json b/jupyter_notebook/static/phosphor-notebook/tsconfig.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/jupyter_notebook/static/phosphor-notebook/tsd.json b/jupyter_notebook/static/phosphor-notebook/tsd.json new file mode 100644 index 0000000000..2d08a3e336 --- /dev/null +++ b/jupyter_notebook/static/phosphor-notebook/tsd.json @@ -0,0 +1,21 @@ +{ + "version": "v4", + "repo": "borisyankov/DefinitelyTyped", + "ref": "master", + "path": "typings", + "bundle": "typings/tsd.d.ts", + "installed": { + "codemirror/codemirror.d.ts": { + "commit": "52fa2b9ce36f2c5843204e9bfbaada7cded9b47a" + }, + "es6-promise/es6-promise.d.ts": { + "commit": "52fa2b9ce36f2c5843204e9bfbaada7cded9b47a" + }, + "marked/marked.d.ts": { + "commit": "575d70adcad0086ec8c6f672a5eb569a4689486c" + }, + "mathjax/mathjax.d.ts": { + "commit": "575d70adcad0086ec8c6f672a5eb569a4689486c" + } + } +} diff --git a/jupyter_notebook/static/services/sessions/session.js b/jupyter_notebook/static/services/sessions/session.js index 923558df6e..b9345adac4 100644 --- a/jupyter_notebook/static/services/sessions/session.js +++ b/jupyter_notebook/static/services/sessions/session.js @@ -19,7 +19,7 @@ define([ * - kernel_name: the type of kernel (e.g. python3) * - base_url: the root url of the notebook server * - ws_url: the url to access websockets - * - notebook: Notebook object + * - events: events object to listen to * * @class Session * @param {Object} options @@ -39,9 +39,8 @@ define([ this.session_service_url = utils.url_join_encode(this.base_url, 'api/sessions'); this.session_url = null; - this.notebook = options.notebook; this.kernel = null; - this.events = options.notebook.events; + this.events = options.events; this.bind_events(); };