├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── examples ├── filebrowser │ ├── index.css │ ├── index.html │ ├── main.py │ ├── package.json │ ├── sample.md │ ├── src │ │ ├── index.ts │ │ ├── tsconfig.json │ │ └── typings.d.ts │ └── webpack.conf.js └── terminal │ ├── index.css │ ├── index.html │ ├── main.py │ ├── package.json │ ├── src │ ├── index.ts │ ├── tsconfig.json │ └── typings.d.ts │ └── webpack.conf.js ├── package.json ├── scripts ├── buildexamples.js ├── cleanexamples.js ├── copycss.js ├── travis_after_success.sh ├── travis_install.sh └── travis_script.sh ├── src ├── clipboard │ └── index.ts ├── codemirror │ ├── codemirror-ipython.ts │ ├── codemirror-ipythongfm.ts │ ├── index.ts │ └── widget.ts ├── dialog │ ├── index.css │ ├── index.ts │ └── theme.css ├── docmanager │ ├── context.ts │ ├── default.ts │ ├── editor.ts │ ├── images.ts │ ├── index.ts │ ├── interfaces.ts │ ├── kernelselector.ts │ ├── manager.ts │ ├── registry.ts │ └── theme.css ├── filebrowser │ ├── browser.ts │ ├── buttons.ts │ ├── crumbs.ts │ ├── dialogs.ts │ ├── index.css │ ├── index.ts │ ├── listing.ts │ ├── model.ts │ ├── theme.css │ ├── tsconfig.json │ └── utils.ts ├── index.css ├── renderers │ ├── index.ts │ └── latex.ts ├── rendermime │ └── index.ts ├── terminal │ ├── index.css │ ├── index.ts │ ├── theme.css │ └── tsconfig.json ├── theme.css ├── tsconfig.json ├── typings.d.ts └── widgets │ └── index.ts ├── test ├── karma-cov.conf.js ├── karma.conf.js ├── src │ ├── dialog │ │ └── dialog.spec.ts │ ├── index.ts │ ├── renderers │ │ ├── latex.spec.ts │ │ └── renderers.spec.ts │ ├── rendermime │ │ └── rendermime.spec.ts │ ├── tsconfig.json │ ├── typings.d.ts │ └── utils.ts ├── webpack-cov.conf.js └── webpack.conf.js └── typings ├── ansi_up └── ansi_up.d.ts ├── backbone ├── backbone-global.d.ts └── backbone.d.ts ├── codemirror └── codemirror.d.ts ├── es6-promise └── es6-promise.d.ts ├── expect.js └── expect.js.d.ts ├── jquery └── jquery.d.ts ├── jupyter-js-widgets └── jupyter-js-widgets.d.ts ├── marked └── marked.d.ts ├── mathjax └── mathjax.d.ts ├── mocha └── mocha.d.ts ├── moment └── moment.d.ts ├── require └── require.d.ts ├── underscore └── underscore.d.ts └── xterm └── xterm.d.ts /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | *.user 3 | .DS_Store 4 | node_modules 5 | coverage 6 | lib 7 | build 8 | docs 9 | .ipynb_checkpoints 10 | Unititled* 11 | untitled* 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.12' 4 | sudo: false 5 | env: 6 | matrix: 7 | - GROUP= 8 | global: 9 | - GH_REF: github.com/jupyter/jupyter-js-ui.git 10 | - secure: ubVHruPyZKaY5IeG8VKjlIp0lTaAToNqWzLTzeT36GTz9DgCWvjRmbt4ZjezGToW4+eG2PT0OEDW5N7MBxmTxbOgdy4q6H98DsIeYLOFXaiggpWt5fpe8MWQcqIm3QIqzgJF4oEtMjAYvRFDgZHOq8ozChafjYB2Xt/G44in7cx7zp+AQwX5ACc9ku8N2x9Iyv7PtOl4Wb16ngQbLhJmNiX/x15lpA656Zwq2agPD5fdcu0w5fPbnkVXbCFDdSKbzrkyFvYDcbJUjE8lir+Eoj8rHwLpznMji6aunRaIXIeuNfRh2mxpZLdphlVJc+1KB69iPD40he21G/93gFrfee44H4jND+z07VFPt1QKBaPbdJxV40A6aJ8AtV1n8BvNjLEC+zTzGyhlIYcvb6c5Az70ERY+icz77zzBGesoXxRIGCmQSKcThhxuTicbJqpYcHQecvpuY9dCzeT8250FCkfeoFd9mxQaqyGjaJMLWnN/fsdDHmFuFD6BRlDMTgPYCfY9D8rMikd8D4WDEZKxx1AMPBDcKWXFvnaCmkYq0Of4ka8UUOZdX7MUC5JL6atGEMxPMyMoZqaGsIfFKhDH6LRuDKPSZMyNmTXGWBzcd3BZTXuw1W3CzM88zdTwoLjsEs6fcu1/rAwJllv/Ygq5CuaClC5BqEBmaYGOk4eVQDA= 11 | install: 12 | - bash ./scripts/travis_install.sh 13 | script: 14 | - bash ./scripts/travis_script.sh 15 | after_success: 16 | - bash ./scripts/travis_after_success.sh 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We follow the [IPython Contributing Guide](https://github.com/ipython/ipython/blob/master/CONTRIBUTING.md). 4 | 5 | All source code is written in [TypeScript](http://www.typescriptlang.org/Handbook). See the [Style Guide](https://github.com/phosphorjs/phosphor/wiki/Style-Guide). 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Project Jupyter 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of jupyter-js-filebrowser nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Jupyter JS UI 2 | ============= 3 | 4 | JavaScript UI Components for Jupyter. 5 | 6 | [API Docs](http://jupyter.github.io/jupyter-js-ui/) 7 | 8 | 9 | Package Install 10 | --------------- 11 | 12 | **Prerequisites** 13 | - [node](http://nodejs.org/) 14 | - [python](https://www.continuum.io/downloads) 15 | 16 | ```bash 17 | npm install --save jupyter-js-ui 18 | conda install notebook # notebook 4.2+ required 19 | ``` 20 | 21 | 22 | Source Build 23 | ------------ 24 | 25 | **Prerequisites** 26 | - [git](http://git-scm.com/) 27 | - [node 0.12+](http://nodejs.org/) 28 | - [python](https://www.continuum.io/downloads) 29 | 30 | ```bash 31 | git clone https://github.com/jupyter/jupyter-js-ui.git 32 | cd jupyter-js-ui 33 | npm install 34 | npm run build 35 | conda install notebook # notebook 4.2+ required 36 | ``` 37 | 38 | **Rebuild** 39 | ```bash 40 | npm run clean 41 | npm run build 42 | ``` 43 | 44 | 45 | Run Tests 46 | --------- 47 | 48 | Follow the source build instructions first. 49 | 50 | ```bash 51 | npm test 52 | ``` 53 | 54 | 55 | Build Examples 56 | -------------- 57 | 58 | Follow the source build instructions first. 59 | Requires a Python install with the Jupyter notebook. 60 | 61 | ```bash 62 | npm run build:examples 63 | ``` 64 | 65 | Change to appropriate `examples` directory and run `python main.py`. 66 | 67 | 68 | Build Docs 69 | ---------- 70 | 71 | Follow the source build instructions first. 72 | 73 | ```bash 74 | npm run docs 75 | ``` 76 | 77 | Navigate to `docs/index.html`. 78 | 79 | 80 | Supported Runtimes 81 | ------------------ 82 | 83 | The runtime versions which are currently *known to work* are listed below. 84 | Earlier versions may also work, but come with no guarantees. 85 | 86 | - IE 11+ 87 | - Firefox 32+ 88 | - Chrome 38+ 89 | 90 | Note: "requirejs" must be included in a global context (usually as a 91 | ` 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/filebrowser/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) Jupyter Development Team. 3 | Distributed under the terms of the Modified BSD License. 4 | """ 5 | import re 6 | import subprocess 7 | import sys 8 | import threading 9 | 10 | import tornado.web 11 | 12 | # Install the pyzmq ioloop. This has to be done before anything else from 13 | # tornado is imported. 14 | from zmq.eventloop import ioloop 15 | ioloop.install() 16 | 17 | PORT = 8765 18 | 19 | 20 | class MainPageHandler(tornado.web.RequestHandler): 21 | 22 | def initialize(self, base_url): 23 | self.base_url = base_url 24 | 25 | def get(self): 26 | return self.render("index.html", static=self.static_url, 27 | base_url=self.base_url) 28 | 29 | 30 | def main(argv): 31 | 32 | url = "http://localhost:%s" % PORT 33 | 34 | nb_command = [sys.executable, '-m', 'notebook', '--no-browser', '--debug', 35 | '--NotebookApp.allow_origin="%s"' % url] 36 | nb_server = subprocess.Popen(nb_command, stderr=subprocess.STDOUT, 37 | stdout=subprocess.PIPE) 38 | 39 | # wait for notebook server to start up 40 | while 1: 41 | line = nb_server.stdout.readline().decode('utf-8').strip() 42 | if not line: 43 | continue 44 | print(line) 45 | if 'Jupyter Notebook is running at:' in line: 46 | base_url = re.search('(http.*?)$', line).groups()[0] 47 | break 48 | 49 | while 1: 50 | line = nb_server.stdout.readline().decode('utf-8').strip() 51 | if not line: 52 | continue 53 | print(line) 54 | if 'Control-C' in line: 55 | break 56 | 57 | def print_thread(): 58 | while 1: 59 | line = nb_server.stdout.readline().decode('utf-8').strip() 60 | if not line: 61 | continue 62 | print(line) 63 | thread = threading.Thread(target=print_thread) 64 | thread.setDaemon(True) 65 | thread.start() 66 | 67 | handlers = [ 68 | (r"/", MainPageHandler, {'base_url': base_url}), 69 | (r'/(.*)', tornado.web.StaticFileHandler, {'path': '.'}), 70 | ] 71 | 72 | app = tornado.web.Application(handlers, static_path='build', 73 | template_path='.', 74 | compiled_template_cache=False) 75 | 76 | app.listen(PORT, 'localhost') 77 | 78 | if sys.platform.startswith('win'): 79 | # add no-op to wake every 5s 80 | # to handle signals that may be ignored by the inner loop 81 | pc = ioloop.PeriodicCallback(lambda: None, 5000) 82 | pc.start() 83 | 84 | loop = ioloop.IOLoop.current() 85 | print('Browse to http://localhost:%s' % PORT) 86 | try: 87 | loop.start() 88 | except KeyboardInterrupt: 89 | print(" Shutting down on SIGINT") 90 | finally: 91 | nb_server.kill() 92 | loop.close() 93 | 94 | if __name__ == '__main__': 95 | main(sys.argv) 96 | -------------------------------------------------------------------------------- /examples/filebrowser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "example", 4 | "scripts": { 5 | "build": "tsc --project src && webpack --config webpack.conf.js", 6 | "clean": "rimraf build && rimraf node_modules", 7 | "postinstall": "npm dedupe", 8 | "update": "rimraf node_modules/jupyter-js-ui && npm install", 9 | "watch": "watch 'npm run update && npm run build' ../../src src --wait 10" 10 | }, 11 | "dependencies": { 12 | "jupyter-js-services": "^0.10.4", 13 | "jupyter-js-ui": "file:../..", 14 | "phosphor-dockpanel": "^0.9.7", 15 | "phosphor-keymap": "^0.8.0", 16 | "phosphor-menus": "^1.0.0-rc.1", 17 | "phosphor-splitpanel": "^1.0.0-rc.1" 18 | }, 19 | "devDependencies": { 20 | "css-loader": "^0.23.1", 21 | "rimraf": "^2.5.2", 22 | "style-loader": "^0.13.1", 23 | "typescript": "^1.8.10", 24 | "watch": "^0.18.0", 25 | "webpack": "^1.13.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/filebrowser/sample.md: -------------------------------------------------------------------------------- 1 | # Title first level 2 | ## Title second Level 3 | ### Title third level 4 | #### h4 5 | ##### h5 6 | ###### h6 7 | # h1 8 | ## h2 9 | ### h3 10 | #### h4 11 | ##### h6 12 | This is just a sample paragraph 13 | You can look at different level of nested unorderd list ljbakjn arsvlasc asc asc awsc asc ascd ascd ascd asdc asc 14 | - level 1 15 | - level 2 16 | - level 2 17 | - level 2 18 | - level 3 19 | - level 3 20 | - level 4 21 | - level 5 22 | - level 6 23 | - level 2 24 | - level 1 25 | - level 1 26 | - level 1 27 | Ordered list 28 | 1. level 1 29 | 2. level 1 30 | 3. level 1 31 | 4. level 1 32 | 1. level 1 33 | 2. level 1 34 | 2. level 1 35 | 3. level 1 36 | 4. level 1 37 | 1. level 1 38 | 2. level 1 39 | 3. level 1 40 | 4. level 1 41 | some Horizontal line 42 | *** 43 | and another one 44 | --- 45 | Colons can be used to align columns. 46 | | Tables | Are | Cool | 47 | | ------------- |:-------------:| -----:| 48 | | col 3 is | right-aligned | 1600 | 49 | | col 2 is | centered | 12 | 50 | | zebra stripes | are neat | 1 | 51 | There must be at least 3 dashes separating each header cell. 52 | The outer pipes (|) are optional, and you don't need to make the 53 | raw Markdown line up prettily. You can also use inline Markdown. 54 | -------------------------------------------------------------------------------- /examples/filebrowser/src/index.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | | Copyright (c) 2014-2015, Jupyter Development Team. 3 | | 4 | | Distributed under the terms of the Modified BSD License. 5 | |----------------------------------------------------------------------------*/ 6 | 'use strict'; 7 | 8 | import { 9 | ContentsManager, NotebookSessionManager, 10 | IKernelSpecIds 11 | } from 'jupyter-js-services'; 12 | 13 | import { 14 | FileBrowserWidget, FileBrowserModel 15 | } from 'jupyter-js-ui/lib/filebrowser'; 16 | 17 | import { 18 | DocumentManager, DocumentWidget, DocumentRegistry 19 | } from 'jupyter-js-ui/lib/docmanager'; 20 | 21 | import { 22 | TextModelFactory 23 | } from 'jupyter-js-ui/lib/docmanager/default'; 24 | 25 | import { 26 | EditorWidgetFactory 27 | } from 'jupyter-js-ui/lib/docmanager/editor'; 28 | 29 | 30 | import { 31 | showDialog, okButton 32 | } from 'jupyter-js-ui/lib/dialog'; 33 | 34 | import { 35 | DockPanel 36 | } from 'phosphor-dockpanel'; 37 | 38 | import { 39 | KeymapManager 40 | } from 'phosphor-keymap'; 41 | 42 | import { 43 | Menu, MenuItem 44 | } from 'phosphor-menus'; 45 | 46 | import { 47 | SplitPanel 48 | } from 'phosphor-splitpanel'; 49 | 50 | import 'jupyter-js-ui/lib/index.css'; 51 | import 'jupyter-js-ui/lib/theme.css'; 52 | 53 | 54 | function main(): void { 55 | let sessionsManager = new NotebookSessionManager(); 56 | sessionsManager.getSpecs().then(specs => { 57 | createApp(sessionsManager, specs); 58 | }); 59 | } 60 | 61 | 62 | function createApp(sessionsManager: NotebookSessionManager, specs: IKernelSpecIds): void { 63 | let contentsManager = new ContentsManager(); 64 | let widgets: DocumentWidget[] = []; 65 | let activeWidget: DocumentWidget; 66 | 67 | let opener = { 68 | open: (widget: DocumentWidget) => { 69 | if (widgets.indexOf(widget) === -1) { 70 | dock.insertTabAfter(widget); 71 | widgets.push(widget); 72 | } 73 | dock.selectWidget(widget); 74 | activeWidget = widget; 75 | widget.disposed.connect((w: DocumentWidget) => { 76 | let index = widgets.indexOf(w); 77 | widgets.splice(index, 1); 78 | }); 79 | } 80 | }; 81 | 82 | let docRegistry = new DocumentRegistry(); 83 | let docManager = new DocumentManager( 84 | docRegistry, contentsManager, sessionsManager, specs, opener 85 | ); 86 | let mFactory = new TextModelFactory(); 87 | let wFactory = new EditorWidgetFactory(); 88 | docRegistry.registerModelFactory(mFactory); 89 | docRegistry.registerWidgetFactory(wFactory, { 90 | displayName: 'Editor', 91 | modelName: 'text', 92 | fileExtensions: ['.*'], 93 | defaultFor: ['.*'], 94 | preferKernel: false, 95 | canStartKernel: true 96 | }); 97 | 98 | let fbModel = new FileBrowserModel(contentsManager, sessionsManager, specs); 99 | let fbWidget = new FileBrowserWidget(fbModel, docManager, opener); 100 | 101 | let panel = new SplitPanel(); 102 | panel.id = 'main'; 103 | panel.addChild(fbWidget); 104 | SplitPanel.setStretch(fbWidget, 0); 105 | let dock = new DockPanel(); 106 | panel.addChild(dock); 107 | SplitPanel.setStretch(dock, 1); 108 | dock.spacing = 8; 109 | 110 | document.addEventListener('focus', event => { 111 | for (let i = 0; i < widgets.length; i++) { 112 | let widget = widgets[i]; 113 | if (widget.node.contains(event.target as HTMLElement)) { 114 | activeWidget = widget; 115 | break; 116 | } 117 | } 118 | }); 119 | 120 | let keymapManager = new KeymapManager(); 121 | keymapManager.add([{ 122 | sequence: ['Enter'], 123 | selector: '.jp-DirListing', 124 | handler: () => { 125 | fbWidget.open(); 126 | } 127 | }, { 128 | sequence: ['Ctrl N'], // Add emacs keybinding for select next. 129 | selector: '.jp-DirListing', 130 | handler: () => { 131 | fbWidget.selectNext(); 132 | } 133 | }, { 134 | sequence: ['Ctrl P'], // Add emacs keybinding for select previous. 135 | selector: '.jp-DirListing', 136 | handler: () => { 137 | fbWidget.selectPrevious(); 138 | } 139 | }, { 140 | sequence: ['Accel S'], 141 | selector: '.jp-CodeMirrorWidget', 142 | handler: () => { 143 | activeWidget.context.save(); 144 | } 145 | }]); 146 | 147 | window.addEventListener('keydown', (event) => { 148 | keymapManager.processKeydownEvent(event); 149 | }); 150 | 151 | let contextMenu = new Menu([ 152 | new MenuItem({ 153 | text: '&Open', 154 | icon: 'fa fa-folder-open-o', 155 | shortcut: 'Ctrl+O', 156 | handler: () => { fbWidget.open(); } 157 | }), 158 | new MenuItem({ 159 | text: '&Rename', 160 | icon: 'fa fa-edit', 161 | shortcut: 'Ctrl+R', 162 | handler: () => { fbWidget.rename(); } 163 | }), 164 | new MenuItem({ 165 | text: '&Delete', 166 | icon: 'fa fa-remove', 167 | shortcut: 'Ctrl+D', 168 | handler: () => { fbWidget.delete(); } 169 | }), 170 | new MenuItem({ 171 | text: 'Duplicate', 172 | icon: 'fa fa-copy', 173 | handler: () => { fbWidget.duplicate(); } 174 | }), 175 | new MenuItem({ 176 | text: 'Cut', 177 | icon: 'fa fa-cut', 178 | shortcut: 'Ctrl+X', 179 | handler: () => { fbWidget.cut(); } 180 | }), 181 | new MenuItem({ 182 | text: '&Copy', 183 | icon: 'fa fa-copy', 184 | shortcut: 'Ctrl+C', 185 | handler: () => { fbWidget.copy(); } 186 | }), 187 | new MenuItem({ 188 | text: '&Paste', 189 | icon: 'fa fa-paste', 190 | shortcut: 'Ctrl+V', 191 | handler: () => { fbWidget.paste(); } 192 | }), 193 | new MenuItem({ 194 | text: 'Download', 195 | icon: 'fa fa-download', 196 | handler: () => { fbWidget.download(); } 197 | }), 198 | new MenuItem({ 199 | text: 'Shutdown Kernel', 200 | icon: 'fa fa-stop-circle-o', 201 | handler: () => { fbWidget.shutdownKernels(); } 202 | }), 203 | new MenuItem({ 204 | text: 'Dialog Demo', 205 | handler: () => { dialogDemo(); } 206 | }), 207 | new MenuItem({ 208 | text: 'Info Demo', 209 | handler: () => { 210 | let msg = 'The quick brown fox jumped over the lazy dog' 211 | showDialog({ 212 | title: 'Cool Title', 213 | body: msg, 214 | buttons: [okButton] 215 | }); 216 | } 217 | }) 218 | ]); 219 | 220 | // Add a context menu to the dir listing. 221 | let node = fbWidget.node.getElementsByClassName('jp-DirListing-content')[0]; 222 | node.addEventListener('contextmenu', (event: MouseEvent) => { 223 | event.preventDefault(); 224 | let x = event.clientX; 225 | let y = event.clientY; 226 | contextMenu.popup(x, y); 227 | }); 228 | 229 | panel.attach(document.body); 230 | 231 | window.onresize = () => panel.update(); 232 | } 233 | 234 | 235 | /** 236 | * Create a non-functional dialog demo. 237 | */ 238 | function dialogDemo(): void { 239 | let body = document.createElement('div'); 240 | let input = document.createElement('input'); 241 | input.value = 'Untitled.ipynb' 242 | let selector = document.createElement('select'); 243 | let option0 = document.createElement('option'); 244 | option0.value = 'python'; 245 | option0.text = 'Python 3'; 246 | selector.appendChild(option0); 247 | let option1 = document.createElement('option'); 248 | option1.value = 'julia'; 249 | option1.text = 'Julia'; 250 | selector.appendChild(option1); 251 | body.appendChild(input); 252 | body.appendChild(selector); 253 | showDialog({ 254 | title: 'Create new notebook', 255 | body, 256 | }); 257 | } 258 | 259 | 260 | window.onload = main; 261 | -------------------------------------------------------------------------------- /examples/filebrowser/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noImplicitAny": true, 4 | "noEmitOnError": true, 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "target": "ES5", 8 | "outDir": "../build" 9 | }, 10 | "files": [ 11 | "index.ts", 12 | "typings.d.ts" 13 | ] 14 | } -------------------------------------------------------------------------------- /examples/filebrowser/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /examples/filebrowser/webpack.conf.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = { 4 | entry: './build/index.js', 5 | output: { 6 | path: __dirname + "/build", 7 | filename: "bundle.js", 8 | publicPath: "./build/" 9 | }, 10 | node: { 11 | fs: "empty" 12 | }, 13 | bail: true, 14 | debug: true, 15 | devtool: 'source-map', 16 | module: { 17 | loaders: [ 18 | { test: /\.css$/, loader: 'style-loader!css-loader' }, 19 | { test: /\.html$/, loader: "file?name=[name].[ext]" } 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/terminal/index.css: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | | Copyright (c) Jupyter Development Team. 3 | | Distributed under the terms of the Modified BSD License. 4 | |----------------------------------------------------------------------------*/ 5 | body { 6 | margin: 0; 7 | padding: 0; 8 | } 9 | 10 | 11 | #main { 12 | position: absolute; 13 | top: 10px; 14 | left: 10px; 15 | right: 10px; 16 | bottom: 10px; 17 | padding: 8px; 18 | } 19 | 20 | 21 | .p-SplitPanel { 22 | height: 400px; 23 | } 24 | 25 | 26 | .p-DockTabPanel { 27 | padding-right: 2px; 28 | padding-bottom: 2px; 29 | } 30 | 31 | 32 | .p-DockTabPanel > .p-StackedPanel { 33 | padding: 10px; 34 | background: white; 35 | border: 1px solid #C0C0C0; 36 | border-top: none; 37 | box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2); 38 | } 39 | 40 | 41 | .p-DockPanel-overlay { 42 | background: rgba(255, 255, 255, 0.6); 43 | border: 1px dashed black; 44 | } 45 | 46 | 47 | .p-DockPanel-overlay.p-mod-root-top, 48 | .p-DockPanel-overlay.p-mod-root-left, 49 | .p-DockPanel-overlay.p-mod-root-right, 50 | .p-DockPanel-overlay.p-mod-root-bottom, 51 | .p-DockPanel-overlay.p-mod-root-center { 52 | border-width: 2px; 53 | } 54 | 55 | 56 | .p-TabBar { 57 | min-height: 24px; 58 | max-height: 24px; 59 | } 60 | 61 | 62 | .p-TabBar-header { 63 | display: none; 64 | } 65 | 66 | 67 | .p-TabBar-footer { 68 | flex: 0 0 1px; 69 | background: #C0C0C0; 70 | } 71 | 72 | 73 | .p-TabBar-content { 74 | min-width: 0; 75 | align-items: flex-end; 76 | } 77 | 78 | 79 | .p-TabBar-tab { 80 | flex: 0 1 125px; 81 | min-height: 20px; 82 | max-height: 20px; 83 | min-width: 35px; 84 | margin-left: -1px; 85 | border: 1px solid #C0C0C0; 86 | border-bottom: none; 87 | padding: 0px 10px; 88 | background: #E5E5E5; 89 | font: 12px Helvetica, Arial, sans-serif; 90 | } 91 | 92 | 93 | .p-TabBar-tab:first-child { 94 | margin-left: 0; 95 | } 96 | 97 | 98 | .p-TabBar-tab.p-mod-current { 99 | min-height: 23px; 100 | max-height: 23px; 101 | background: white; 102 | transform: translateY(1px); 103 | } 104 | 105 | 106 | .p-TabBar-tab:hover:not(.p-mod-current) { 107 | background: #F0F0F0; 108 | } 109 | 110 | 111 | .p-TabBar-tabIcon, 112 | .p-TabBar-tabText, 113 | .p-TabBar-tabCloseIcon { 114 | line-height: 20px; 115 | } 116 | 117 | 118 | .p-TabBar-tab.p-mod-closable > .p-TabBar-tabCloseIcon { 119 | margin-left: 4px; 120 | } 121 | 122 | 123 | .p-TabBar-tab.p-mod-closable > .p-TabBar-tabCloseIcon:before { 124 | content: '\f00d'; 125 | font-family: FontAwesome; 126 | } 127 | 128 | 129 | .p-TabBar-tab.p-mod-drag-image { 130 | min-height: 23px; 131 | max-height: 23px; 132 | min-width: 125px; 133 | border: none; 134 | box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3); 135 | transform: translateX(-40%) translateY(-58%); 136 | } 137 | 138 | .p-MenuBar { 139 | padding-left: 5px; 140 | background: #FAFAFA; 141 | color: rgba(0, 0, 0, 0.87); 142 | border-bottom: 1px solid #DDDDDD; 143 | font: 13px Helvetica, Arial, sans-serif; 144 | } 145 | 146 | 147 | .p-MenuBar-menu { 148 | transform: translateY(-1px); 149 | } 150 | 151 | 152 | .p-MenuBar-item { 153 | padding: 4px 8px; 154 | border-left: 1px solid transparent; 155 | border-right: 1px solid transparent; 156 | } 157 | 158 | 159 | .p-MenuBar-item.p-mod-active { 160 | background: #E5E5E5; 161 | } 162 | 163 | 164 | .p-MenuBar-item.p-mod-disabled { 165 | color: rgba(0, 0, 0, 0.26); 166 | } 167 | 168 | 169 | .p-MenuBar-item.p-type-separator { 170 | margin: 2px; 171 | padding: 0; 172 | border: none; 173 | border-left: 1px solid #DDDDDD; 174 | } 175 | 176 | 177 | .p-MenuBar.p-mod-active .p-MenuBar-item.p-mod-active { 178 | z-index: 10001; 179 | background: white; 180 | border-left: 1px solid #C0C0C0; 181 | border-right: 1px solid #C0C0C0; 182 | box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.2); 183 | } 184 | 185 | 186 | .p-Menu { 187 | z-index: 10000; 188 | padding: 3px 0px; 189 | background: white; 190 | color: rgba(0, 0, 0, 0.87); 191 | border: 1px solid #C0C0C0; 192 | font: 12px Helvetica, Arial, sans-serif; 193 | box-shadow: 0px 1px 6px rgba(0, 0, 0, 0.2); 194 | } 195 | 196 | 197 | .p-Menu-item.p-mod-active { 198 | background: #E5E5E5; 199 | } 200 | 201 | 202 | .p-Menu-item.p-mod-disabled { 203 | color: rgba(0, 0, 0, 0.26); 204 | } 205 | 206 | 207 | .p-Menu-itemIcon { 208 | width: 21px; 209 | padding: 4px 2px; 210 | } 211 | 212 | 213 | .p-Menu-itemText { 214 | padding: 4px 35px 4px 2px; 215 | } 216 | 217 | 218 | .p-Menu-itemShortcut { 219 | padding: 4px 0px; 220 | } 221 | 222 | 223 | .p-Menu-itemSubmenuIcon { 224 | width: 16px; 225 | padding: 4px 0px; 226 | } 227 | 228 | 229 | .p-Menu-item.p-type-separator > span { 230 | padding: 0; 231 | height: 9px; 232 | } 233 | 234 | 235 | .p-Menu-item.p-type-separator > span::after { 236 | content: ''; 237 | display: block; 238 | position: relative; 239 | top: 4px; 240 | border-top: 1px solid #DDDDDD; 241 | } 242 | 243 | 244 | .p-Menu-itemIcon::before, 245 | .p-Menu-itemSubmenuIcon::before { 246 | font-family: FontAwesome; 247 | } 248 | 249 | 250 | .p-Menu-item.p-type-check.p-mod-checked > .p-Menu-itemIcon::before { 251 | content: '\f00c'; 252 | } 253 | 254 | 255 | .p-Menu-item.p-type-submenu > .p-Menu-itemSubmenuIcon::before { 256 | content: '\f0da'; 257 | } 258 | -------------------------------------------------------------------------------- /examples/terminal/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Terminal Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/terminal/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) Jupyter Development Team. 3 | Distributed under the terms of the Modified BSD License. 4 | """ 5 | import re 6 | import subprocess 7 | import sys 8 | import threading 9 | import tornado.web 10 | 11 | # Install the pyzmq ioloop. This has to be done before anything else from 12 | # tornado is imported. 13 | from zmq.eventloop import ioloop 14 | ioloop.install() 15 | 16 | PORT = 8765 17 | 18 | 19 | class MainPageHandler(tornado.web.RequestHandler): 20 | 21 | def initialize(self, ws_url, base_url): 22 | self.base_url = base_url 23 | self.ws_url = ws_url 24 | 25 | def get(self): 26 | return self.render("index.html", static=self.static_url, 27 | ws_url=self.ws_url, base_url=self.base_url) 28 | 29 | 30 | def main(argv): 31 | 32 | url = "http://localhost:%s" % PORT 33 | 34 | nb_command = [sys.executable, '-m', 'notebook', '--no-browser', '--debug', 35 | '--NotebookApp.allow_origin="%s"' % url] 36 | nb_server = subprocess.Popen(nb_command, stderr=subprocess.STDOUT, 37 | stdout=subprocess.PIPE) 38 | 39 | # wait for notebook server to start up 40 | while 1: 41 | line = nb_server.stdout.readline().decode('utf-8').strip() 42 | if not line: 43 | continue 44 | print(line) 45 | if 'Jupyter Notebook is running at:' in line: 46 | host = re.search('http(.*?)$', line).groups()[0] 47 | ws_url = 'ws' + host 48 | break 49 | 50 | while 1: 51 | line = nb_server.stdout.readline().decode('utf-8').strip() 52 | if not line: 53 | continue 54 | print(line) 55 | if 'Control-C' in line: 56 | break 57 | 58 | def print_thread(): 59 | while 1: 60 | line = nb_server.stdout.readline().decode('utf-8').strip() 61 | if not line: 62 | continue 63 | print(line) 64 | thread = threading.Thread(target=print_thread) 65 | thread.setDaemon(True) 66 | thread.start() 67 | 68 | base_url = 'http' + host 69 | handlers = [ 70 | (r"/", MainPageHandler, {'ws_url': ws_url, 'base_url': base_url}), 71 | (r'/(.*)', tornado.web.StaticFileHandler, 72 | {'path': '.'}), 73 | ] 74 | 75 | app = tornado.web.Application(handlers, static_path='build', 76 | template_path='.', 77 | compiled_template_cache=False) 78 | 79 | app.listen(PORT, 'localhost') 80 | 81 | if sys.platform.startswith('win'): 82 | # add no-op to wake every 5s 83 | # to handle signals that may be ignored by the inner loop 84 | pc = ioloop.PeriodicCallback(lambda: None, 5000) 85 | pc.start() 86 | 87 | loop = ioloop.IOLoop.current() 88 | print('Browse to http://localhost:%s' % PORT) 89 | try: 90 | loop.start() 91 | except KeyboardInterrupt: 92 | print(" Shutting down on SIGINT") 93 | finally: 94 | nb_server.kill() 95 | loop.close() 96 | 97 | if __name__ == '__main__': 98 | main(sys.argv) 99 | -------------------------------------------------------------------------------- /examples/terminal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "example", 4 | "scripts": { 5 | "build": "tsc --project src && webpack --config webpack.conf.js", 6 | "clean": "rimraf build && rimraf node_modules", 7 | "postinstall": "npm dedupe", 8 | "update": "rimraf node_modules/jupyter-js-ui && npm install", 9 | "watch": "watch 'npm run update && npm run build' ../../src src --wait 10" 10 | }, 11 | "dependencies": { 12 | "jupyter-js-ui": "file:../..", 13 | "phosphor-dockpanel": "^0.9.7" 14 | }, 15 | "devDependencies": { 16 | "css-loader": "^0.23.1", 17 | "rimraf": "^2.5.2", 18 | "style-loader": "^0.13.1", 19 | "typescript": "^1.8.10", 20 | "watch": "^0.18.0", 21 | "webpack": "^1.13.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/terminal/src/index.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | | Copyright (c) 2014-2015, Jupyter Development Team. 3 | | 4 | | Distributed under the terms of the Modified BSD License. 5 | |----------------------------------------------------------------------------*/ 6 | 'use strict'; 7 | 8 | import { 9 | DockPanel 10 | } from 'phosphor-dockpanel'; 11 | 12 | import { 13 | TerminalWidget 14 | } from 'jupyter-js-ui/lib/terminal'; 15 | 16 | import 'jupyter-js-ui/lib/index.css'; 17 | import 'jupyter-js-ui/lib/theme.css'; 18 | 19 | 20 | function main(): void { 21 | let term1 = new TerminalWidget({ background: 'black', 22 | color: 'white'}); 23 | let term2 = new TerminalWidget({ background: 'white', 24 | color: 'black'}); 25 | 26 | term1.title.closable = true; 27 | term2.title.closable = true; 28 | let dock = new DockPanel(); 29 | dock.insertTabBefore(term1); 30 | dock.insertTabBefore(term2); 31 | 32 | dock.attach(document.body); 33 | dock.id = 'main'; 34 | 35 | window.onresize = () => dock.fit(); 36 | } 37 | 38 | 39 | window.onload = main; 40 | -------------------------------------------------------------------------------- /examples/terminal/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noImplicitAny": true, 4 | "noEmitOnError": true, 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "target": "ES5", 8 | "outDir": "../build" 9 | }, 10 | "files": [ 11 | "index.ts", 12 | "typings.d.ts" 13 | ] 14 | } -------------------------------------------------------------------------------- /examples/terminal/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /examples/terminal/webpack.conf.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | entry: './build/index.js', 4 | output: { 5 | path: __dirname + "/build", 6 | filename: "bundle.js", 7 | publicPath: "./build/" 8 | }, 9 | node: { 10 | fs: "empty" 11 | }, 12 | bail: true, 13 | devtool: 'source-map', 14 | module: { 15 | loaders: [ 16 | { test: /\.css$/, loader: 'style-loader!css-loader' }, 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jupyter-js-ui", 3 | "version": "0.14.0", 4 | "description": "JavaScript UI Components for Jupyter", 5 | "main": "lib/index.js", 6 | "typings": "lib/index.d.ts", 7 | "dependencies": { 8 | "ansi_up": "^1.3.0", 9 | "backbone": "^1.2.0", 10 | "codemirror": "^5.12.0", 11 | "jupyter-js-services": "^0.10.4", 12 | "jupyter-js-utils": "^0.4.0", 13 | "jupyter-js-widgets": "2.0.0-dev.0", 14 | "marked": "^0.3.5", 15 | "moment": "^2.11.2", 16 | "phosphor-arrays": "^1.0.6", 17 | "phosphor-domutil": "^1.2.0", 18 | "phosphor-dragdrop": "^0.9.0", 19 | "phosphor-menus": "^1.0.0-rc.1", 20 | "phosphor-messaging": "^1.0.6", 21 | "phosphor-panel": "^1.0.0-rc.1", 22 | "phosphor-properties": "^2.0.0", 23 | "phosphor-widget": "^1.0.0-rc.1", 24 | "xterm": "^0.33.0" 25 | }, 26 | "devDependencies": { 27 | "concurrently": "^2.0.0", 28 | "css-loader": "^0.23.1", 29 | "expect.js": "^0.3.1", 30 | "file-loader": "^0.8.5", 31 | "fs-extra": "^0.26.4", 32 | "istanbul-instrumenter-loader": "^0.1.3", 33 | "json-loader": "^0.5.4", 34 | "karma": "^0.13.19", 35 | "karma-chrome-launcher": "^0.2.2", 36 | "karma-coverage": "^0.5.3", 37 | "karma-firefox-launcher": "^0.1.7", 38 | "karma-ie-launcher": "^0.2.0", 39 | "karma-mocha": "^0.2.1", 40 | "karma-mocha-reporter": "^1.1.5", 41 | "mocha": "^2.3.4", 42 | "raw-loader": "^0.5.1", 43 | "rimraf": "^2.5.0", 44 | "style-loader": "^0.13.0", 45 | "typedoc": "https://github.com/SierraSoftworks/typedoc.git#cb5aea2b218b4f773451e2818a48629ee9c5066e", 46 | "typescript": "^1.8.0", 47 | "url-loader": "^0.5.7", 48 | "watch": "^0.17.1", 49 | "webpack": "^1.12.11" 50 | }, 51 | "scripts": { 52 | "clean": "rimraf docs && rimraf lib && rimraf test/build && rimraf test/coverage", 53 | "clean:examples": "node scripts/cleanexamples.js", 54 | "build:examples": "node scripts/buildexamples.js", 55 | "build:src": "tsc --project src && node scripts/copycss.js", 56 | "build:test": "tsc --project test/src && webpack --config test/webpack.conf.js", 57 | "build": "npm run build:src", 58 | "docs": "typedoc --mode modules --module commonjs --excludeNotExported --target es5 --moduleResolution node --out docs/ src", 59 | "prepublish": "npm run build", 60 | "postinstall": "npm dedupe", 61 | "test:chrome": "karma start --browsers=Chrome test/karma.conf.js", 62 | "test:coverage": "npm run build:test && webpack --config test/webpack-cov.conf.js && karma start test/karma-cov.conf.js", 63 | "test:firefox": "karma start --browsers=Firefox test/karma.conf.js", 64 | "test:ie": "karma start --browsers=IE test/karma.conf.js", 65 | "test:debug": "karma start --browsers=Chrome --singleRun=false --debug=true test/karma.conf.js", 66 | "test": "npm run build:test && npm run test:firefox", 67 | "watch": "watch 'npm run build' src --wait 10", 68 | "watch:test": "watch 'npm run build && npm test' src src/test --wait 10" 69 | }, 70 | "repository": { 71 | "type": "git", 72 | "url": "https://github.com/jupyter/jupyter-js-ui" 73 | }, 74 | "keywords": [ 75 | "jupyter", 76 | "filebrowser", 77 | "terminal" 78 | ], 79 | "files": [ 80 | "lib/*.css", 81 | "lib/*.d.ts", 82 | "lib/*.js", 83 | "lib/**/*.css", 84 | "lib/**/*.d.ts", 85 | "lib/**/*.js" 86 | ], 87 | "author": "Project Jupyter", 88 | "license": "BSD-3-Clause", 89 | "bugs": { 90 | "url": "https://github.com/jupyter/jupyter-js-ui/issues" 91 | }, 92 | "homepage": "https://github.com/jupyter/jupyter-js-ui" 93 | } 94 | -------------------------------------------------------------------------------- /scripts/buildexamples.js: -------------------------------------------------------------------------------- 1 | var childProcess = require('child_process'); 2 | var fs = require('fs'); 3 | 4 | // Build all of the example folders. 5 | dirs = fs.readdirSync('examples'); 6 | 7 | var cmd; 8 | for (var i = 0; i < dirs.length; i++) { 9 | if (dirs[i].indexOf('.') !== -1) { 10 | continue; 11 | } 12 | if (dirs[i].indexOf('node_modules') !== -1) { 13 | continue; 14 | } 15 | console.log('Building: ' + dirs[i] + '...'); 16 | process.chdir('examples/' + dirs[i]); 17 | childProcess.execSync('npm run update', { stdio: [0, 1, 2] }); 18 | childProcess.execSync('npm run build', { stdio: [0, 1, 2] }); 19 | process.chdir('../..'); 20 | } 21 | -------------------------------------------------------------------------------- /scripts/cleanexamples.js: -------------------------------------------------------------------------------- 1 | var childProcess = require('child_process'); 2 | var fs = require('fs'); 3 | 4 | childProcess.execSync('rimraf examples/node_modules', { stdio: [0, 1, 2] }); 5 | 6 | // Clean all of the example folders. 7 | dirs = fs.readdirSync('examples'); 8 | 9 | for (var i = 0; i < dirs.length; i++) { 10 | if (dirs[i].indexOf('.') !== -1) { 11 | continue; 12 | } 13 | var cmd = 'rimraf examples/' + dirs[i] + '/build'; 14 | childProcess.execSync(cmd, { stdio: [0, 1, 2] }); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /scripts/copycss.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs-extra'); 2 | fs.copySync('src/', 'lib/', { filter: /\.css$/ }); 3 | -------------------------------------------------------------------------------- /scripts/travis_after_success.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [[ $TRAVIS_PULL_REQUEST == false && $TRAVIS_BRANCH == "master" ]] 3 | then 4 | echo "-- pushing docs --" 5 | 6 | ( cd docs 7 | git init 8 | git config user.email "travis@travis-ci.com" 9 | git config user.name "Travis Bot" 10 | 11 | git add . 12 | git commit -m "Deploy to GitHub Pages" 13 | git push --force --quiet "https://${GHTOKEN}@${GH_REF}" master:gh-pages > /dev/null 2>&1 14 | ) && echo "-- pushed docs --" 15 | else 16 | echo "-- will only push docs from master --" 17 | fi 18 | -------------------------------------------------------------------------------- /scripts/travis_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | npm install 3 | wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; 4 | bash miniconda.sh -b -p $HOME/miniconda 5 | export PATH="$HOME/miniconda/bin:$PATH" 6 | hash -r 7 | conda config --set always_yes yes --set changeps1 no 8 | conda update -q conda 9 | conda info -a 10 | conda install jupyter 11 | 12 | # create jupyter base dir (needed for config retreival) 13 | mkdir ~/.jupyter 14 | -------------------------------------------------------------------------------- /scripts/travis_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | export DISPLAY=:99.0 4 | sh -e /etc/init.d/xvfb start || true 5 | export PATH="$HOME/miniconda/bin:$PATH" 6 | 7 | npm run clean 8 | npm run build 9 | npm run clean:examples 10 | npm run build:examples 11 | 12 | npm run test 13 | npm run test:coverage 14 | 15 | npm run docs 16 | -------------------------------------------------------------------------------- /src/clipboard/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 'use strict'; 4 | 5 | /** 6 | * Copy text to the system clipboard. 7 | * 8 | * #### Notes 9 | * This can only be called in response to a user input event. 10 | */ 11 | export 12 | function copyToClipboard(text: string): void { 13 | let node = document.body; 14 | let handler = (event: ClipboardEvent) => { 15 | let data = event.clipboardData || (window as any).clipboardData; 16 | data.setData('text', text); 17 | event.preventDefault(); 18 | node.removeEventListener('copy', handler); 19 | }; 20 | node.addEventListener('copy', handler); 21 | generateClipboardEvent(node); 22 | } 23 | 24 | 25 | /** 26 | * Generate a clipboard event on a node. 27 | * 28 | * @param node - The element on which to generate the event. 29 | * 30 | * @param type - The type of event to generate: `'copy'` or `'cut'`. 31 | * `'paste'` events cannot be programmatically generated. 32 | * 33 | * #### Notes 34 | * This can only be called in response to a user input event. 35 | */ 36 | export 37 | function generateClipboardEvent(node: HTMLElement, type='copy'): void { 38 | // http://stackoverflow.com/a/5210367 39 | 40 | // Identify selected text. 41 | var sel = window.getSelection(); 42 | 43 | // Save the current selection. 44 | var savedRanges: any[] = []; 45 | for (var i = 0, len = sel.rangeCount; i < len; ++i) { 46 | savedRanges[i] = sel.getRangeAt(i).cloneRange(); 47 | } 48 | 49 | // Select the node content. 50 | var range = document.createRange(); 51 | range.selectNodeContents(node); 52 | sel.removeAllRanges(); 53 | sel.addRange(range); 54 | 55 | // Execute the command. 56 | document.execCommand(type); 57 | 58 | // Restore the previous selection. 59 | sel = window.getSelection(); 60 | sel.removeAllRanges(); 61 | for (var i = 0, len = savedRanges.length; i < len; ++i) { 62 | sel.addRange(savedRanges[i]); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/codemirror/codemirror-ipython.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 'use strict'; 4 | 5 | import * as CodeMirror 6 | from 'codemirror'; 7 | 8 | import 'codemirror/mode/meta'; 9 | import 'codemirror/mode/python/python'; 10 | 11 | 12 | /** 13 | * Define an IPython codemirror mode. 14 | * 15 | * It is a slightly altered Python Mode with a `?` operator. 16 | */ 17 | CodeMirror.defineMode('ipython', (config: CodeMirror.EditorConfiguration, modeOptions?: any) => { 18 | let pythonConf: any = {}; 19 | for (let prop in modeOptions) { 20 | if (modeOptions.hasOwnProperty(prop)) { 21 | pythonConf[prop] = modeOptions[prop]; 22 | } 23 | } 24 | pythonConf.name = 'python'; 25 | pythonConf.singleOperators = new RegExp('^[\\+\\-\\*/%&|\\^~<>!\\?]'); 26 | pythonConf.identifiers = new RegExp('^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*'); 27 | return CodeMirror.getMode(config, pythonConf); 28 | }); 29 | 30 | CodeMirror.defineMIME('text/x-ipython', 'ipython'); 31 | CodeMirror.modeInfo.push({ 32 | ext: [], 33 | mime: 'text/x-ipython', 34 | mode: 'ipython', 35 | name: 'ipython' 36 | }); 37 | -------------------------------------------------------------------------------- /src/codemirror/codemirror-ipythongfm.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 'use strict'; 4 | 5 | import * as CodeMirror 6 | from 'codemirror'; 7 | 8 | import 'codemirror/mode/stex/stex'; 9 | import 'codemirror/mode/gfm/gfm'; 10 | import 'codemirror/addon/mode/multiplex'; 11 | 12 | 13 | /** 14 | * Define an IPython GFM (GitHub Flavored Markdown) mode. 15 | * 16 | * Is just a slightly altered GFM Mode with support for latex. 17 | * Latex support was supported by Codemirror GFM as of 18 | * https://github.com/codemirror/CodeMirror/pull/567 19 | * But was later removed in 20 | * https://github.com/codemirror/CodeMirror/commit/d9c9f1b1ffe984aee41307f3e927f80d1f23590c 21 | */ 22 | CodeMirror.defineMode('ipythongfm', (config: CodeMirror.EditorConfiguration, modeOptions?: any) => { 23 | let gfm_mode = CodeMirror.getMode(config, 'gfm'); 24 | let tex_mode = CodeMirror.getMode(config, 'stex'); 25 | 26 | return CodeMirror.multiplexingMode( 27 | gfm_mode, 28 | { 29 | open: '$', close: '$', 30 | mode: tex_mode, 31 | delimStyle: 'delimit' 32 | }, 33 | { 34 | // not sure this works as $$ is interpreted at (opening $, closing $, as defined just above) 35 | open: '$$', close: '$$', 36 | mode: tex_mode, 37 | delimStyle: 'delimit' 38 | }, 39 | { 40 | open: '\\(', close: '\\)', 41 | mode: tex_mode, 42 | delimStyle: 'delimit' 43 | }, 44 | { 45 | open: '\\[', close: '\\]', 46 | mode: tex_mode, 47 | delimStyle: 'delimit' 48 | } 49 | // .. more multiplexed styles can follow here 50 | ); 51 | }); 52 | 53 | CodeMirror.defineMIME('text/x-ipythongfm', 'ipythongfm'); 54 | -------------------------------------------------------------------------------- /src/codemirror/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 'use strict'; 4 | 5 | import * as CodeMirror 6 | from 'codemirror'; 7 | 8 | import './codemirror-ipython'; 9 | import './codemirror-ipythongfm'; 10 | 11 | // Bundle other common modes 12 | import 'codemirror/mode/javascript/javascript'; 13 | import 'codemirror/mode/css/css'; 14 | import 'codemirror/mode/julia/julia'; 15 | import 'codemirror/mode/r/r'; 16 | import 'codemirror/mode/markdown/markdown'; 17 | 18 | 19 | /** 20 | * Load a codemirror mode by file name. 21 | */ 22 | export 23 | function loadModeByFileName(editor: CodeMirror.Editor, filename: string): void { 24 | loadInfo(editor, CodeMirror.findModeByFileName(filename)); 25 | } 26 | 27 | 28 | /** 29 | * Load a codemirror mode by mime type. 30 | */ 31 | export 32 | function loadModeByMIME(editor: CodeMirror.Editor, mimetype: string): void { 33 | loadInfo(editor, CodeMirror.findModeByMIME(mimetype)); 34 | } 35 | 36 | 37 | 38 | /** 39 | * Load a codemirror mode by mode name. 40 | */ 41 | export 42 | function loadModeByName(editor: CodeMirror.Editor, mode: string): void { 43 | loadInfo(editor, CodeMirror.findModeByName(mode)); 44 | } 45 | 46 | 47 | /** 48 | * Load a CodeMirror mode based on a mode spec. 49 | */ 50 | function loadInfo(editor: CodeMirror.Editor, info: CodeMirror.modespec): void { 51 | if (!info) { 52 | editor.setOption('mode', 'null'); 53 | return; 54 | } 55 | if (CodeMirror.modes.hasOwnProperty(info.mode)) { 56 | editor.setOption('mode', info.mime); 57 | } else { 58 | require([`codemirror/mode/${info.mode}/${info.mode}`], () => { 59 | editor.setOption('mode', info.mime); 60 | }); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/codemirror/widget.ts: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | | Copyright (c) 2014-2015, PhosphorJS Contributors 3 | | 4 | | Distributed under the terms of the BSD 3-Clause License. 5 | | 6 | | The full license is in the file LICENSE, distributed with this software. 7 | |----------------------------------------------------------------------------*/ 8 | 'use strict'; 9 | 10 | import * as CodeMirror 11 | from 'codemirror'; 12 | 13 | import 'codemirror/mode/meta'; 14 | 15 | import 'codemirror/lib/codemirror.css'; 16 | 17 | import { 18 | Message 19 | } from 'phosphor-messaging'; 20 | 21 | import { 22 | ResizeMessage, Widget 23 | } from 'phosphor-widget'; 24 | 25 | 26 | /** 27 | * The class name added to CodeMirrorWidget instances. 28 | */ 29 | const EDITOR_CLASS = 'jp-CodeMirrorWidget'; 30 | 31 | 32 | /** 33 | * A widget which hosts a CodeMirror editor. 34 | */ 35 | export 36 | class CodeMirrorWidget extends Widget { 37 | 38 | /** 39 | * Construct a CodeMirror widget. 40 | */ 41 | constructor(options?: CodeMirror.EditorConfiguration) { 42 | super(); 43 | this.addClass(EDITOR_CLASS); 44 | this._editor = CodeMirror(this.node, options); 45 | } 46 | 47 | /** 48 | * Dispose of the resources held by the widget. 49 | */ 50 | dispose(): void { 51 | this._editor = null; 52 | super.dispose(); 53 | } 54 | 55 | /** 56 | * Get the editor wrapped by the widget. 57 | * 58 | * #### Notes 59 | * This is a ready-only property. 60 | */ 61 | get editor(): CodeMirror.Editor { 62 | return this._editor; 63 | } 64 | 65 | /** 66 | * A message handler invoked on an `'after-attach'` message. 67 | */ 68 | protected onAfterAttach(msg: Message): void { 69 | if (!this.isVisible) { 70 | this._needsRefresh = true; 71 | return; 72 | } 73 | this._editor.refresh(); 74 | this._needsRefresh = false; 75 | } 76 | 77 | /** 78 | * A message handler invoked on an `'after-show'` message. 79 | */ 80 | protected onAfterShow(msg: Message): void { 81 | if (this._needsRefresh) { 82 | this._editor.refresh(); 83 | this._needsRefresh = false; 84 | } 85 | } 86 | 87 | /** 88 | * A message handler invoked on an `'resize'` message. 89 | */ 90 | protected onResize(msg: ResizeMessage): void { 91 | if (msg.width < 0 || msg.height < 0) { 92 | this._editor.refresh(); 93 | } else { 94 | this._editor.setSize(msg.width, msg.height); 95 | } 96 | this._needsRefresh = false; 97 | } 98 | 99 | private _editor: CodeMirror.Editor = null; 100 | private _needsRefresh = true; 101 | } 102 | -------------------------------------------------------------------------------- /src/dialog/index.css: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------- 2 | | Copyright (c) 2014-2016, Jupyter Development Team. 3 | | 4 | | Distributed under the terms of the Modified BSD License. 5 | |----------------------------------------------------------------------------*/ 6 | .jp-Dialog { 7 | position: absolute; 8 | z-index: 10000; 9 | display: flex; 10 | flex-direction: column; 11 | align-items: center; 12 | justify-content: center; 13 | top: 0px; 14 | left: 0px; 15 | margin: 0; 16 | padding: 0; 17 | width: 100%; 18 | height: 100%; 19 | } 20 | 21 | 22 | .jp-Dialog-content { 23 | margin-left: auto; 24 | margin-right: auto; 25 | } 26 | 27 | 28 | .jp-Dialog-header { 29 | 30 | } 31 | 32 | 33 | .jp-Dialog-footer { 34 | 35 | } 36 | 37 | 38 | .jp-Dialog-title { 39 | overflow: hidden; 40 | white-space: nowrap; 41 | text-overflow: ellipsis; 42 | } 43 | -------------------------------------------------------------------------------- /src/dialog/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 'use strict'; 4 | 5 | /** 6 | * The class name added to dialog instances. 7 | */ 8 | const DIALOG_CLASS = 'jp-Dialog'; 9 | 10 | /** 11 | * The class name added to dialog content node. 12 | */ 13 | const CONTENT_CLASS = 'jp-Dialog-content'; 14 | 15 | /** 16 | * The class name added to dialog header node. 17 | */ 18 | const HEADER_CLASS = 'jp-Dialog-header'; 19 | 20 | /** 21 | * The class name added to dialog title node. 22 | */ 23 | const TITLE_CLASS = 'jp-Dialog-title'; 24 | 25 | /** 26 | * The class name added to dialog body node. 27 | */ 28 | const BODY_CLASS = 'jp-Dialog-body'; 29 | 30 | /** 31 | * The class name added to a dialog body content node. 32 | */ 33 | const BODY_CONTENT_CLASS = 'jp-Dialog-bodyContent'; 34 | 35 | /** 36 | * The class name added to a dialog content node. 37 | */ 38 | const FOOTER_CLASS = 'jp-Dialog-footer'; 39 | 40 | /** 41 | * The class name added to a dialog button node. 42 | */ 43 | const BUTTON_CLASS = 'jp-Dialog-button'; 44 | 45 | /** 46 | * The class name added to a dialog button icon node. 47 | */ 48 | const BUTTON_ICON_CLASS = 'jp-Dialog-buttonIcon'; 49 | 50 | /** 51 | * The class name added to a dialog button text node. 52 | */ 53 | const BUTTON_TEXT_CLASS = 'jp-Dialog-buttonText'; 54 | 55 | /* 56 | * The class name added to dialog Confirm buttons. 57 | */ 58 | const OK_BUTTON_CLASS = 'jp-Dialog-okButton'; 59 | 60 | /** 61 | * The class name added to dialog Cancel buttons. 62 | */ 63 | const CANCEL_BUTTON_CLASS = 'jp-Dialog-cancelButton'; 64 | 65 | /** 66 | * The class name added to dialog input field wrappers. 67 | */ 68 | const INPUT_WRAPPER_CLASS = 'jp-Dialog-inputWrapper'; 69 | 70 | /** 71 | * The class name added to dialog input fields. 72 | */ 73 | const INPUT_CLASS = 'jp-Dialog-input'; 74 | 75 | /** 76 | * The class name added to dialog select wrappers. 77 | */ 78 | const SELECT_WRAPPER_CLASS = 'jp-Dialog-selectWrapper'; 79 | 80 | /** 81 | * The class anem added to dialog select nodes. 82 | */ 83 | const SELECT_CLASS = 'jp-Dialog-select'; 84 | 85 | 86 | /** 87 | * A button applied to a dialog. 88 | */ 89 | export 90 | interface IButtonItem { 91 | /** 92 | * The text for the button. 93 | */ 94 | text: string; 95 | 96 | /** 97 | * The icon class for the button. 98 | */ 99 | icon?: string; 100 | 101 | /** 102 | * The extra class name to associate with the button. 103 | */ 104 | className?: string; 105 | } 106 | 107 | 108 | /** 109 | * A default confirmation button. 110 | */ 111 | export 112 | const okButton: IButtonItem = { 113 | text: 'OK', 114 | className: OK_BUTTON_CLASS 115 | }; 116 | 117 | 118 | /** 119 | * A default cancel button. 120 | */ 121 | export 122 | const cancelButton: IButtonItem = { 123 | text: 'CANCEL', 124 | className: CANCEL_BUTTON_CLASS 125 | }; 126 | 127 | 128 | /** 129 | * The options used to create a dialog. 130 | */ 131 | export 132 | interface IDialogOptions { 133 | /** 134 | * The tope level text for the dialog (defaults to an empty string). 135 | */ 136 | title?: string; 137 | 138 | /** 139 | * The main body element for the dialog or a message to display. 140 | * 141 | * #### Notes 142 | * If a `string` is provided, it will be used as the `HTMLContent` of 143 | * a ``. If an `` or `