diff --git a/.gitignore b/.gitignore index a5199a571..c66028864 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ *.pyc -*.egg-info \ No newline at end of file +*.egg-info +*node_modules +.history +settings.json +jupyter_geppetto/*.js +jupyter_geppetto/*.js.map +*.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index 21c8925b8..2df68e5c3 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,177 @@

- Geppetto logo + Geppetto logo

# Geppetto Jupyter Notebook Extension -This is an experimental repo for a Jupyter notebook extension. This extension extends Jupyter Python server based on tornado that allows the client to establish a websocket connection and server static resources. +This is an experimental repo for a Jupyter notebook [extension](https://ipython.readthedocs.io/en/stable/config/extensions/). +This extension enables the Jupyter Python server based +on tornado that allows the client to establish a websocket connection and serve static resources. -How to install: +Other than serving the application as a Geppetto backend, Jupyter Geppetto allows, by embedding a notebook in the page, +to interact with the notebook and synchronize frontend features with the notebook. + +## How to install + +Before installing, it is recommended to activate a Python 3 virtual environment: +```bash +python3 -m venv jupyter-geppetto +source jupyter-geppetto/bin/activate +``` + +Or, with conda + +```bash +conda create -n jupyter-geppetto python=3.7 +conda activate jupyter-geppetto ``` + +Install with pip +```bash pip install jupyter_geppetto jupyter nbextension enable --py --sys-prefix jupyter_geppetto ``` -How to install extension from sources: -``` +Install extension from sources +```bash git clone --recursive https://github.com/openworm/org.geppetto.frontend.jupyter.git pip install . -jupyter nbextension install --py jupyter_geppetto -jupyter nbextension enable --py jupyter_geppetto -``` -To overwrite the local install: + +jupyter nbextension install --py --symlink --sys-prefix jupyter_geppetto +jupyter nbextension enable --py --sys-prefix jupyter_geppetto +jupyter serverextension enable --py --sys-prefix jupyter_geppetto ``` +## Overwrite the local install +```bash pip install . --upgrade --no-deps --force-reinstall ``` +Notice: old installs made without --sys-prefix (so ) + +## How to run +After the extension is installed the Jupyter notebook must be run with the parameter --library, which specifies the +Python libraries to be loaded together with the extension: +```bash +exec jupyter notebook --NotebookApp.default_url=/geppetto --NotebookApp.token='' --library=my_geppetto_application_lib +``` To connect go to the URL: http://localhost:8888/geppetto -assuming the default Jupyter configuration, otherwise change port accordingly. +assuming the default Jupyter configuration, otherwise change the port accordingly. Note if you get a 404 and you have a custom configuration of Jupyter notebook you will have to add the following to your jupyter_notebook_config.py: ``` c.NotebookApp.nbserver_extensions = {'jupyter_geppetto':True} ``` + + +# Development + +Jupyter Geppetto serves In order to use the synchronization capability, + +## Geppetto websocket api +Jupyter Geppetto implements the websocket api through a Tornado handler. It supports the messages coming from +a [geppetto-application](https://github.com/openworm/geppetto-application) frontend. See +[geppetto-client](https://github.com/openworm/geppetto-client) for the available messages and implementation. + +Currently, only a subset of the messages is supported. For further information, see the current release of +[pygeppetto](https://github.com/openworm/pygeppetto). + +## Web api +Allows to add own custom routes and handlers from inside the application. +The dependencies must be added from within the libraries specified in the parameter --library + +In the file root of your module (__init__.py of your package), call the RouteManager methods to add your custom routes: +```python +from jupyter_geppetto.webapi import RouteManager + +RouteManager.add_controller(MyController) # Add a controller (preferred way for http requests) +RouteManager.add_route('/my/path', MyTornadoHandler) # for more custom control +``` + +A controller class is a standard class with webapi annotations on methods. +Methods return the response as a string. +The RouteManager creates a handler for each method and binds the method with the handler. + +Example: +```Python +from jupyter_geppetto.webapi import get, post + +class MyController: + + @get('/my/simple/path') + def simple_action(handler): + return "My response payload" # Simple text response + + @get('/myresource/(.*)') + def action_with_url_param(handler, param): + import json + return json.dumps({'param':param}) # JSON response + + @get('/myresource') + def action_with_query_string_params(handler, param1, param2=None): + '''Handles + /myresource?param1=something¶m2=somethingelse + /myresource?param1=something + ''' + return ... + + @get('/myresource1') + def alt_action_with_query_string_params(handler, **kwargs): + '''Handles any query string parameter''' + return ... + + @get('/myresource1/(.*)') + def action_with_url_and_query_string_params(handler, param, param1, param2): + '''Handles /myresource?param1=comething¶m2=somethingelse''' + return ... + + @post('/myresource2') + def simple_post_action(handler, payload, param1, param2): + '''Handles /myresource and body param1=something¶m2=somethingelse''' + return ... + + @get('/myresource3', {'Content-type': 'image/png', 'Cache-Control': 'max-age=600'}) + def get_action_with_headers(handler): + '''Sets headers''' + return ... + + @get('/myresource3') + def get_action_use_handler(handler): + '''The first parameter is the tornado handler''' + handler.write('Whatever') + return ... +``` + +## Iframe embedding +In the frontend, it is required to embed an iframe with a jupyter notebook. + +Example +```javascript +import PythonConsole from 'geppetto-client/js/components/interface/pythonConsole/PythonConsole'; + +// Inside your React component render: + +``` + +## Synchronization +This feature allows to synchronize backend and frontend objects. In order to use synchronization, it is required to have +the iframe embed enabled and initialized. +It's implemented through a [IPython/Jupyter widget](https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Basics.html). +On the JS side, the connection is achieved through the PythonControlledCapability of the [Geppetto client](https://github.com/openworm/geppetto-client/). + + +Usage +```python +from jupyter_geppetto import synchronization +class MyClass: + def a(self): + ... + def b(self): + ... +synchronization.startSynchronization(MyClass().__dict__) + +``` + +## Deployment +An application based on Jupyter Geppetto can be deployed as a standard jupyter notebook application. +It is recommended a dockerized setup with JupyterHub (e.g. on Kubernetes). + diff --git a/__init__.py b/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/js/package-lock.json b/js/package-lock.json index 4a3bc6f4f..b9d9dc010 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "org.geppetto.frontend.jupyter", - "version": "0.4.1", + "version": "0.4.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -25,7 +25,7 @@ "@jupyterlab/coreutils": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@jupyterlab/coreutils/-/coreutils-2.2.1.tgz", - "integrity": "sha512-XkGMBXqEAnENC4fK/L3uEqqxyNUtf4TI/1XNDln7d5VOPHQJSBTbYlBAZ0AQotn2qbs4WqmV6icxje2ITVedqQ==", + "integrity": "sha1-wnHq8vbkaHV7qWYPJL08Pl1v5YM=", "dev": true, "requires": { "@phosphor/algorithm": "^1.1.2", @@ -43,7 +43,7 @@ "@jupyterlab/observables": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@jupyterlab/observables/-/observables-2.1.1.tgz", - "integrity": "sha512-PzmJ/jF5fWzHCvjPAWBi3YjtHRAF0bwyjpd8W8aJt64TzhEZh0se8xCNGOURzD+8TxOsTF9JpQ9wIDBr4V226Q==", + "integrity": "sha1-xditKVxbC86RSmB6NCsK9FULIYc=", "dev": true, "requires": { "@phosphor/algorithm": "^1.1.2", @@ -56,7 +56,7 @@ "@jupyterlab/services": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@jupyterlab/services/-/services-3.2.1.tgz", - "integrity": "sha512-zCMruGGYxTe427ESK4YUO1V/liFOFYpebYPRsJ+j9CFdV+Hm760+nx4pFX6N6Z9TbS+5cs8BgZ+ebm8unRZrJg==", + "integrity": "sha1-6Nkyntc/eUkJ94bSLF6UsHvrBBs=", "dev": true, "requires": { "@jupyterlab/coreutils": "^2.2.1", @@ -85,7 +85,7 @@ "@phosphor/commands": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/@phosphor/commands/-/commands-1.6.1.tgz", - "integrity": "sha512-iRgn7QX64e0VwZ91KFo964a/LVpw9XtiYIYtpymEyKY757NXvx6ZZMt1CqKfntoDcSZJeVte4eV8jJWhZoVlDA==", + "integrity": "sha1-b2DCo7dZMWzRNjtCbfO0A2uyx/0=", "dev": true, "requires": { "@phosphor/algorithm": "^1.1.2", @@ -170,7 +170,7 @@ "@phosphor/widgets": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.6.0.tgz", - "integrity": "sha512-HqVckVF8rJ15ss0Zf/q0AJ69ZKNFOO26qtNKAdGZ9SmmkSMf73X6pB0R3Fj5+Y4Sjl8ezIIKG6mXj+DxOofnwA==", + "integrity": "sha1-67qACLaxMkfQPnPl84cskNLJx48=", "dev": true, "requires": { "@phosphor/algorithm": "^1.1.2", @@ -214,7 +214,7 @@ "@types/sizzle": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", - "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==", + "integrity": "sha1-qBG4wY4rq6t9VCszZYh64uTZ3kc=", "dev": true }, "@types/underscore": { @@ -236,7 +236,7 @@ "acorn": { "version": "5.7.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "integrity": "sha1-Z6ojG/iBKXS4UjWpZ3Hra9B+onk=", "dev": true }, "acorn-dynamic-import": { @@ -311,7 +311,7 @@ "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "integrity": "sha1-vLJLTzeTTZqnrBe0ra+J58du8us=", "dev": true, "requires": { "micromatch": "^3.1.4", @@ -321,7 +321,7 @@ "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", "dev": true, "requires": { "sprintf-js": "~1.0.2" @@ -336,7 +336,7 @@ "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", "dev": true }, "arr-union": { @@ -392,7 +392,7 @@ "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", "dev": true, "optional": true, "requires": { @@ -402,7 +402,7 @@ "asn1.js": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "integrity": "sha1-ucK/WAXx5kqt7tbfOiv6+1pz9aA=", "dev": true, "requires": { "bn.js": "^4.0.0", @@ -474,7 +474,7 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", "dev": true }, "autoprefixer": { @@ -501,7 +501,7 @@ "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "integrity": "sha1-8OAD2cqef1nHpQiUXXsu+aBKVC8=", "dev": true, "optional": true }, @@ -534,7 +534,7 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", "dev": true, "requires": { "cache-base": "^1.0.1", @@ -558,7 +558,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -567,7 +567,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -576,7 +576,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -587,7 +587,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -595,7 +595,7 @@ "base64-js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "integrity": "sha1-yrHmEY8FEJXli1KBrqjBzSK/wOM=", "dev": true }, "batch": { @@ -629,7 +629,7 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "integrity": "sha1-LN4J617jQfSEdGuwMJsyU7GxRC8=", "dev": true }, "body-parser": { @@ -669,6 +669,7 @@ "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "dev": true, + "optional": true, "requires": { "hoek": "2.x.x" } @@ -676,7 +677,7 @@ "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -686,7 +687,7 @@ "braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", "dev": true, "requires": { "arr-flatten": "^1.1.0", @@ -735,7 +736,7 @@ "browserify-cipher": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "integrity": "sha1-jWR0wbhwv9q807z8wZNKEOlPFfA=", "dev": true, "requires": { "browserify-aes": "^1.0.4", @@ -746,7 +747,7 @@ "browserify-des": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "integrity": "sha1-OvTx9Zg5QDVy8cZiBDdfen9wPpw=", "dev": true, "requires": { "cipher-base": "^1.0.1", @@ -783,7 +784,7 @@ "browserify-zlib": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=", "dev": true, "requires": { "pako": "~1.0.5" @@ -813,7 +814,7 @@ "buffer-indexof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "integrity": "sha1-Uvq8xqYG0aADAoAmSO9o9jnaJow=", "dev": true }, "buffer-xor": { @@ -843,7 +844,7 @@ "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", "dev": true, "requires": { "collection-visit": "^1.0.0", @@ -943,7 +944,7 @@ "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=", "dev": true, "requires": { "inherits": "^2.0.1", @@ -953,7 +954,7 @@ "clap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", - "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", + "integrity": "sha1-TzZ0WzIAhJJVf0ZBLWbVDLmbzlE=", "dev": true, "requires": { "chalk": "^1.1.3" @@ -962,7 +963,7 @@ "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", "dev": true, "requires": { "arr-union": "^3.1.0", @@ -1052,7 +1053,7 @@ "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "integrity": "sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=", "dev": true, "requires": { "color-name": "1.1.3" @@ -1093,8 +1094,9 @@ "combined-stream": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "integrity": "sha1-LR0kMXr7ir6V1tLAsHtXgTU52Cg=", "dev": true, + "optional": true, "requires": { "delayed-stream": "~1.0.0" } @@ -1126,7 +1128,7 @@ "compression": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", - "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", + "integrity": "sha1-J+DhdqryYPfywoE8PkQK258Zk9s=", "dev": true, "requires": { "accepts": "~1.3.5", @@ -1174,7 +1176,7 @@ "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", "dev": true }, "cookie": { @@ -1204,7 +1206,7 @@ "create-ecdh": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "integrity": "sha1-yREbbzMEXEaX8UR4f5JUzcd8Rf8=", "dev": true, "requires": { "bn.js": "^4.1.0", @@ -1262,7 +1264,7 @@ "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "integrity": "sha1-OWz58xN/A+S45TLFj2mCVOAPgOw=", "dev": true, "requires": { "browserify-cipher": "^1.0.0", @@ -1287,7 +1289,7 @@ "css-loader": { "version": "0.28.7", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.7.tgz", - "integrity": "sha512-GxMpax8a/VgcfRrVy0gXD6yLd5ePYbXX/5zGgTVYp4wXtJklS8Z2VaUArJgc//f6/Dzil7BaJObdSv8eKKCPgg==", + "integrity": "sha1-Xy7pid0y7dkHcX+VMxdlYWCZnBs=", "dev": true, "requires": { "babel-code-frame": "^6.11.0", @@ -1410,7 +1412,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "dev": true, "requires": { "ms": "2.0.0" @@ -1437,7 +1439,7 @@ "default-gateway": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz", - "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==", + "integrity": "sha1-t+8znl4CSwRUZ69APVA0jbRkLQ8=", "dev": true, "requires": { "execa": "^0.10.0", @@ -1447,7 +1449,7 @@ "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "integrity": "sha1-Sl7Hxk364iw6FBJNus3uhG2Ay8Q=", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -1460,7 +1462,7 @@ "execa": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "integrity": "sha1-/0Vqj1P5D47MxxqW0Rvfx/CCy1A=", "dev": true, "requires": { "cross-spawn": "^6.0.0", @@ -1477,7 +1479,7 @@ "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=", "dev": true, "requires": { "object-keys": "^1.0.12" @@ -1486,7 +1488,7 @@ "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", "dev": true, "requires": { "is-descriptor": "^1.0.2", @@ -1496,7 +1498,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -1505,7 +1507,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -1514,7 +1516,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -1525,7 +1527,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -1562,7 +1564,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "dev": true, + "optional": true }, "depd": { "version": "1.1.2", @@ -1589,7 +1592,7 @@ "detect-node": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "integrity": "sha1-AU7o+PZpxcWAI9pkuBecCDooxGw=", "dev": true }, "diffie-hellman": { @@ -1612,7 +1615,7 @@ "dns-packet": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "integrity": "sha1-EqpCaYEHW+UAuRDu3NC0fdfe2lo=", "dev": true, "requires": { "ip": "^1.1.0", @@ -1631,7 +1634,7 @@ "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "integrity": "sha1-PTH1AZGmdJ3RN1p/Ui6CPULlTto=", "dev": true }, "ecc-jsbn": { @@ -1660,7 +1663,7 @@ "elliptic": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", - "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "integrity": "sha1-wtC3d2kRuGcixjLDwGxg8vgZk5o=", "dev": true, "requires": { "bn.js": "^4.4.0", @@ -1699,7 +1702,7 @@ "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "integrity": "sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg=", "dev": true, "requires": { "prr": "~1.0.1" @@ -1708,7 +1711,7 @@ "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", "dev": true, "requires": { "is-arrayish": "^0.2.1" @@ -1730,7 +1733,7 @@ "es-to-primitive": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "integrity": "sha1-7fckeAM0VujdqO8J4ArZZQcH83c=", "dev": true, "requires": { "is-callable": "^1.1.4", @@ -1842,7 +1845,7 @@ "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", "dev": true, "requires": { "estraverse": "^4.1.0" @@ -1879,7 +1882,7 @@ "eventemitter3": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=", "dev": true }, "events": { @@ -1900,7 +1903,7 @@ "evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=", "dev": true, "requires": { "md5.js": "^1.3.4", @@ -1969,7 +1972,7 @@ "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=", "dev": true, "requires": { "is-number": "^2.1.0", @@ -2054,7 +2057,7 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=", "dev": true, "optional": true }, @@ -2071,7 +2074,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -2082,7 +2085,7 @@ "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", "dev": true, "requires": { "array-unique": "^0.3.2", @@ -2116,7 +2119,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -2125,7 +2128,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -2134,7 +2137,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -2145,7 +2148,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -2154,7 +2157,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "dev": true, + "optional": true }, "fastparse": { "version": "1.1.1", @@ -2747,7 +2751,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -2847,25 +2852,27 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", "dev": true }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=", "dev": true }, "get-stream": { @@ -2902,7 +2909,7 @@ "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "integrity": "sha1-OWCDLT8VdBCDQtr9OmezMsCWnfE=", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -3029,7 +3036,7 @@ "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", "dev": true, "requires": { "function-bind": "^1.1.1" @@ -3136,12 +3143,13 @@ "version": "2.16.3", "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true + "dev": true, + "optional": true }, "hosted-git-info": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "integrity": "sha1-l/I2l3vW4SVAiTD/bePuxigewEc=", "dev": true }, "hpack.js": { @@ -3159,7 +3167,7 @@ "html-comment-regex": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "integrity": "sha1-l9RoiutcgYhqNk+qDK0d2hTUM6c=", "dev": true }, "html-entities": { @@ -3195,7 +3203,7 @@ "http-proxy": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "integrity": "sha1-etOElGWPhGBeL220Q230EPTlvpo=", "dev": true, "requires": { "eventemitter3": "^3.0.0", @@ -3358,7 +3366,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -3384,7 +3392,7 @@ "postcss": { "version": "6.0.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", "dev": true, "requires": { "chalk": "^2.4.1", @@ -3395,13 +3403,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -3412,7 +3420,7 @@ "ieee754": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", - "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "integrity": "sha1-UL8k5bnIu5ivSWTJQc2wkY2ntgs=", "dev": true }, "image-size": { @@ -3453,7 +3461,7 @@ "internal-ip": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-2.0.3.tgz", - "integrity": "sha512-XxJMiJOjXbb9LlwH6SVTsnUPymYACunXzKg3dqU+HIC+xYIkUhMyTiT/H6xxPmhlE4zHq50lKlx0CZlyN2C76Q==", + "integrity": "sha1-7Tz5tnGsf/IwN7+srULrQ5zZVGw=", "dev": true, "requires": { "default-gateway": "^2.2.2", @@ -3523,7 +3531,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", "dev": true }, "is-builtin-module": { @@ -3538,7 +3546,7 @@ "is-callable": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "integrity": "sha1-HhrfIZ4e62hNaR+dagX/DTCiTXU=", "dev": true }, "is-data-descriptor": { @@ -3559,7 +3567,7 @@ "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", @@ -3570,7 +3578,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", "dev": true } } @@ -3638,7 +3646,7 @@ "is-path-in-cwd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "integrity": "sha1-WsSLNF72dTOb1sekipEhELJBz1I=", "dev": true, "requires": { "is-path-inside": "^1.0.0" @@ -3662,7 +3670,7 @@ "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", "dev": true, "requires": { "isobject": "^3.0.1" @@ -3707,7 +3715,7 @@ "is-symbol": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "integrity": "sha1-oFX2rlcZLK7jKeeoYBGLSXqVDzg=", "dev": true, "requires": { "has-symbols": "^1.0.0" @@ -3729,7 +3737,7 @@ "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", "dev": true }, "is-wsl": { @@ -3766,7 +3774,7 @@ "jquery": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", - "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==", + "integrity": "sha1-lYzinoHJeQ8xvneS311NlfxX+8o=", "dev": true }, "js-base64": { @@ -3807,7 +3815,7 @@ "json-loader": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", + "integrity": "sha1-3KFKcCNf+C8KyaOr62DTN6NlGF0=", "dev": true }, "json-parser": { @@ -3915,7 +3923,7 @@ "less": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz", - "integrity": "sha512-KPdIJKWcEAb02TuJtaLrhue0krtRLoRoo7x6BNJIBelO00t/CCdJQUnHW5V34OnHMWzIktSalJxRO+FvytQlCQ==", + "integrity": "sha1-zBJg9RyQCp7A2R+2mYE54CUHtjs=", "dev": true, "requires": { "errno": "^0.1.1", @@ -3981,7 +3989,7 @@ "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40=", "dev": true }, "lodash.camelcase": { @@ -4060,7 +4068,7 @@ "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "integrity": "sha1-tdB7jjIW4+J81yjXL3DR5qNCAF8=", "dev": true, "requires": { "hash-base": "^3.0.0", @@ -4108,7 +4116,7 @@ "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -4129,7 +4137,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -4137,7 +4145,7 @@ "miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=", "dev": true, "requires": { "bn.js": "^4.0.0", @@ -4168,13 +4176,13 @@ "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=", "dev": true }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "integrity": "sha1-LhlN4ERibUoQ5/f7wAznPoPk1cc=", "dev": true }, "minimalistic-crypto-utils": { @@ -4186,7 +4194,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -4201,7 +4209,7 @@ "mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "integrity": "sha1-pJ5yaNzhoNlpjkUybFYm3zVD0P4=", "dev": true, "requires": { "for-in": "^1.0.2", @@ -4211,7 +4219,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -4251,7 +4259,7 @@ "multicast-dns": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "integrity": "sha1-oOx72QVcQoL3kMPIL04o2zsxsik=", "dev": true, "requires": { "dns-packet": "^1.3.1", @@ -4274,7 +4282,7 @@ "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -4293,7 +4301,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -4319,13 +4327,13 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=", "dev": true }, "node-forge": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", - "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", + "integrity": "sha1-bBUsNFzhHFL0ZcKr2VfoY5zWdN8=", "dev": true }, "node-libs-browser": { @@ -4491,7 +4499,7 @@ "obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "integrity": "sha1-Cb6jND1BhZ69RGKS0RydTbYZCE4=", "dev": true }, "on-finished": { @@ -4521,7 +4529,7 @@ "opn": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", - "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==", + "integrity": "sha1-y1Reeqt4VivrEao7+rxwQuF2EDU=", "dev": true, "requires": { "is-wsl": "^1.1.0" @@ -4530,7 +4538,7 @@ "original": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "integrity": "sha1-5EKmHP/hxf0gpl8yYcJmY7MD8l8=", "dev": true, "requires": { "url-parse": "^1.4.3" @@ -4545,7 +4553,7 @@ "os-locale": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=", "dev": true, "requires": { "execa": "^0.7.0", @@ -4562,7 +4570,7 @@ "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "integrity": "sha1-uGvV8MJWkJEcdZD8v8IBDVSzzLg=", "dev": true, "requires": { "p-try": "^1.0.0" @@ -4580,7 +4588,7 @@ "p-map": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "integrity": "sha1-5OlPMR6rvIYzoeeZCBZfyiYkG2s=", "dev": true }, "p-try": { @@ -4718,7 +4726,7 @@ "pbkdf2": { "version": "3.0.17", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "integrity": "sha1-l2wgZTBhexTrsyEUI597CTNuk6Y=", "dev": true, "requires": { "create-hash": "^1.1.2", @@ -4784,7 +4792,7 @@ "postcss": { "version": "5.2.18", "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", "dev": true, "requires": { "chalk": "^1.1.3", @@ -4891,7 +4899,7 @@ "postcss-filter-plugins": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz", - "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==", + "integrity": "sha1-giRf34IzcEFkXkdxFNjlk6oYuOw=", "dev": true, "requires": { "postcss": "^5.0.4" @@ -5057,7 +5065,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -5083,7 +5091,7 @@ "postcss": { "version": "6.0.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", "dev": true, "requires": { "chalk": "^2.4.1", @@ -5094,13 +5102,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -5121,7 +5129,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -5147,7 +5155,7 @@ "postcss": { "version": "6.0.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", "dev": true, "requires": { "chalk": "^2.4.1", @@ -5158,13 +5166,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -5185,7 +5193,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -5211,7 +5219,7 @@ "postcss": { "version": "6.0.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", "dev": true, "requires": { "chalk": "^2.4.1", @@ -5222,13 +5230,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -5369,13 +5377,13 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=", "dev": true }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=", "dev": true, "optional": true, "requires": { @@ -5385,7 +5393,7 @@ "proxy-addr": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "integrity": "sha1-7PxzO/Iv+Mb0B/onUye5q2fki5M=", "dev": true, "requires": { "forwarded": "~0.1.2", @@ -5407,7 +5415,7 @@ "public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "integrity": "sha1-T8ydd6B+SLp1J+fL4N4z0HATMeA=", "dev": true, "requires": { "bn.js": "^4.1.0", @@ -5501,7 +5509,7 @@ "randomfill": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "integrity": "sha1-ySGW/IarQr6YPxvzF3giSTHWFFg=", "dev": true, "requires": { "randombytes": "^2.0.5", @@ -5591,7 +5599,7 @@ "readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "integrity": "sha1-DodiKjMlqjPokihcr4tOhGUppSU=", "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -5638,13 +5646,13 @@ "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "integrity": "sha1-SoVuxLVuQHfFV1icroXnpMiGmhE=", "dev": true }, "regex-cache": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", "dev": true, "requires": { "is-equal-shallow": "^0.1.3" @@ -5653,7 +5661,7 @@ "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", "dev": true, "requires": { "extend-shallow": "^3.0.2", @@ -5695,7 +5703,7 @@ "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "integrity": "sha1-eC4NglwMWjuzlzH4Tv7mt0Lmsc4=", "dev": true }, "repeat-string": { @@ -5778,7 +5786,7 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", "dev": true }, "right-align": { @@ -5802,7 +5810,7 @@ "ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "integrity": "sha1-ocGm9iR1FXe6XQeRTLyShQWFiQw=", "dev": true, "requires": { "hash-base": "^3.0.0", @@ -5812,7 +5820,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", "dev": true }, "safe-regex": { @@ -5827,13 +5835,14 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", + "dev": true, + "optional": true }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=", "dev": true }, "select-hose": { @@ -5860,7 +5869,7 @@ "send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "integrity": "sha1-bsyh4PjBVtFBWXVZhI32RzCmu8E=", "dev": true, "requires": { "debug": "2.6.9", @@ -5896,7 +5905,7 @@ "serve-static": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "integrity": "sha1-CV6Ecv1bRiN9tQzkhqQ/S4bGzsE=", "dev": true, "requires": { "encodeurl": "~1.0.2", @@ -5914,7 +5923,7 @@ "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -5943,7 +5952,7 @@ "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=", "dev": true }, "sha.js": { @@ -5980,7 +5989,7 @@ "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", "dev": true, "requires": { "base": "^0.11.1", @@ -6016,7 +6025,7 @@ "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", "dev": true, "requires": { "define-property": "^1.0.0", @@ -6036,7 +6045,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -6045,7 +6054,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -6054,7 +6063,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -6065,7 +6074,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true } } @@ -6073,7 +6082,7 @@ "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", "dev": true, "requires": { "kind-of": "^3.2.0" @@ -6148,7 +6157,7 @@ "source-map-resolve": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "integrity": "sha1-cuLMNAlVQ+Q7LGKyxMENSpBU8lk=", "dev": true, "requires": { "atob": "^2.1.1", @@ -6177,13 +6186,13 @@ "spdx-exceptions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "integrity": "sha1-LqRQrudPKom/uUUZwH/Nb0EyKXc=", "dev": true }, "spdx-expression-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=", "dev": true, "requires": { "spdx-exceptions": "^2.1.0", @@ -6228,7 +6237,7 @@ "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", "dev": true, "requires": { "extend-shallow": "^3.0.0" @@ -6291,7 +6300,7 @@ "statuses": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "integrity": "sha1-u3PURtonlhBu/MG2AaJT1sRr0Ic=", "dev": true }, "stream-browserify": { @@ -6307,7 +6316,7 @@ "stream-http": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "integrity": "sha1-stJCRpKIpaJ+xP6JM6z2I95lFPw=", "dev": true, "requires": { "builtin-status-codes": "^3.0.0", @@ -6326,7 +6335,7 @@ "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", @@ -6359,7 +6368,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -6368,7 +6377,7 @@ "stringstream": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", - "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", + "integrity": "sha1-eIAiWw1K0Q4wkn0Weh1vL9OzOnI=", "dev": true, "optional": true }, @@ -6447,7 +6456,7 @@ "timers-browserify": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", - "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "integrity": "sha1-HSjj0qrfHVpZlsTp+VYBzQU0gK4=", "dev": true, "requires": { "setimmediate": "^1.0.4" @@ -6471,7 +6480,7 @@ "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", "dev": true, "requires": { "define-property": "^2.0.2", @@ -6493,7 +6502,7 @@ "tough-cookie": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "integrity": "sha1-7GDO44rGdQY//JelwYlwV47oNlU=", "dev": true, "optional": true, "requires": { @@ -6526,7 +6535,7 @@ "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=", "dev": true, "requires": { "media-typer": "0.3.0", @@ -6579,7 +6588,7 @@ "underscore": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==", + "integrity": "sha1-BtzjSg5op7q8KbNluOdLiSUgOWE=", "dev": true }, "union-value": { @@ -6718,7 +6727,7 @@ "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=", "dev": true }, "util": { @@ -6751,7 +6760,7 @@ "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", "dev": true, "requires": { "spdx-correct": "^3.0.0", @@ -6767,7 +6776,7 @@ "vendors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz", - "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==", + "integrity": "sha1-f8te759WI7FWvOqJ7DfWNnbyGAE=", "dev": true }, "verror": { @@ -6803,7 +6812,7 @@ "watchpack": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "integrity": "sha1-S8EsLr6KonenHx0/FNaFx7RGzQA=", "dev": true, "requires": { "chokidar": "^2.0.2", @@ -6814,7 +6823,7 @@ "wbuf": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "integrity": "sha1-wdjRSTFtPqhShIiVy2oL/oh7h98=", "dev": true, "requires": { "minimalistic-assert": "^1.0.0" @@ -6823,7 +6832,7 @@ "webpack": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.6.0.tgz", - "integrity": "sha512-OsHT3D0W0KmPPh60tC7asNnOmST6bKTiR90UyEdT9QYoaJ4OYN4Gg7WK1k3VxHK07ZoiYWPsKvlS/gAjwL/vRA==", + "integrity": "sha1-qJqSn77iBdNaT6LMSHvpy+yImLw=", "dev": true, "requires": { "acorn": "^5.0.0", @@ -6853,7 +6862,7 @@ "webpack-dev-middleware": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", - "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", + "integrity": "sha1-+PwRIM47T8VoDO7LQ9d3lmshEF4=", "dev": true, "requires": { "memory-fs": "~0.4.1", @@ -6874,7 +6883,7 @@ "webpack-dev-server": { "version": "2.8.2", "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.8.2.tgz", - "integrity": "sha512-wD9bs+Z1uwvf3Jc+8ZkyMI0Xi+aJJYjC2UZplOWoo/vStelK5Mv62X2uXYEYIQEjy9wJQMzC0fEFqQsg7vVEIg==", + "integrity": "sha1-q9YfQQd4zEyEPXzrv0FGWxq3c0w=", "dev": true, "requires": { "ansi-html": "0.0.7", @@ -6906,7 +6915,7 @@ "anymatch": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "integrity": "sha1-VT3Lj5HjyImEXf26NMd3IbkLnXo=", "dev": true, "requires": { "micromatch": "^2.1.5", @@ -7170,7 +7179,7 @@ "webpack-sources": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", - "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", + "integrity": "sha1-KijcufH0X+lg2PFJMlK17mUw+oU=", "dev": true, "requires": { "source-list-map": "^2.0.0", @@ -7180,7 +7189,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", "dev": true } } @@ -7198,7 +7207,7 @@ "websocket-extensions": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "integrity": "sha1-XS/yKXcAPsaHpLhwc9+7rBRszyk=", "dev": true }, "whet.extend": { @@ -7210,7 +7219,7 @@ "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", "dev": true, "requires": { "isexe": "^2.0.0" diff --git a/js/src/index.js b/js/src/index.js index 4fcc17d8f..e2ded815b 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -1,51 +1,51 @@ import './jupyter_geppetto'; import './geppettoJupyter.less'; + function load_extension() { + if(IPython.notebook.kernel == null) { + console.warn("Kernel not initialized. Waiting to load Jupyter Geppetto extension"); + setTimeout(load_extension, 100); + return; + } + console.log("Jupyter Geppetto extension loading"); + // Hide the header + $('#header').hide(); + + // Add shortcut to hide/show header + IPython.keyboard_manager.command_shortcuts.add_shortcut('ctrl-`', function (event) { + if (IPython.notebook.mode == 'command') { + $('#header').toggle(); + return false; + } + return true; + }); + + + // Make Jupyter (aka IPython) available to Geppetto + window.parent.IPython = IPython; + + // This loads the requirements into the notebook kernel + // TODO import synchronization as jupyter_geppetto is deprecated + IPython.notebook.kernel.execute('from jupyter_geppetto import synchronization, utils, synchronization as jupyter_geppetto'); + + // This will allow the application to be aware that the notebook kernel is ready + window.parent.GEPPETTO.trigger('jupyter_geppetto_extension_ready'); - // Hide the header - $('#header').hide(); - - // Add shortcut to hide/show header - IPython.keyboard_manager.command_shortcuts.add_shortcut('ctrl-`', function (event) { - if (IPython.notebook.mode == 'command') { - $('#header').toggle(); - return false; - } - return true; - }); - - // Make Jupyter (aka IPython) available to Geppetto - window.parent.IPython = IPython; - - // If a Geppetto extension is defining a custom behavior to load the kernel we call it - IPython.notebook.restart_kernel({ confirm: false }).then(function () { - - //Import the GUI sync to use the Python Controlled Capabilities, logging, etc - IPython.notebook.kernel.execute('from jupyter_geppetto import jupyter_geppetto, utils'); - - // Load the project & activate the experiment - var project = { id: 1, name: 'Project', experiments: [{ "id": 1, "name": 'Experiment', "status": 'DESIGN' }] } - window.parent.GEPPETTO.Manager.loadProject(project, false); - window.parent.GEPPETTO.Manager.loadExperiment(1, [], []); - - // Trigger event for the extension (ComponentInitialization) to run custom code - window.parent.GEPPETTO.trigger('jupyter_geppetto_extension_ready') - }); } var load_ipython_extension = function () { - if (IPython.notebook !== undefined && IPython.notebook._fully_loaded) { - console.log("Kernel ready") - load_extension(); - } else { - console.log("Waiting for kernel to be ready") - IPython.notebook.events.on("notebook_loaded.Notebook", function () { - load_extension(); - }) - } + if (IPython.notebook !== undefined && IPython.notebook._fully_loaded) { + console.log("Kernel ready") + load_extension(); + } else { + console.log("Waiting for kernel to be ready") + IPython.notebook.events.on("notebook_loaded.Notebook", function () { + load_extension(); + }) + } }; export { - load_ipython_extension + load_ipython_extension }; diff --git a/jupyter_geppetto/__init__.py b/jupyter_geppetto/__init__.py index 7293e5550..0801d4d9b 100644 --- a/jupyter_geppetto/__init__.py +++ b/jupyter_geppetto/__init__.py @@ -1,25 +1,29 @@ +import logging import os.path -import json -import codecs -from notebook.utils import url_path_join -from notebook.base.handlers import IPythonHandler -import tornado.websocket + import tornado.web -import nbformat as nbf -import logging -from nbformat.v4.nbbase import new_notebook -import pkg_resources -import traceback +from jupyter_geppetto.handlers import GeppettoController +from jupyter_geppetto.service import PathService +from jupyter_geppetto.settings import host_pattern, notebook_path, webapp_root_paths, home_page, \ + geppetto_servlet_path_name +from jupyter_geppetto.utils import createNotebook +from jupyter_geppetto.webapi import RouteManager +from jupyter_geppetto.websocket_connection import TornadoGeppettoWebSocketHandler +from notebook.utils import url_path_join +from tornado.routing import Matcher +from tornado.web import StaticFileHandler + +# @deprecated Backward compatibility: remove when every application stop using +import jupyter_geppetto.synchronization as jupyter_geppetto def _jupyter_server_extension_paths(): - return [{ "module": "jupyter_geppetto" }] + def _jupyter_nbextension_paths(): - return [dict( section="notebook", # the path is relative to the `jupyter_geppetto` directory @@ -30,89 +34,111 @@ def _jupyter_nbextension_paths(): require="jupyter_geppetto/index")] -class GeppettoHandler(IPythonHandler): +def _add_routes(nbapp, routes, host_pattern='.*$', base_path='/'): + nbapp.log.info('Adding routes starting at base path {}'.format(base_path)) + for route in routes: + nbapp.log.info('Adding http route {}'.format(route.path)) + route_path = url_path_join(base_path, route.path) + nbapp.log.info('Complete route url: {}'.format(route_path)) + nbapp.web_app.add_handlers(host_pattern, [(route_path, route.handler)]) + if route_path[-1] != '/': + nbapp.web_app.add_handlers(host_pattern, [(route_path + '/', route.handler)]) + else: + nbapp.web_app.add_handlers(host_pattern, [(route_path[0:-1], route.handler)]) - def get(self): - try: - config = self.application.settings['config'] - if 'library' in config: - # Create initial ipynb if it doesn't exist - if not os.path.isfile('notebook.ipynb'): - nb0 = new_notebook(cells=[], - metadata={'language': 'python',}) - f = codecs.open('notebook.ipynb', encoding='utf-8', mode='w') - nbf.write(nb0, f, 4) - f.close() - - template = pkg_resources.resource_filename(config['library'], 'geppetto/src/main/webapp/build/geppetto.vm') - self.write(open(template).read()) - else: - self.log.warning('Package to load missing in the url') - self.write('Package to load missing in the url') - except Exception: - self.log.info('Error on Geppetto Server extension') - traceback.print_exc() - - - - -class GeppettoProjectsHandler(IPythonHandler): - def get(self): - self.write({}) +def _add_static_routes(nbapp, static_paths, host_pattern='.*$', base_path='/'): + nbapp.log.info('Adding routes starting at base path {}'.format(base_path)) + for static_path in static_paths: + nbapp.log.info('Adding static http route {} pointing at'.format(static_path)) + for webapp_root_path in webapp_root_paths: + route_path = url_path_join(base_path, webapp_root_path, '(.*)') + nbapp.log.info('Complete route url: {}'.format(route_path)) + nbapp.web_app.add_handlers(host_pattern, [(route_path, StaticFileHandler, {"path": static_path})]) + + +def init_routes(nbapp, base_path): + web_app = nbapp.web_app + config = web_app.settings['config'] + if 'library' in config: + modules = config['library'].split(',') + for moduleName in modules: + nbapp.log.info('Initializing library module {}'.format(moduleName)) + module = __import__(moduleName) # Here the module should add its routes to the RouteManager + + _add_routes(nbapp, RouteManager.routes, host_pattern, base_path) + _add_static_routes(nbapp, RouteManager.static, host_pattern, base_path) + +class GeppettoServletMatcher(Matcher): -class WebSocketHandler(tornado.websocket.WebSocketHandler): + def match(self, request): + if geppetto_servlet_path_name == request.path[-len(geppetto_servlet_path_name):]: + return {} - def open(self): - # 1 -> Send the connection - self.write_message( - {"type": "client_id", "data": "{\"clientID\":\"Connection1\"}"}) - # 2 -> Check user privileges - self.write_message( - {"type": "user_privileges", "data": "{\"user_privileges\": \"{\\\"userName\\\":\\\"Python User\\\",\\\"loggedIn\\\":true,\\\"hasPersistence\\\":false,\\\"privileges\\\":[\\\"READ_PROJECT\\\",\\\"DOWNLOAD\\\",\\\"DROPBOX_INTEGRATION\\\", \\\"RUN_EXPERIMENT\\\", \\\"WRITE_PROJECT\\\"]}\"}"}) - def on_message(self, message): - jsonMessage = json.loads(message) - if (jsonMessage['type'] == 'geppetto_version'): - # Where do we get the geppetto version from? - self.write_message({"requestID": jsonMessage['requestID'], "type": "geppetto_version", "data": "{\"geppetto_version\":\"0.4.1\"}"}) +class BasePathRecognitionMatcher(Matcher): + '''Allows adding routes dynamically starting to the first call to /geppetto. + Starts as a catch-all then turns off after all the routes are added.''' - def on_close(self): - pass + def __init__(self, nbapp): + self.paths = [] + self.nbapp = nbapp + + def match(self, request): + path = request.path + self.nbapp.log.debug('Trying to match path: {}'.format(path)) + + if home_page not in path: + return None # We activate the path initialization only for the first home call + + base_path = path.split(home_page)[0] + if not base_path or base_path[0] != '/': + base_path = '/' + base_path + + self.nbapp.log.debug('Path found: {}'.format(base_path)) + if base_path in self.paths: + return None # Skip already added base path + + self.paths.append(base_path) + self.nbapp.log.info('New context path found: {}. Relative routes will be added.'.format(base_path)) + init_routes(self.nbapp, base_path) + return {} + + +class RetryHandler(tornado.web.RequestHandler): + + def get(self): + self.redirect(self.request.path) def load_jupyter_server_extension(nbapp): - try: + nbapp.log.info("Starting Geppetto Jupyter extension") + logging.info = nbapp.log.info + logging.debug = nbapp.log.debug + logging.error = nbapp.log.error + + if settings.debug: + nbapp.log_level = 'DEBUG' + RouteManager.initNotebookApp(nbapp) + + if not os.path.exists(notebook_path): + nbapp.log.info("Creating notebook {}".format(notebook_path)) + createNotebook(notebook_path) + else: + nbapp.log.info("Using notebook {}".format(notebook_path)) + + # Just add the wildcard matcher here. Other routes will be added dinamically from within the matcher. + nbapp.web_app.add_handlers(host_pattern, [(BasePathRecognitionMatcher(nbapp), RetryHandler)]) + nbapp.web_app.add_handlers(host_pattern, [(GeppettoServletMatcher(), TornadoGeppettoWebSocketHandler)]) + # init_routes(nbapp, '/') + nbapp.log.info("Geppetto Jupyter extension is running!") - web_app = nbapp.web_app - config = web_app.settings['config'] - - host_pattern = '.*$' - route_pattern = url_path_join(web_app.settings['base_url'], '/geppetto') - web_app.add_handlers(host_pattern, [(route_pattern, GeppettoHandler)]) - - route_pattern_geppetto_projects = url_path_join( - web_app.settings['base_url'], '/geppettoprojects') - web_app.add_handlers( - host_pattern, [(route_pattern_geppetto_projects, GeppettoProjectsHandler)]) - - websocket_pattern = url_path_join( - web_app.settings['base_url'], '/org.geppetto.frontend/GeppettoServlet') - web_app.add_handlers(host_pattern, [(websocket_pattern, WebSocketHandler)]) - - if 'library' in config: - nbapp.log.info("Geppetto Jupyter extension loading library: " + str(config['library'])) - template = pkg_resources.resource_filename(config['library'], 'geppetto/src/main/webapp/') # always use slash - web_app.add_handlers(host_pattern, [(r"/geppetto/(.*)", tornado.web.StaticFileHandler, { - 'path': template})]) - web_app.add_handlers(host_pattern, [(r"/org.geppetto.frontend/geppetto/(.*)", tornado.web.StaticFileHandler, { - 'path': template})]) - else: - nbapp.log.warning('Package to load missing in the url') - raise Exception - except Exception: - nbapp.log.info('Error on Geppetto Server extension') - traceback.print_exc() + nbapp.log.error('Error on Geppetto Server extension') + raise + +# We're adding here the base routes: or maybe it should be the application to add all the routes it needs +RouteManager.add_controller(GeppettoController) +RouteManager.add_web_client(PathService.get_webapp_directory()) \ No newline at end of file diff --git a/jupyter_geppetto/handlers.py b/jupyter_geppetto/handlers.py new file mode 100644 index 000000000..3415b7b36 --- /dev/null +++ b/jupyter_geppetto/handlers.py @@ -0,0 +1,23 @@ +import logging + +from jupyter_geppetto.service import PathService + +from .settings import template_path, home_page +from .webapi import get + + +class GeppettoController: + + @get('/geppettoprojects') + def getProjects(self, **kwargs): + # TODO still no project handling here. + return {} + + @get(home_page) + def index(self, **kwargs): + try: + template = template_path + return open(PathService.get_webapp_resource(template)).read() + except Exception: + logging.info('Error on Geppetto Server extension') + raise diff --git a/jupyter_geppetto/jupyter_geppetto.py b/jupyter_geppetto/jupyter_geppetto.py deleted file mode 100644 index 38af1c1f4..000000000 --- a/jupyter_geppetto/jupyter_geppetto.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -jupyter_geppetto.py -""" -import logging -from collections import defaultdict -import ipywidgets as widgets -import json -from traitlets import (CUnicode, Unicode, Instance, List, Dict, Bool, Float, Int) -from jupyter_geppetto import utils - - -# This is a list of all the models that are synched between Python and Javascript -synched_models = defaultdict(list) -context = None - -utils.configure_logging() - -def remove_component_sync(componentType, model): - component_to_remove = None - for existingModel, synched_component in list(synched_models.items()): - if existingModel == model: - component_to_remove = model - break - if(component_to_remove): - synched_models[component_to_remove].disconnect() - del synched_models[component_to_remove] - -class ComponentSync(widgets.Widget): - _model_name = Unicode('ComponentSync').tag(sync=True) - _model_module = Unicode('jupyter_geppetto').tag(sync=True) - _model_module_version = Unicode('~1.0.0') - - componentType = Unicode('componentType').tag(sync=True) - model = Unicode('').tag(sync=True) - id = Unicode('').tag(sync=True) - value = CUnicode().tag(sync=True) - - widget_name = Unicode('').tag(sync=True) - embedded = Bool(True).tag(sync=True) - read_only = Bool(False).tag(sync=True) - extraData = None - - def __init__(self, **kwargs): - super(ComponentSync, self).__init__(**kwargs) - - if 'model' in kwargs and kwargs["model"] is not None and kwargs["model"] != '': - synched_models[kwargs["model"]] = self - - self._value_handler = widgets.CallbackDispatcher() - #the method updateModel is executed in response to the sync_value event - self._value_handler.register_callback(self.updateModel) - - self.on_msg(self._handle_component_msg) - - def _handle_component_msg(self, _, content, buffers): - if content.get('event', '') == 'sync_value': - self._value_handler(self, content) - - - def updateModel(self, *args): - if self.model != None and self.model != '' and args[1]['value'] != None: - try: - value = json.loads(args[1]['value']) - if isinstance(value, str): - value = "'" + value + "'" - else: - value = str(value) - logging.debug("Updating model with new value " + value) - - context_path = next(iter(context)) - if(context_path and context_path!=""): - logging.debug("self.model = " + context_path+"."+self.model) - exec(context_path+"."+self.model + "=" + value, context) - else: - logging.debug("self.model = " + self.model) - exec(self.model + "=" + value) - except Exception as identifier: - logging.exception("Error updating model") - - def connect(self): - logging.debug("ComponentSync connecting to " + self.model) - self.send({"type": "connect"}) - - def disconnect(self): - logging.debug("ComponentSync disconnecting from " + self.model) - self.send({"type": "disconnect"}) - self._value_handler.register_callback(self.updateModel, remove=True) - self.on_msg(self._handle_component_msg, remove=True) - - - def __str__(self): - return "Component Sync => Widget Name: " + self.widget_name + ", Embedded: " + str(self.embedded) + ", Sync Value: " + self.value + ", Model: " + str(self.model) + ", Extra Data: " + self.extraData diff --git a/jupyter_geppetto/service.py b/jupyter_geppetto/service.py new file mode 100644 index 000000000..1f375db82 --- /dev/null +++ b/jupyter_geppetto/service.py @@ -0,0 +1,25 @@ +from jupyter_geppetto import settings +from notebook.utils import url_path_join +import logging +import os +import sys +import glob + +class PathService: + webapp_directory = os.path.abspath(".") + '/webapp/' + + @classmethod + def get_webapp_directory(cls): + if not os.path.exists(cls.webapp_directory): + + discovered_paths = glob.glob(os.path.abspath(".") + '/*/' + settings.geppetto_webapp_file) + if discovered_paths: + cls.webapp_directory = os.path.dirname(discovered_paths[0]) + logging.info('Webapp directory discovered: {}'.format(cls.webapp_directory)) + else: + logging.error('Cannot determine webapp directory. PathService won\'t work') + return cls.webapp_directory + + @classmethod + def get_webapp_resource(cls, path): + return url_path_join(cls.get_webapp_directory(), path) diff --git a/jupyter_geppetto/settings.py b/jupyter_geppetto/settings.py new file mode 100644 index 000000000..3d4c0dad9 --- /dev/null +++ b/jupyter_geppetto/settings.py @@ -0,0 +1,29 @@ +''' +Settings for the module. Change right after importing, if needed. +''' + +# The path of the geppetto client. Relative path is from the application root +geppetto_webapp_file = 'GeppettoConfiguration.json' + +# The path of the template. It should be relative to the webapp path +template_path = 'build/geppetto.vm' +# The path of the Jupyter notebook file +notebook_path = 'notebook.ipynb' +# Server host pattern +host_pattern = '.*$' + +webapp_root_paths = ['/geppetto'] + +home_page = '/geppetto' + +geppetto_servlet_path_name = 'GeppettoServlet' + + +debug = False + + +class websocket: + compression_enabled = False + min_message_length_for_compression = 200 + + diff --git a/jupyter_geppetto/synchronization.py b/jupyter_geppetto/synchronization.py index ceaafcf6f..af607f767 100644 --- a/jupyter_geppetto/synchronization.py +++ b/jupyter_geppetto/synchronization.py @@ -1,14 +1,101 @@ """ synchronization.py """ -import traceback - -import logging -from jupyter_geppetto import jupyter_geppetto import time import threading -import importlib + + +import logging +from collections import defaultdict +import ipywidgets as widgets import json +from traitlets import (CUnicode, Unicode, Instance, List, Dict, Bool, Float, Int) +from jupyter_geppetto import utils + +# This is a list of all the models that are synched between Python and Javascript +synched_models = defaultdict(list) +context = None + +utils.configure_logging() + + +def remove_component_sync(componentType, model): + component_to_remove = None + for existingModel, synched_component in list(synched_models.items()): + if existingModel == model: + component_to_remove = model + break + if (component_to_remove): + synched_models[component_to_remove].disconnect() + del synched_models[component_to_remove] + + +class ComponentSync(widgets.Widget): + '''Defines the synchronization widget''' + _model_name = Unicode('ComponentSync').tag(sync=True) + _model_module = Unicode('jupyter_geppetto').tag(sync=True) + _model_module_version = Unicode('~1.0.0') + + componentType = Unicode('componentType').tag(sync=True) + model = Unicode('').tag(sync=True) + id = Unicode('').tag(sync=True) + value = CUnicode().tag(sync=True) + + widget_name = Unicode('').tag(sync=True) + embedded = Bool(True).tag(sync=True) + read_only = Bool(False).tag(sync=True) + extraData = None + + def __init__(self, **kwargs): + super(ComponentSync, self).__init__(**kwargs) + + if 'model' in kwargs and kwargs["model"] is not None and kwargs["model"] != '': + synched_models[kwargs["model"]] = self + + self._value_handler = widgets.CallbackDispatcher() + # the method updateModel is executed in response to the sync_value event + self._value_handler.register_callback(self.updateModel) + + self.on_msg(self._handle_component_msg) + + def _handle_component_msg(self, _, content, buffers): + if content.get('event', '') == 'sync_value': + self._value_handler(self, content) + + def updateModel(self, *args): + if self.model != None and self.model != '' and args[1]['value'] != None: + try: + value = json.loads(args[1]['value']) + if isinstance(value, str): + value = "'" + value + "'" + else: + value = str(value) + logging.debug("Updating model with new value " + value) + + object_name = next(iter(context)) + if (object_name and object_name != ""): + logging.debug("self.model = " + object_name + "." + self.model) + exec(object_name + "." + self.model + "=" + value, context) + else: + logging.debug("self.model = " + self.model) + exec(self.model + "=" + value) + except Exception as identifier: + logging.exception("Error updating model") + + def connect(self): + logging.debug("ComponentSync connecting to " + self.model) + self.send({"type": "connect"}) + + def disconnect(self): + logging.debug("ComponentSync disconnecting from " + self.model) + self.send({"type": "disconnect"}) + self._value_handler.register_callback(self.updateModel, remove=True) + self.on_msg(self._handle_component_msg, remove=True) + + def __str__(self): + return "Component Sync => Widget Name: " + self.widget_name + ", Embedded: " + str( + self.embedded) + ", Sync Value: " + self.value + ", Model: " + str( + self.model) + ", Extra Data: " + self.extraData def startSynchronization(scope): @@ -21,10 +108,9 @@ class LoopTimer(threading.Thread): """ a Timer that calls f every interval - A thread that checks all the variables that we are synching between Python and Javascript and if + A thread that checks all the variables that we are syncing between Python and Javascript and if these variables have changed on the Python side will propagate the changes to Javascript - TODO This code should move to a generic geppetto class since it's not NetPyNE specific """ def __init__(self, interval, scope, fun=None): @@ -49,7 +135,7 @@ def run(self): def process_events(self): try: - for model, synched_component in list(jupyter_geppetto.synched_models.items()): + for model, synched_component in list(synched_models.items()): modelValue=None if model != '': try: diff --git a/jupyter_geppetto/utils.py b/jupyter_geppetto/utils.py index fd6c48309..077084295 100644 --- a/jupyter_geppetto/utils.py +++ b/jupyter_geppetto/utils.py @@ -1,27 +1,32 @@ import logging + +from ipykernel.jsonutil import json_clean # from jupyter_client import session from zmq.utils import jsonapi -from ipykernel.jsonutil import json_clean + def convertToJS(content): # return session.json_packer(content).decode("utf-8") # Old way: this needs to be deleted if the above line is enough return jsonapi.dumps(json_clean(content)).decode("utf-8") + def convertToPython(content): # return session.json_unpacker(content) # Old way: this needs to be deleted if the above line is enough return jsonapi.loads(content) + def exception_to_string(exc_info): import IPython.core.ultratb tb = IPython.core.ultratb.VerboseTB() return tb.text(*exc_info) + def getJSONError(message, exc_info): data = {} data['type'] = 'ERROR' - data['message'] = message + data['websocket'] = message if isinstance(exc_info, str): details = exc_info @@ -30,21 +35,37 @@ def getJSONError(message, exc_info): data['details'] = details return data + def getJSONReply(): data = {} data['type'] = 'OK' return data + def configure_logging(): try: # Configure log logger = logging.getLogger() - fhandler = logging.FileHandler(filename='netpyne-ui.log', mode='a') - formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + fhandler = logging.FileHandler(filename='app.log', mode='a') + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s') fhandler.setFormatter(formatter) logger.addHandler(fhandler) logger.setLevel(logging.DEBUG) logging.debug('Log configured') except Exception as exception: logging.exception("Unexpected error while initializing Geppetto from Python:") - logging.error(exception) \ No newline at end of file + logging.error(exception) + + +def createNotebook(filename): + import nbformat as nbf + from nbformat.v4.nbbase import new_notebook + import codecs + nb0 = new_notebook(cells=[], metadata={"kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }}) + f = codecs.open(filename, encoding='utf-8', mode='w') + nbf.write(nb0, f, 4) + f.close() diff --git a/jupyter_geppetto/webapi.py b/jupyter_geppetto/webapi.py new file mode 100644 index 000000000..37cc46691 --- /dev/null +++ b/jupyter_geppetto/webapi.py @@ -0,0 +1,180 @@ +''' +Define decorators to easily define action controllers + +''' +from notebook.base.handlers import IPythonHandler +from tornado.web import RequestHandler, StaticFileHandler +import logging +from jupyter_geppetto import utils + + +import sys + + +def export(fn): + '''Decorator to add to the __all__ variable''' + mod = sys.modules[fn.__module__] + if not hasattr(mod, '__all__'): + mod.__all__ = [] + mod.__all__.append(fn.__name__) + return fn + + +class METHODS: + GET = 'get' + POST = 'post' + PUT = 'put' + DELETE = 'delete' + + +class Route: + def __init__(self, path, handler): + self.path = path + self.handler = handler + + +@export +class RouteManager: + '''Gives an access point to define routes and relates controllers/handlers''' + routes = [] + static = [] + + @classmethod + def initNotebookApp(cls, nbapp): + cls.nbapp = nbapp + + @classmethod + def add_routes(cls, routes): + cls.routes += routes + + @classmethod + def add_route(cls, path, handler): + cls.routes.append(Route(path, handler)) + + @classmethod + def add_controller(cls, controller_class): + '''A controller is a class which defined the actions as methods decorated with tornado_action decorators + (get, post, or custom)''' + cls.routes += [c for c in controller_class.__dict__.values() if type(c) == Route] + + @classmethod + def add_web_client(cls, files_path): + cls.static.append(files_path) + + +class HandlerType(type): + '''Just a recognizable metaclass''' + pass + + +def tornado_action(method, path, headers={}): + '''Annotation to build a tornado web handler on a function or method''' + + assert method in (getattr(METHODS, m) for m in dir(METHODS)), \ + 'Method {} not allowed. Should be one of {}'.format( + method, tuple(getattr(METHODS, m) for m in dir(METHODS) if not '_' in m)) + + def real_decorator(fn): + def handlerFn(self, *args): + logging.info('Calling {}'.format(fn.__name__)) + function_arguments = fn.__code__.co_varnames + kwargs = {name: self.get_query_argument(name) + for name in function_arguments[len(args) + 1:] + if self.get_query_argument(name, None) != None + } + kwargs.update({name: self.get_body_argument(name) + for name in function_arguments[len(args) + 1:] + if self.get_body_argument(name, None) != None + }) + logging.debug('Positional arguments: {}'.format(args)) + logging.debug('Keyword arguments: {}'.format(kwargs)) + value = fn(self, *args, **kwargs) + + for hname, hvalue in headers.items(): + self.set_header(hname, hvalue) + if value is not None: + self.finish(value) + + route = Route(path, HandlerType(path, (IPythonHandler,), {method: handlerFn})) + return route + + return real_decorator + + +@export +def get(path, headers={}): + '''Annotation for get actions. + Parameters are taken first from the path wildcard pieces (.*), then from the query string. Query string parameters + must have the same name of decorated function arguments. + ''' + return tornado_action(METHODS.GET, path, headers) + + +@export +def post(path, headers={}): + '''Annotation for post actions''' + return tornado_action(METHODS.POST, path, headers) + + +if __name__ == '__main__': + class TestController: + + @get('/test') + def foo(self, arg, argument=1): + return '{} {} {}'.format('foo', arg, argument) + + + print(TestController.foo.path, TestController.foo.handler) + + + class MockApplication: + ui_methods = {} + ui_modules = {} + settings = {} + + def log_request(self, *args): + pass + + + class MockConnection: + def set_close_callback(self, *args): + return 0 + + def write(self, something, callback=None): + print('write') + print(something) + + def finish(self, *args, **kwargs): + print('finish') + + def write_headers(self, *args, **kwargs): + print('write_headers') + + + class MockRequest: + connection = MockConnection() + query_arguments = {'argument': ['2']} + method = 'GET' + headers = {} + uri = '' + remote_ip = '' + + + print("\n\nTest auto created handler") + handler = TestController.foo.handler(MockApplication(), MockRequest()) + handler._execute([]) + handler._finished = False + handler.get(1) + + print("\n\nTest standard handler") + + + class EquivalentHandler(RequestHandler): + def get(self, arg): + self.finish('{} {} {}'.format('foo', arg, self.get_query_argument('argument'))) + + + handler = EquivalentHandler(MockApplication(), MockRequest()) + handler._execute([]) + handler._finished = False + handler.get(1) diff --git a/jupyter_geppetto/websocket_connection.py b/jupyter_geppetto/websocket_connection.py new file mode 100644 index 000000000..5c561ad2b --- /dev/null +++ b/jupyter_geppetto/websocket_connection.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +""" generated source for module WebsocketConnection """ +from __future__ import print_function + +import gzip +import json +import logging + +from jupyter_geppetto import settings +from pyecore.valuecontainer import EList +from pygeppetto.api.message_handler import GeppettoMessageHandler +from tornado.websocket import WebSocketHandler + + +# package: org.geppetto.frontend.controllers +# +# * Class used to process Web Socket Connections. Messages sent from the connecting clients, web socket connections, are received in here. +# * +# * @author matteocantarelli +# * +# + + +class TornadoGeppettoWebSocketHandler(WebSocketHandler, GeppettoMessageHandler): + + def __init__(self, *args, **kwargs): + WebSocketHandler.__init__(self, *args, **kwargs) + GeppettoMessageHandler.__init__(self) + + def open(self): + # 1 -> Send the connection + logging.info('Open websocket') + self.__class__.connection_id += 1 + + self.sendClientId() + + # 2 -> Check user privileges + self.sendPrivileges() + + def send_message_data(self, msg_data): + msg = json.dumps(msg_data) + if settings.websocket.compression_enabled and len(msg) > settings.websocket.min_message_length_for_compression: + self.write_message(gzip.compress(bytes(msg, 'utf-8')), binary=True) + self.write_message(msg) + + def on_message(self, message): + self.handle_message(json.loads(message)) + + def on_close(self): + # self.write_message({ + # 'type': 'socket_closed', + # 'data': '' + # }) + # self.geppettoHandler.closeProject() + logging.info("Closed Connection ...") + + # NOTE: no other websocket expected for now + + # + # * @param runnableQueries + # * @return A list based on the EMF class. It's not possible to use directly the EMF class as Gson requires fields with public access modifiers which breaks EMF encapsulation + # + + def convertRunnableQueriesDataTransferModel(self, runnableQueries): + """ generated source for method convertRunnableQueriesDataTransferModel """ + runnableQueriesEMF = EList('') + from pygeppetto.model.datasources.datasources import RunnableQuery + for dt in runnableQueries: + rqEMF = RunnableQuery(targetVariablePath=dt.targetVariablePath, queryPath=dt.queryPath) + runnableQueriesEMF.append(rqEMF) + return runnableQueriesEMF diff --git a/setup.py b/setup.py index 2cd8c2671..53affba7b 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setuptools.setup( name="jupyter_geppetto", - version="0.4.2", + version="1.0.0", url="https://github.com/openworm/org.geppetto.frontend.jupyter", author="The Geppetto Development Team", author_email="info@geppetto.org", @@ -66,7 +66,7 @@ 'prometheus-client==0.3.1', 'prompt-toolkit==1.0.15', 'ptyprocess==0.6.0', - 'pyecore==0.8.7', + #'pyecore==0.8.7', 'Pygments==2.2.0', 'pyparsing==2.2.0', 'python-dateutil==2.7.3', @@ -86,6 +86,6 @@ 'webencodings==0.5.1', 'widgetsnbextension==3.4.1', 'jupyter==1.0.0', - 'pygeppetto==0.4.2' + 'pygeppetto>=0.5.0' ], )