diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3be9d1f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +max_line_length = 120 +tab_width = 2 + +[*.py] +indent_size = 4 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fdd8af5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +# IntelliJ +.idea/ +*.iml + +# Javascript compiled files +beakerx_widgets/static/ +js/css/*.css +js/css/*.css.map +js/dist/ +js/lib/ + +# Karma tests coverage +js/coverage/ + +# Beaker +/beakerx_widgets/*/static/ + +# Node modules +node_modules/ + +yarn-error.log + +# Python +*.py[cod] +__pycache__/ +*.egg-info + +# Jupyter notebook checkpoints +.ipynb_checkpoints/ +*Untitled*.ipynb + +# Gradle +.gradle/ +gradle/ +gradlew +gradlew.bat +/build/ +*/build/ + +# conda +dist + diff --git a/README.md b/README.md index 4585e28..1ae8d36 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ -# an empty repository +``` +conda env create -n beakerx_widgets -f configuration.yml +conda activate beakerx_widgets +# install beakerx_base (cd $PATH_TO_BEAKERX_BASE; pip install -e .) +(cd beakerx_widgets; pip install -r requirements.txt --verbose) +beakerx_widgets install +``` diff --git a/beakerx_widgets/LICENSE b/beakerx_widgets/LICENSE new file mode 100644 index 0000000..5c304d1 --- /dev/null +++ b/beakerx_widgets/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/beakerx_widgets/MANIFEST.in b/beakerx_widgets/MANIFEST.in new file mode 100644 index 0000000..ab718bd --- /dev/null +++ b/beakerx_widgets/MANIFEST.in @@ -0,0 +1,5 @@ +recursive-include beakerx_widgets/static * + +include LICENSE +include NOTICE +include setupbase.py diff --git a/beakerx_widgets/NOTICE b/beakerx_widgets/NOTICE new file mode 100644 index 0000000..4680966 --- /dev/null +++ b/beakerx_widgets/NOTICE @@ -0,0 +1,5 @@ +BeakerX: Beaker Extensions for Jupyter +Copyright 2014-2017 Two Sigma Open Source, LLC + +This product includes software developed at +Two Sigma Open Source, LLC (http://opensource.twosigma.com/). \ No newline at end of file diff --git a/beakerx_widgets/beakerx_widgets/__init__.py b/beakerx_widgets/beakerx_widgets/__init__.py new file mode 100644 index 0000000..6750d22 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/__init__.py @@ -0,0 +1,51 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ._version import version_info, __version__ +from .commands import parse_widgets +from .handlers import load_jupyter_server_extension +# +from .forms import * +from .magics import * +from .outputs import * +from .plots import * +from .spark import * + + +def _jupyter_nbextension_paths(): + return [ + { + 'section': 'notebook', + 'src': 'static', + 'dest': 'beakerx_widgets', + 'require': 'beakerx_widgets/extension' + }, { + 'section': 'tree', + 'src': 'static', + 'dest': 'beakerx_widgets', + 'require': 'beakerx_widgets/tree-extension' + } + ] + + +def _jupyter_server_extension_paths(): + return [dict(module="beakerx_widgets")] + + +def run(): + try: + parse_widgets() + except KeyboardInterrupt: + return 130 + return 0 diff --git a/beakerx_widgets/beakerx_widgets/_version.py b/beakerx_widgets/beakerx_widgets/_version.py new file mode 100644 index 0000000..ff98aa3 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/_version.py @@ -0,0 +1,2 @@ +version_info = (1, 6, 0) +__version__ = '.'.join(map(str, version_info)) diff --git a/beakerx_widgets/beakerx_widgets/beakerx_autotranslation_server.py b/beakerx_widgets/beakerx_widgets/beakerx_autotranslation_server.py new file mode 100644 index 0000000..161d68d --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/beakerx_autotranslation_server.py @@ -0,0 +1,121 @@ +# Copyright 2018 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import base64 +import logging +import os +import random +import socket +import string +import tornado.ioloop +import tornado.web + +beakerx = {} + +logging.getLogger('tornado.access').disabled = True + + +def basic_auth(f): + def auth(username, password): + return username == "beakerx" and password == os.environ["BEAKERX_AUTOTRANSLATION_PASSWORD"] + + def _request_auth(handler): + handler.set_status(401) + return handler.finish() + + def _request_error(handler, exception): + handler.set_status(500) + return handler.finish(exception) + + def wrap(*args): + handler = args[0] + try: + auth_header = handler.request.headers.get('Authorization') + if (auth_header is None) or (not auth_header.startswith('Basic ')): + return _request_auth(handler) + auth_decoded = base64.b64decode(auth_header[6:]) + username, password = auth_decoded.decode('UTF-8').split(':', 2) + if auth(username, password): + f(*args) + else: + _request_auth(handler) + except Exception as e: + _request_error(handler, e) + + return wrap + + +class MainSaveHandler(tornado.web.RequestHandler): + + @basic_auth + def post(self): + input_json = tornado.escape.json_decode(self.request.body) + self.validate_autotraslation_input(input_json, "sessionId") + self.validate_autotraslation_input(input_json, "name") + self.validate_autotraslation_input(input_json, "json") + + session_id = input_json["sessionId"] + name = input_json["name"] + json = input_json["json"] + if session_id not in beakerx: + beakerx[session_id] = {} + + beakerx[session_id][name] = json + return self.finish("ok") + + @staticmethod + def validate_autotraslation_input(input_json, key): + if key not in input_json: + raise Exception("Data doesn't contain attribute: "+key) + + +class MainGetHandler(tornado.web.RequestHandler): + + @basic_auth + def get(self, session_id, name): + if session_id in beakerx and name in beakerx[session_id]: + return self.finish(beakerx[session_id][name]) + return self.finish("undefined") + + +def make_app(): + return tornado.web.Application([ + (r"/autotranslation/(.*)/(.*)", MainGetHandler), + (r"/autotranslation/", MainSaveHandler), + ]) + + +def get_free_tcp_port(): + tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + tcp.bind(('localhost', 0)) + addr, port = tcp.getsockname() + tcp.close() + return port + + +def random_string_generator(size=128): + s = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in + range(size)) + return s + + +def init_env(): + os.environ["BEAKERX_AUTOTRANSLATION_PASSWORD"] = random_string_generator() + os.environ["BEAKERX_AUTOTRANSLATION_PORT"] = str(get_free_tcp_port()) + + +def start_autotranslation_server(): + init_env() + app = make_app() + app.listen(os.environ["BEAKERX_AUTOTRANSLATION_PORT"]) diff --git a/beakerx_widgets/beakerx_widgets/beakerx_server.py b/beakerx_widgets/beakerx_widgets/beakerx_server.py new file mode 100644 index 0000000..5f66153 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/beakerx_server.py @@ -0,0 +1,43 @@ +# Copyright 2018 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import socket +import zmq +import threading + + +class BeakerxZMQServer: + + def __init__(self, beakerXQueue): + self.queue = beakerXQueue + self.url = "tcp://127.0.0.1:" + BeakerxZMQServer.get_free_tcp_port() + thread = threading.Thread(target=self.threaded_function, daemon=True) + thread.start() + + def threaded_function(self): + context = zmq.Context() + socket = context.socket(zmq.REP) + socket.bind(self.url) + while True: + message = socket.recv() + self.queue.put(message) + socket.send_string("Ok") + + @staticmethod + def get_free_tcp_port(): + tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + tcp.bind(('localhost', 0)) + addr, port = tcp.getsockname() + tcp.close() + return str(port) diff --git a/beakerx_widgets/beakerx_widgets/bkr2ipynb.py b/beakerx_widgets/beakerx_widgets/bkr2ipynb.py new file mode 100644 index 0000000..1481a9c --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/bkr2ipynb.py @@ -0,0 +1,101 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Create a notebook containing code from a beaker notebook. +Run as: python bkr2ipynb.py [notebook name].bkr +""" +import re +import sys +import json +import nbformat +import argparse +import os +from nbformat.v4 import new_notebook, new_code_cell, new_markdown_cell + +def setHeader(level, title): + dash = '' + while level != 0: + dash += '#' + level -= 1 + return '{0} {1}'.format(dash, title) + +def getFixedCodeText(cell): + ret = ''; + body = cell['body'] + if isinstance(body, list): + ret = "\n".join(body) + else: + ret = body + ret = re.sub(r'\bbeaker\b', 'beakerx', ret) + return ret; + +def parseBkr(data): + nb = new_notebook() + evaluators = list((cell['evaluator']) for cell in data['cells'] if 'evaluator' in cell) + kernel_name = max(evaluators, key=evaluators.count) if evaluators else 'IPython' + if kernel_name in ['JavaScript', 'HTML', 'TeX']: + kernel_name = 'IPython' + if kernel_name == 'IPython': + kernel_spec = {"kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }} + else: + kernel_spec = {"kernelspec": { + "display_name": kernel_name, + "language": kernel_name.lower(), + "name": kernel_name.lower() + }} + nb.metadata = kernel_spec + for cell in data['cells']: + if cell['type'] == 'code': + metadata = {} + if 'initialization' in cell: + metadata['init_cell'] = True + if 'tags' in cell: + tags = [cell['tags']] + metadata['tags'] = tags + if cell['evaluator'] != kernel_name: + if cell['evaluator'] == 'TeX': + nb.cells.append(new_markdown_cell("${0}$".format(getFixedCodeText(cell['input'])))) + else: + nb.cells.append( + new_code_cell(source='%%{0}\n{1}'.format(cell['evaluator'].lower(), getFixedCodeText(cell['input'])), + metadata=metadata)) + else: + nb.cells.append(new_code_cell(source=getFixedCodeText(cell['input']), metadata=metadata)) + if cell['type'] == 'markdown': + nb.cells.append(new_markdown_cell(getFixedCodeText(cell))) + if cell['type'] == 'section': + nb.cells.append(new_markdown_cell(setHeader(cell['level'], cell['title']))) + return nb + +def convertNotebook(notebook): + with open(notebook, encoding='utf-8') as data_file: + data = json.load(data_file) + nb = parseBkr(data) + nbformat.write(nb, os.path.splitext(notebook)[0] + '.ipynb') + +def main(args): + for notebook in args.notebooks: + convertNotebook(notebook) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('notebooks', nargs='+', + help="beaker notebooks to be converted. Enter *.bkr in case you want to convert all notebooks at once.") + if len(sys.argv) == 1: + parser.print_help() + args = parser.parse_args() + main(args) diff --git a/beakerx_widgets/beakerx_widgets/commands.py b/beakerx_widgets/beakerx_widgets/commands.py new file mode 100644 index 0000000..624c981 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/commands.py @@ -0,0 +1,93 @@ +# Copyright 2018 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import argparse +import sys +import beakerx_widgets +from notebook import notebookapp as app +from .install import install, uninstall +from .bkr2ipynb import main +# from beakerx_magics import Py4JServer + + +def install_subparser(subparser): + install_parser = subparser.add_parser('install', help='installs BeakerX extensions') + install_parser.set_defaults(func=install) + install_parser.add_argument("--prefix", + help="location of the environment to install into", + default=sys.prefix) + install_parser.add_argument("--lab", + help="install lab extension", + action='store_true') + return subparser + + +def uninstall_subparser(subparser): + uninstall_parser = subparser.add_parser('uninstall', help='uninstalls BeakerX extensions') + uninstall_parser.set_defaults(func=uninstall) + uninstall_parser.add_argument("--prefix", + help="location of the environment to uninstall from", + default=sys.prefix) + uninstall_parser.add_argument("--lab", + help="uninstall lab extension", + action='store_true') + return subparser + + +def bkr2ipynb_subparser(subparser): + bkr2ipynb_parser = subparser.add_parser('bkr2ipynb', help='converts Beaker notebooks to ipynb format') + bkr2ipynb_parser.set_defaults(func=main) + bkr2ipynb_parser.add_argument('notebooks', nargs='+', + help="Beaker notebooks to be converted. Enter *.bkr in case you want to convert all notebooks at once.") + return subparser + + +# def py4j_server_subparser(subparser): +# py4j_server_parser = subparser.add_parser('py4j_server') +# py4j_server_parser.set_defaults(func=start_py4j_server) +# py4j_server_parser.add_argument("--port") +# py4j_server_parser.add_argument("--pyport") +# py4j_server_parser.add_argument("--kernel") +# py4j_server_parser.add_argument("--context") + + +# def start_py4j_server(args): +# Py4JServer(args.port, args.pyport, args.kernel, args.context) + + +def run_jupyter(jupyter_commands): + app.launch_new_instance(jupyter_commands) + + +def init_parser(): + parser = argparse.ArgumentParser() + parser.add_argument('--version', action='version', version=beakerx_widgets.__version__) + parser.set_defaults(func=run_jupyter) + + subparsers = parser.add_subparsers() + install_subparser(subparsers) + uninstall_subparser(subparsers) + bkr2ipynb_subparser(subparsers) + # py4j_server_subparser(subparsers) + return parser + + +def parse_widgets(): + parser = init_parser() + args, jupyter_commands = parser.parse_known_args() + if args.func == run_jupyter: + args.func(jupyter_commands) + elif not jupyter_commands: + args.func(args) + else: + parser.parse_args(jupyter_commands) diff --git a/beakerx_widgets/beakerx_widgets/environment.py b/beakerx_widgets/beakerx_widgets/environment.py new file mode 100644 index 0000000..21277c0 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/environment.py @@ -0,0 +1,125 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from os import environ, path, fdopen, makedirs, O_RDWR, O_CREAT, O_TRUNC, open as osopen +from jupyter_core import paths +import json +import pathlib + +default_config = """ +{ + "beakerx": { + "version": 2, + "jvm_options": { + "heap_GB": null, + "other": [], + "properties": {} + }, + "ui_options": { + "auto_close": true, + "improve_fonts": true, + "wide_cells": true, + "auto_save": true, + "use_data_grid": true, + "show_catalog": false + }, + "spark_options":{} + } +} +""" + + +class EnvironmentSettings: + def __init__(self): + pass + + config_path = path.join(paths.jupyter_config_dir(), 'beakerx.json') + + @staticmethod + def save_setting_to_file(content): + makedirs(paths.jupyter_config_dir(), exist_ok=True) + with fdopen(osopen(EnvironmentSettings.config_path, O_RDWR | O_CREAT, 0o600), 'w+') as file: + file_content = file.read() + new_settings = json.loads(content) + if file_content: + saved_settings = json.loads(file_content) + file.seek(0) + file.truncate() + for setting_name in new_settings['beakerx']: + saved_settings['beakerx'][setting_name] = new_settings['beakerx'][setting_name] + else: + saved_settings = new_settings + file.write(json.dumps(saved_settings, indent=4, sort_keys=True)) + + @staticmethod + def read_setting_from_file(): + try: + file = open(EnvironmentSettings.config_path, 'r') + content = file.read() + beakerx_settings = json.loads(content) + if beakerx_settings['beakerx'].get('version') is None: + content = EnvironmentSettings._convert_to_version_2(beakerx_settings) + except IOError: + content = default_config + EnvironmentSettings.save_setting_to_file(default_config) + except ValueError as ex: + print ('Error while parsing beakerx.json: ', ex) + content = default_config + else: + file.close() + + return content + + @staticmethod + def _convert_to_version_2(beakerx_settings): + settings = beakerx_settings['beakerx']['jvm_options'] + new_prop = [] + for x in settings['properties']: + prop = { + 'name': x, + 'value': settings['properties'][x] + } + new_prop.append(prop) + settings['properties'] = new_prop + if settings.get('heap_GB'): + settings['heap_GB'] = float(settings['heap_GB']) + content = json.dumps(beakerx_settings) + return content + + @staticmethod + def read_beakerx_env_settings(): + args = [] + + settings = json.loads(EnvironmentSettings.read_setting_from_file()) + beakerx_settings = settings['beakerx'] + if 'jvm_options' in beakerx_settings: + jvm_settings = beakerx_settings['jvm_options'] + for x in jvm_settings['other']: + args.append(x) + + for x in jvm_settings['properties']: + name = x.get('name') + value = x.get('value') + value = '-D' + name + '=' + value + args.append(value) + + if 'heap_GB' in jvm_settings and jvm_settings['heap_GB']: + val = float(jvm_settings['heap_GB']) + if val.is_integer(): + value = '-Xmx' + str(int(val)) + 'g' + else: + value = '-Xmx' + str(int(val * 1024)) + 'm' + args.append(value) + + return args diff --git a/beakerx_widgets/beakerx_widgets/forms/__init__.py b/beakerx_widgets/beakerx_widgets/forms/__init__.py new file mode 100644 index 0000000..d82ca3a --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/forms/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .easyforms import EasyForm diff --git a/beakerx_widgets/beakerx_widgets/forms/easyforms.py b/beakerx_widgets/beakerx_widgets/forms/easyforms.py new file mode 100644 index 0000000..7671897 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/forms/easyforms.py @@ -0,0 +1,191 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from beakerx_base import BeakerxBox, getValue, BeakerxText, BeakerxPassword, BeakerxTextArea, BeakerxButton, \ + SelectMultipleWithRows, SelectMultipleSingle, DatePicker, BeakerxComboBox, BeakerxCheckbox, BeakerxHBox, \ + BeakerxVBox, BeakerxCheckboxGroup, BeakerxLabel, RadioButtons, EasyFormComponent +from ipykernel.comm import Comm +from traitlets import Unicode + + +class EasyForm(BeakerxBox): + _view_name = Unicode('EasyFormView').tag(sync=True) + _model_name = Unicode('EasyFormModel').tag(sync=True) + _view_module = Unicode('beakerx_widgets').tag(sync=True) + _model_module = Unicode('beakerx_widgets').tag(sync=True) + easyFormName = Unicode(default_value='Form default').tag(sync=True) + test = "" + HORIZONTAL = 1 + VERTICAL = 2 + + def __init__(self, *args, **kwargs): + super(EasyForm, self).__init__(**kwargs) + self.easyFormName = getValue(kwargs, 'title', "") + if self.easyFormName == "" and len(args) > 0: + self.easyFormName = args[0] + + def _handle_msg(self, msg): + print(msg) + + def addTextField(self, *args, **kwargs): + text = BeakerxText(description=self.getDescription(args, kwargs)) + text.size = getValue(kwargs, 'width', -1) + self.children += (text,) + self.components[text.description] = text + return text + + def addPasswordField(self, *args, **kwargs): + password = BeakerxPassword(description=self.getDescription(args, kwargs)) + password.size = getValue(kwargs, 'width', -1) + self.children += (password,) + self.components[password.description] = password + return password + + def addTextArea(self, *args, **kwargs): + textarea = BeakerxTextArea( + description=self.getDescription(args, kwargs)) + textarea.cols = getValue(kwargs, 'width', -1) + textarea.rows = getValue(kwargs, 'height', -1) + textarea.value = getValue(kwargs, 'value', "") + textarea.placeholder = getValue(kwargs, 'placeholder', "") + self.children += (textarea,) + self.components[textarea.description] = textarea + return textarea + + def addButton(self, *args, **kwargs): + button = BeakerxButton(description=self.getDescription(args, kwargs)) + button.tag = getValue(kwargs, 'tag', "") + button.on_click(self.buttonCallback) + self.children += (button,) + return button + + def buttonCallback(self, *args): + if len(args) > 0: + args[0].actionPerformed() + arguments = dict(target_name='beakerx_widgets.tag.run') + comm = Comm(**arguments) + msg = {'runByTag': args[0].tag} + state = {'state': msg} + comm.send(data=state, buffers=[]) + + def addList(self, *args, **kwargs): + multi_select = getValue(kwargs, 'multi', True) + if multi_select: + list = SelectMultipleWithRows( + description=self.getDescription(args, kwargs)) + else: + list = SelectMultipleSingle( + description=self.getDescription(args, kwargs)) + list.options = self.getOptions(args, kwargs) + list.size = getValue(kwargs, 'rows', len(list.options)) + + self.children += (list,) + self.components[list.description] = list + return list + + def addDatePicker(self, *args, **kwargs): + data_picker = DatePicker(description=self.getDescription(args, kwargs)) + data_picker.value = getValue(kwargs, 'value', '') + self.children += (data_picker,) + self.components[data_picker.description] = data_picker + return data_picker + + def addComboBox(self, *args, **kwargs): + dropdown = BeakerxComboBox(description=self.getDescription(args, kwargs)) + dropdown.options = self.getOptions(args, kwargs) + dropdown.original_options = self.getOptions(args, kwargs) + dropdown.editable = getValue(kwargs, 'editable', False) + self.children += (dropdown,) + self.components[dropdown.description] = dropdown + return dropdown + + def addCheckBox(self, *args, **kwargs): + checkbox = BeakerxCheckbox(description=self.getDescription(args, kwargs)) + checkbox.value = getValue(kwargs, 'value', False) + self.children += (checkbox,) + self.components[checkbox.description] = checkbox + return checkbox + + def addCheckBoxes(self, *args, **kwargs): + layout = BeakerxHBox() + orientation = getValue(kwargs, 'orientation', EasyForm.VERTICAL) + if orientation == EasyForm.HORIZONTAL: + + box = BeakerxHBox() + else: + box = BeakerxVBox() + checkbox = BeakerxCheckboxGroup() + + for checkBoxItem in self.getOptions(args, kwargs): + children = BeakerxCheckbox(description=checkBoxItem) + checkbox.addChildren(children) + box.children += (children,) + + layout.children += (BeakerxLabel(value=self.getDescription(args, kwargs)), box,) + self.children += (layout,) + self.components[self.getDescription(args, kwargs)] = checkbox + return layout + + def addRadioButtons(self, *args, **kwargs): + orientation = getValue(kwargs, 'orientation', EasyForm.VERTICAL) + radio_buttons = RadioButtons(options=self.getOptions(args, kwargs), + description=self.getDescription(args, + kwargs)) + radio_buttons.index = None + if orientation == EasyForm.VERTICAL: + self.children += (radio_buttons,) + else: + box = BeakerxHBox() + box.children += (radio_buttons,) + self.children += (box,) + self.components[radio_buttons.description] = radio_buttons + return radio_buttons + + def addWidget(self, name, widget): + EasyFormComponent.add_interface_to(widget) + self.children += (widget,) + self.components[name] = widget + return widget + + def __iter__(self): + return iter(self.components) + + def __getitem__(self, key): + return self.get(key) + + def __setitem__(self, key, value): + self.put(key, value) + + def get(self, key): + if key in self.components: + return self.components[key].value + else: + return "" + + def put(self, key, value): + self.components[key].set_value(value) + + @staticmethod + def getDescription(args, kwargs): + if len(args) > 0: + return args[0] + else: + return getValue(kwargs, 'description', "") + + @staticmethod + def getOptions(args, kwargs): + if len(args) > 1: + return args[1][:] + else: + return getValue(kwargs, 'options', []) diff --git a/beakerx_widgets/beakerx_widgets/forms/tests/__init__.py b/beakerx_widgets/beakerx_widgets/forms/tests/__init__.py new file mode 100644 index 0000000..a10f022 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/forms/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2018 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/beakerx_widgets/beakerx_widgets/forms/tests/test_easyforms.py b/beakerx_widgets/beakerx_widgets/forms/tests/test_easyforms.py new file mode 100644 index 0000000..1c463b1 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/forms/tests/test_easyforms.py @@ -0,0 +1,109 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import unittest +from unittest.mock import MagicMock + +from beakerx_base import BeakerxText, BeakerxPassword, BeakerxTextArea, BeakerxButton +from beakerx_widgets.forms.easyforms import EasyForm + + +class TestEasyForms(unittest.TestCase): + + def test_easyforms(self): + # given + # when + ef = EasyForm() + # then + self.assertEqual(ef.easyFormName, "") + self.assertEqual(len(ef.children), 0) + self.assertEqual(len(ef.components), 0) + + def test_easyform_name(self): + # given + # when + ef = EasyForm("Hello EasyForm!") + # then + self.assertEqual(ef.easyFormName, "Hello EasyForm!") + + def test_easyform_add_text_field(self): + # given + ef = EasyForm() + # when + ef.addTextField('first', width=10) + ef['first'] = 'First' + # then + self.assertEqual(len(ef.children), 1) + self.assertIsInstance(ef.children[0], BeakerxText) + self.assertIn('first', ef.components) + self.assertEqual(ef.components['first'].value, 'First') + self.assertEqual(ef.components['first'].description, 'first') + self.assertEqual(ef.components['first'].size, 10) + + def test_easyform_add_password_field(self): + # given + ef = EasyForm() + # when + ef.addPasswordField("Password Field", width=10) + # then + self.assertEqual(len(ef.children), 1) + self.assertIsInstance(ef.children[0], BeakerxPassword) + self.assertIn('Password Field', ef.components) + p = ef.components['Password Field'] + self.assertEqual(p.description, 'Password Field') + self.assertEqual(p.value, '') + self.assertEqual(p.size, 10) + + def test_easyform_add_text_area(self): + # given + ef = EasyForm() + # when + ef.addTextArea("Text Area", width=10, height=5) + # then + self.assertEqual(len(ef.children), 1) + self.assertIsInstance(ef.children[0], BeakerxTextArea) + self.assertIn('Text Area', ef.components) + ta = ef.components['Text Area'] + self.assertEqual(ta.description, 'Text Area') + self.assertEqual(ta.value, '') + self.assertEqual(ta.placeholder, '') + self.assertEqual(ta.cols, 10) + self.assertEqual(ta.rows, 5) + + def test_easyform_add_button(self): + # given + ef = EasyForm() + # when + b = ef.addButton('OK', tag='tag') + b.actionPerformed = MagicMock() + # then + self.assertEqual(len(ef.children), 1) + self.assertEqual(len(ef.components), 0) + self.assertIsInstance(ef.children[0], BeakerxButton) + b = ef.children[0] + self.assertEqual(b.tag, 'tag') + self.assertFalse(b.actionPerformed.called) + b.click() + self.assertTrue(b.actionPerformed.called) + + def test_easyform_add_list(self): + # given + ef = EasyForm() + # when + l1 = ef.addList('List 1', ["a", "b", "c"]) + l2 = ef.addList('List 2', ["a", "b", "c"], multi=False) + l3 = ef.addList('List 3', ["a", "b", "c"], rows=2) + # then + print(ef.components.keys()) + self.assertEqual(len(ef.children), 3) + print((l1, l2, l3)) diff --git a/beakerx_widgets/beakerx_widgets/handlers.py b/beakerx_widgets/beakerx_widgets/handlers.py new file mode 100644 index 0000000..e34789a --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/handlers.py @@ -0,0 +1,165 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import os + +import beakerx_widgets +import tornado +import zmq +from notebook.base.handlers import APIHandler, IPythonHandler +from notebook.utils import url_path_join +from tornado import web, gen +from tornado.simple_httpclient import HTTPStreamClosedError + +from .beakerx_autotranslation_server import start_autotranslation_server +from .environment import EnvironmentSettings + + +class BeakerxRestHandler(APIHandler): + + def data_received(self, chunk): + pass + + @web.authenticated + @gen.coroutine + def post(self): + + data = tornado.escape.json_decode(self.request.body) + content = json.dumps(data) + params = json.loads(content) + + type = params.get('type') + url = params['url'] + if type == "python": + context = zmq.Context() + socket = context.socket(zmq.REQ) + socket.connect(url) + socket.send_string(content) + response = socket.recv() + self.finish(response) + socket.close() + context.destroy() + else: + req = tornado.httpclient.HTTPRequest( + url=url, + method=self.request.method, + body=self.request.body, + headers=self.request.headers, + follow_redirects=False, + allow_nonstandard_methods=True + ) + client = tornado.httpclient.AsyncHTTPClient() + try: + res = yield client.fetch(req) + self.finish(res.body) + except Exception as e: + raise web.HTTPError(500, 'Internal server error:\n' + str(e)) + + +class SparkMetricsExecutorsHandler(APIHandler): + def data_received(self, chunk): + pass + + @web.authenticated + @gen.coroutine + def get(self): + + app_id = self.get_argument('sparkAppId', None) + ui_web_url = self.get_argument('sparkUiWebUrl', None) + + url = ui_web_url + "/api/v1/applications/" + app_id + "/allexecutors" + req = tornado.httpclient.HTTPRequest( + url=url, + method=self.request.method, + body=self.request.body, + headers=self.request.headers, + follow_redirects=False, + allow_nonstandard_methods=True + ) + + client = tornado.httpclient.AsyncHTTPClient() + try: + res = yield client.fetch(req) + self.finish(res.body) + except ConnectionRefusedError as cre: + pass # spark was stopped + except HTTPStreamClosedError as hsce: + pass # spark was stopped + except Exception as ex: + raise web.HTTPError(500, 'Internal server error:\n' + str(ex)) + + +class SettingsHandler(APIHandler): + def data_received(self, chunk): + pass + + @staticmethod + def _read_property(): + return EnvironmentSettings.read_setting_from_file() + + @web.authenticated + def get(self): + self.finish(SettingsHandler._read_property()) + + @web.authenticated + def post(self): + data = tornado.escape.json_decode(self.request.body) + + EnvironmentSettings.save_setting_to_file(json.dumps(data)) + + self.finish(json.dumps(SettingsHandler._read_property())) + + +class VersionHandler(APIHandler): + def data_received(self, chunk): + pass + + @web.authenticated + def get(self): + data = {'version': beakerx_widgets.__version__} + self.finish(json.dumps(data)) + + +class JavaDoc(web.StaticFileHandler, IPythonHandler): + def initialize(self): + beakerx_path = os.path.dirname(beakerx_widgets.__file__) + web.StaticFileHandler.initialize(self, path=os.path.join(beakerx_path, 'javadoc')) + + @web.authenticated + def get(self, path): + self.set_header('Content-Type', 'text/html') + return web.StaticFileHandler.get(self, path) + + +def load_jupyter_server_extension(nbapp): + start_autotranslation_server() + + web_app = nbapp.web_app + host_pattern = '.*$' + settings_route_pattern = url_path_join(web_app.settings['base_url'], '/beakerx_widgets', '/settings') + spark_metrics_executors_route_pattern = url_path_join(web_app.settings['base_url'], '/beakerx_widgets', + '/sparkmetrics/executors') + version_route_pattern = url_path_join(web_app.settings['base_url'], '/beakerx_widgets', '/version') + javadoc_route_pattern = url_path_join(web_app.settings['base_url'], '/static', '/javadoc/(.*)') + javadoc_lab_route_pattern = url_path_join(web_app.settings['base_url'], '/javadoc/(.*)') + beakerx__rest_route_pattern = url_path_join(web_app.settings['base_url'], '/beakerx_widgets', '/rest') + + web_app.add_handlers(host_pattern, [(settings_route_pattern, SettingsHandler)]) + web_app.add_handlers(host_pattern, [(spark_metrics_executors_route_pattern, SparkMetricsExecutorsHandler)]) + web_app.add_handlers(host_pattern, [(version_route_pattern, VersionHandler)]) + web_app.add_handlers(host_pattern, [(javadoc_route_pattern, JavaDoc)]) + web_app.add_handlers(host_pattern, [(javadoc_lab_route_pattern, JavaDoc)]) + web_app.add_handlers(host_pattern, [(beakerx__rest_route_pattern, BeakerxRestHandler)]) + nbapp.log.info("[beakerx_widgets] enabled") diff --git a/beakerx_widgets/beakerx_widgets/install.py b/beakerx_widgets/beakerx_widgets/install.py new file mode 100644 index 0000000..c544ba3 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/install.py @@ -0,0 +1,200 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Installs BeakerX Widgets into a Jupyter and Python environment.''' + +import argparse +import json +import os +import pkg_resources +import shutil +import subprocess +import sys +import pathlib + +from jupyter_core import paths +from traitlets.config.manager import BaseJSONConfigManager +from distutils import log + +def _base_classpath_for(kernel): + return pkg_resources.resource_filename( + 'beakerx', os.path.join('kernel', kernel)) + + +def _classpath_for(kernel): + return pkg_resources.resource_filename( + 'beakerx', os.path.join('kernel', kernel, 'lib', '*')) + + +def _uninstall_nbextension(): + subprocess.check_call(["jupyter", "nbextension", "disable", "beakerx_widgets", "--py", "--sys-prefix"]) + subprocess.check_call(["jupyter", "nbextension", "uninstall", "beakerx_widgets", "--py", "--sys-prefix"]) + subprocess.check_call(["jupyter", "serverextension", "disable", "beakerx_widgets", "--py", "--sys-prefix"]) + + +def _install_nbextension(): + if sys.platform == 'win32': + subprocess.check_call(["jupyter", "nbextension", "install", "beakerx_widgets", "--py", "--sys-prefix"]) + else: + subprocess.check_call(["jupyter", "nbextension", "install", "beakerx_widgets", "--py", "--symlink", "--sys-prefix"]) + + subprocess.check_call(["jupyter", "nbextension", "enable", "beakerx_widgets", "--py", "--sys-prefix"]) + + subprocess.check_call(["jupyter", "serverextension", "enable", "beakerx_widgets", "--py", "--sys-prefix"]) + + +def _install_labextensions(lab): + if lab: + subprocess.check_call(["jupyter", "labextension", "install", "@jupyter-widgets/jupyterlab-manager"]) + subprocess.check_call(["jupyter", "labextension", "install", "beakerx-jupyterlab"]) + + +def _uninstall_labextensions(lab): + if lab: + subprocess.check_call(["jupyter", "labextension", "uninstall", "beakerx-jupyterlab"]) + subprocess.check_call(["jupyter", "labextension", "uninstall", "@jupyter-widgets/jupyterlab-manager"]) + + +def _install_tabledisplay(lab): + if lab: + subprocess.check_call(["beakerx_tabledisplay", "install", "--lab"]) + else: + subprocess.check_call(["beakerx_tabledisplay", "install"]) + + +def _uninstall_tabledisplay(): + subprocess.check_call(["beakerx_tabledisplay", "uninstall"]) + + +def _copy_tree(src, dst): + if os.path.exists(dst): + shutil.rmtree(dst) + shutil.copytree(src, dst) + + +def _copy_icons(): + log.info("installing icons...") + # kernels = KernelSpecManager().find_kernel_specs() + # for kernel in _all_kernels(): + # dst_base = kernels.get(kernel) + # src_base = _base_classpath_for(kernel) + # shutil.copyfile(os.path.join(src_base, 'logo-32x32.png'), os.path.join(dst_base, 'logo-32x32.png')) + # shutil.copyfile(os.path.join(src_base, 'logo-64x64.png'), os.path.join(dst_base, 'logo-64x64.png')) + + +def _install_css(): + log.info("installing custom CSS...") + # resource = os.path.join('static', 'custom') + # src_base = pkg_resources.resource_filename('beakerx_widgets', resource) + # dst_base = pkg_resources.resource_filename('notebook', resource) + # _copy_tree(os.path.join(src_base, 'fonts'), os.path.join(dst_base, 'fonts')) + # shutil.copyfile(os.path.join(src_base, 'custom.css'), os.path.join(dst_base, 'custom.css')) + + +def _install_magics(): + log.info("installing groovy magic for python...") + dir_path = os.path.join(sys.prefix, 'etc', 'ipython') + os.makedirs(dir_path, exist_ok=True) + with open(os.path.join(dir_path, 'ipython_config.py'), 'w+') as ipython_config: + ipython_config.write("c = get_config()\n") + ipython_config.write("c.InteractiveShellApp.extensions = [" + "'beakerx_widgets.magics.kernel_magic',\n" + "'beakerx_widgets.magics.groovy_magic',\n" + "'beakerx_widgets.magics.clojure_magic',\n" + "'beakerx_widgets.magics.sparkex_magic',\n" + "'beakerx_widgets.magics.kotlin_magic',\n" + "'beakerx_widgets.magics.scala_magic',\n" + "'beakerx_widgets.magics.sql_magic',\n" + "'beakerx_widgets.magics.java_magic',\n" + "'beakerx_widgets.magics.kernel_runner_magic'\n" + "]\n") + +def _set_conf_privileges(): + config_path = os.path.join(paths.jupyter_config_dir(), 'beakerx.json') + if pathlib.Path(config_path).exists(): + os.chmod(config_path, 0o600) + + +def _pretty(it): + return json.dumps(it, indent=2) + + +def _install_kernelspec_manager(prefix, disable=False): + CKSM = "beakerx_widgets.kernel_spec.BeakerXKernelSpec" + KSMC = "kernel_spec_class" + + action_prefix = "Dis" if disable else "En" + log.info("{}abling BeakerX server config...".format(action_prefix)) + path = os.path.join(prefix, "etc", "jupyter") + if not os.path.exists(path): + log.debug("Making directory {}...".format(path)) + os.makedirs(path) + cm = BaseJSONConfigManager(config_dir=path) + cfg = cm.get("jupyter_notebook_config") + log.debug("Existing config in {}...\n{}".format(path, _pretty(cfg))) + nb_app = cfg.setdefault("KernelSpecManager", {}) + if disable and nb_app.get(KSMC, None) == CKSM: + nb_app.pop(KSMC) + elif not disable: + nb_app.update({KSMC: CKSM}) + + log.debug("Writing config in {}...".format(path)) + cm.set("jupyter_notebook_config", cfg) + cfg = cm.get("jupyter_notebook_config") + + log.debug("Verifying config in {}...\n{}".format(path, _pretty(cfg))) + if disable: + assert KSMC not in cfg["KernelSpecManager"] + else: + assert cfg["KernelSpecManager"][KSMC] == CKSM + + log.info("{}abled BeakerX server config".format(action_prefix)) + + +def make_parser(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--prefix", + help="location of the environment to install into", + default=sys.prefix) + parser.add_argument("--disable", + help="Remove Beakerx extension", + action='store_true') + return parser + + +def _disable_beakerx(args): + _uninstall_nbextension() + _uninstall_labextensions(args.lab) + _install_kernelspec_manager(args.prefix, disable=True) + + +def _install_beakerx(args): + _install_nbextension() + _install_labextensions(args.lab) + _install_css() + _copy_icons() + _install_kernelspec_manager(args.prefix) + _install_magics() + _set_conf_privileges() + + +def install(args): + _install_beakerx(args) + +def uninstall(args): + _disable_beakerx(args) + + +if __name__ == "__main__": + install() diff --git a/beakerx_widgets/beakerx_widgets/kernel_spec.py b/beakerx_widgets/beakerx_widgets/kernel_spec.py new file mode 100644 index 0000000..2a79776 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/kernel_spec.py @@ -0,0 +1,24 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from jupyter_client.kernelspec import KernelSpec +from .environment import EnvironmentSettings + + +class BeakerXKernelSpec(KernelSpec): + def __init__(self, **kw): + super(BeakerXKernelSpec, self).__init__(**kw) + if self.argv[0] == 'java': + args = EnvironmentSettings.read_beakerx_env_settings() + self.argv[1:1] = args diff --git a/beakerx_widgets/beakerx_widgets/magics/__init__.py b/beakerx_widgets/beakerx_widgets/magics/__init__.py new file mode 100644 index 0000000..a10f022 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2018 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/beakerx_widgets/beakerx_widgets/magics/clojure_magic.py b/beakerx_widgets/beakerx_widgets/magics/clojure_magic.py new file mode 100644 index 0000000..578a6c9 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/clojure_magic.py @@ -0,0 +1,38 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC # +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from IPython import get_ipython +from IPython.core.magic import (magics_class, cell_magic) +from beakerx_widgets.magics.kernel_runner_magic import KernelRunnerMagic +from ipykernel.zmqshell import ZMQInteractiveShell + + +@magics_class +class ClojureMagics(KernelRunnerMagic): + + def __init__(self, shell): + super(ClojureMagics, self).__init__(shell) + + @cell_magic + def clojure(self, line, cell): + return super(ClojureMagics, self).kernel("clojure", cell) + + +def load_ipython_extension(ipython): + if isinstance(ipython, ZMQInteractiveShell): + ipython.register_magics(ClojureMagics) + + +if __name__ == '__main__': + ip = get_ipython() + ip.register_magics(ClojureMagics) diff --git a/beakerx_widgets/beakerx_widgets/magics/groovy_magic.py b/beakerx_widgets/beakerx_widgets/magics/groovy_magic.py new file mode 100644 index 0000000..2a42953 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/groovy_magic.py @@ -0,0 +1,39 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC # +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from IPython import get_ipython +from IPython.core.magic import (magics_class, cell_magic) +from beakerx_widgets.magics.kernel_runner_magic import KernelRunnerMagic +from ipykernel.zmqshell import ZMQInteractiveShell + + + +@magics_class +class GroovyMagics(KernelRunnerMagic): + + def __init__(self, shell): + super(GroovyMagics, self).__init__(shell) + + @cell_magic + def groovy(self, line, cell): + return super(GroovyMagics, self).kernel("groovy", cell) + + +def load_ipython_extension(ipython): + if isinstance(ipython, ZMQInteractiveShell): + ipython.register_magics(GroovyMagics) + + +if __name__ == '__main__': + ip = get_ipython() + ip.register_magics(GroovyMagics) diff --git a/beakerx_widgets/beakerx_widgets/magics/java_magic.py b/beakerx_widgets/beakerx_widgets/magics/java_magic.py new file mode 100644 index 0000000..ea17604 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/java_magic.py @@ -0,0 +1,39 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC # +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from IPython import get_ipython +from IPython.core.magic import (magics_class, cell_magic) +from beakerx_widgets.magics.kernel_runner_magic import KernelRunnerMagic +from ipykernel.zmqshell import ZMQInteractiveShell + + + +@magics_class +class JavaMagics(KernelRunnerMagic): + + def __init__(self, shell): + super(JavaMagics, self).__init__(shell) + + @cell_magic + def java(self, line, cell): + return super(JavaMagics, self).kernel("java", cell) + + +def load_ipython_extension(ipython): + if isinstance(ipython, ZMQInteractiveShell): + ipython.register_magics(JavaMagics) + + +if __name__ == '__main__': + ip = get_ipython() + ip.register_magics(JavaMagics) diff --git a/beakerx_widgets/beakerx_widgets/magics/jvm_kernel_magic.py b/beakerx_widgets/beakerx_widgets/magics/jvm_kernel_magic.py new file mode 100644 index 0000000..eb6b8b5 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/jvm_kernel_magic.py @@ -0,0 +1,113 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC # +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import sys +from queue import Empty + +from jupyter_client.kernelspec import NoSuchKernel +from jupyter_client.manager import KernelManager +from py4j.clientserver import ClientServer, JavaParameters, PythonParameters + + +class JVMKernelMagic: + + def __init__(self, kernel_name, context): + self.km = None + self.kc = None + self.comms = [] + self.kernel_name = kernel_name + self.context = context + self.start() + + def start(self): + self.km = KernelManager() + self.km.kernel_name = self.kernel_name + self.km.start_kernel(extra_arguments=[self.context]) + self.kc = self.km.client() + self.kc.start_channels() + self.kc.wait_for_ready() + + def stop_kernel(self): + self.kc.stop_channels() + self.km.shutdown_kernel(now=True) + + def run_cell(self, code): + if not self.km: + self.start() + self.kc.execute(code, allow_stdin=True) + + def get_shell_msg(self): + return self.kc.get_shell_msg() + + def get_iopub_msg(self): + try: + msg = self.kc.get_iopub_msg(timeout=1) + return msg + except Empty: + return None + + def pass_msg(self, msg_raw): + msg_json = json.loads(msg_raw) + content = msg_json['content'] + msg_type = msg_json['header']['msg_type'] + msg = self.kc.session.msg(msg_type, content) + self.kc.shell_channel.send(msg) + return None + + +class PythonEntryPoint(object): + + def __init__(self, kernel_name, context): + self.pm = JVMKernelMagic(kernel_name, context) + + def evaluate(self, code): + print('code for evaluate {}'.format(code)) + self.pm.run_cell(code) + return None + + def getShellMsg(self): + shellMsg = self.pm.get_shell_msg() + return json.dumps(shellMsg, default=str) + + def getIopubMsg(self): + iopubMsg = self.pm.get_iopub_msg() + return json.dumps(iopubMsg, default=str) + + def shutdownKernel(self): + self.pm.stop_kernel() + return None + + def sendMessage(self, msg_raw): + self.pm.pass_msg(msg_raw) + return None + + class Java: + implements = ["com.twosigma.beakerx.kernel.PythonEntryPoint"] + + +class Py4JServer: + def __init__(self, port, pyport, kernel_name, context): + try: + pep = PythonEntryPoint(kernel_name, context) + except NoSuchKernel: + sys.exit(2) + ClientServer( + java_parameters=JavaParameters(port=int(port)), + python_parameters=PythonParameters(port=int(pyport)), + python_server_entry_point=pep) + print('Py4j server is running') + + +if __name__ == '__main__': + Py4JServer(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]) diff --git a/beakerx_widgets/beakerx_widgets/magics/kernel_magic.py b/beakerx_widgets/beakerx_widgets/magics/kernel_magic.py new file mode 100644 index 0000000..0990ba0 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/kernel_magic.py @@ -0,0 +1,125 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC # +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import atexit +import base64 +import json +import logging +import os +from queue import Empty + +from IPython import get_ipython +from IPython.core.magic import (Magics, magics_class) +from ipykernel.zmqshell import ZMQInteractiveShell +from jupyter_client.manager import KernelManager + + +@magics_class +class KernelMagics(Magics): + _execution_count = 1 + + def stop_kernel(self): + self.kc.stop_channels() + self.km.shutdown_kernel(now=True) + + def __init__(self, shell): + super(KernelMagics, self).__init__(shell) + self.km = None + self.kc = None + self.comms = [] + + def start(self, kernel_name): + self.km = KernelManager() + self.km.kernel_name = kernel_name + self.km.start_kernel(extra_arguments=[self._context_base64()]) + atexit.register(self.stop_kernel) + self.kc = self.km.client() + self.kc.start_channels() + try: + self.kc.wait_for_ready() + print("{} started successfully\n".format(kernel_name.capitalize())) + except AttributeError: + self._wait_for_ready_backport() + + def run_cell(self, line, code): + if not self.km: + self.start() + self.kc.execute(code, allow_stdin=True) + reply = self.kc.get_shell_msg() + self._handle_iopub_messages() + + def _handle_iopub_messages(self): + while True: + try: + msg = self.kc.get_iopub_msg(timeout=1) + except Empty: + break + comm_id = msg['content'].get('comm_id') + if comm_id and comm_id not in self.comms: + self.comms.append(comm_id) + self.shell.kernel.session.send(self.shell.kernel.iopub_socket, msg['msg_type'], + msg['content'], + metadata=msg['metadata'], + parent=self.shell.kernel._parent_header, + ident=msg.get('comm_id'), + buffers=msg['buffers'], + ) + + def pass_message(self, msg_raw): + comm_id = msg_raw['content'].get('comm_id') + if comm_id in self.comms: + content = msg_raw['content'] + msg = self.kc.session.msg(msg_raw['msg_type'], content) + self.kc.shell_channel.send(msg) + self._handle_iopub_messages() + else: + self.log.warn("No such comm: %s", comm_id) + if self.log.isEnabledFor(logging.DEBUG): + # don't create the list of keys if debug messages aren't enabled + self.log.debug("Current comms: %s", list(self.comms.keys())) + + def _context_base64(self): + context_json = json.dumps({ + 'port': os.environ["BEAKERX_AUTOTRANSLATION_PORT"], + 'contextId': get_ipython().kernel.session.session, + }) + return base64.b64encode(context_json.encode('utf-8')).decode() + + +def comm_msg(stream, ident, msg): + content = msg['content'] + comm_id = content['comm_id'] + comm_manager = get_ipython().kernel.comm_manager + comm = comm_manager.comms.get(comm_id) + if comm is None: + magic_registry = comm_manager.kernel.shell.magics_manager.registry + for magic in magic_registry.values(): + if (hasattr(magic, 'pass_message') and comm_id in magic.comms): + try: + magic.pass_message(msg) + return + except Exception: + comm_manager.log.error('Exception in comm_msg for %s', comm_id, exc_info=True) + comm_manager.log.warn("No such comm: %s", comm_id) + if comm_manager.log.isEnabledFor(logging.DEBUG): + comm_manager.log.debug("Current comms: %s", list(comm_manager.comms.keys())) + else: + try: + comm.handle_msg(msg) + except Exception: + comm_manager.log.error('Exception in comm_msg for %s', comm_id, exc_info=True) + + +def load_ipython_extension(ipython): + if isinstance(ipython, ZMQInteractiveShell): + ipython.kernel.shell_handlers['comm_msg'] = comm_msg diff --git a/beakerx_widgets/beakerx_widgets/magics/kernel_runner_magic.py b/beakerx_widgets/beakerx_widgets/magics/kernel_runner_magic.py new file mode 100644 index 0000000..183599c --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/kernel_runner_magic.py @@ -0,0 +1,44 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC # +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from IPython import get_ipython +from IPython.core.magic import (magics_class, cell_magic, Magics) +from beakerx_widgets.magics.kernel_magic import KernelMagics +from ipykernel.zmqshell import ZMQInteractiveShell + + +@magics_class +class KernelRunnerMagic(Magics): + kernels = {} + + def __init__(self, shell): + super(KernelRunnerMagic, self).__init__(shell) + + @cell_magic + def kernel(self, line, cell): + if line not in self.kernels: + km = KernelMagics(self.shell) + km.start(line) + self.kernels[line] = km + + return self.kernels[line].run_cell(line, cell) + + +def load_ipython_extension(ipython): + if isinstance(ipython, ZMQInteractiveShell): + ipython.register_magics(KernelRunnerMagic) + + +if __name__ == '__main__': + ip = get_ipython() + ip.register_magics(KernelRunnerMagic) diff --git a/beakerx_widgets/beakerx_widgets/magics/kotlin_magic.py b/beakerx_widgets/beakerx_widgets/magics/kotlin_magic.py new file mode 100644 index 0000000..e6a70b2 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/kotlin_magic.py @@ -0,0 +1,38 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC # +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from IPython import get_ipython +from IPython.core.magic import (magics_class, cell_magic) +from beakerx_widgets.magics.kernel_runner_magic import KernelRunnerMagic +from ipykernel.zmqshell import ZMQInteractiveShell + + +@magics_class +class KotlinMagics(KernelRunnerMagic): + + def __init__(self, shell): + super(KotlinMagics, self).__init__(shell) + + @cell_magic + def kotlin(self, line, cell): + return super(KotlinMagics, self).kernel("kotlin", cell) + + +def load_ipython_extension(ipython): + if isinstance(ipython, ZMQInteractiveShell): + ipython.register_magics(KotlinMagics) + + +if __name__ == '__main__': + ip = get_ipython() + ip.register_magics(KotlinMagics) diff --git a/beakerx_widgets/beakerx_widgets/magics/scala_magic.py b/beakerx_widgets/beakerx_widgets/magics/scala_magic.py new file mode 100644 index 0000000..164df87 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/scala_magic.py @@ -0,0 +1,38 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC # +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from IPython import get_ipython +from IPython.core.magic import (magics_class, cell_magic) +from beakerx_widgets.magics.kernel_runner_magic import KernelRunnerMagic +from ipykernel.zmqshell import ZMQInteractiveShell + + +@magics_class +class ScalaMagics(KernelRunnerMagic): + + def __init__(self, shell): + super(ScalaMagics, self).__init__(shell) + + @cell_magic + def scala(self, line, cell): + return super(ScalaMagics, self).kernel("scala", cell) + + +def load_ipython_extension(ipython): + if isinstance(ipython, ZMQInteractiveShell): + ipython.register_magics(ScalaMagics) + + +if __name__ == '__main__': + ip = get_ipython() + ip.register_magics(ScalaMagics) diff --git a/beakerx_widgets/beakerx_widgets/magics/sparkex_magic.py b/beakerx_widgets/beakerx_widgets/magics/sparkex_magic.py new file mode 100644 index 0000000..cf310db --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/sparkex_magic.py @@ -0,0 +1,117 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC # +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +import ipywidgets as widgets +from IPython import get_ipython +from IPython.core import magic_arguments +from IPython.core.display import display +from IPython.core.magic import (Magics, magics_class, cell_magic) +from beakerx_widgets.spark.profile import Profile +from beakerx_widgets.spark.spark_engine import SparkEngine +from beakerx_widgets.spark.spark_factory import SparkFactory +from beakerx_widgets.spark.sparkex import IpythonManager, BeakerxSparkServerFactory +from pyspark.sql import SparkSession + + +class SingleSparkSession: + + def __init__(self) -> None: + self.active = False + + def is_active(self): + return self.active + + def activate(self): + self.active = True + + def inactivate(self): + self.active = False + + +class SparkSessionFactory: + def builder(self): + return SparkSession.builder + + +@magics_class +class SparkexMagics(Magics): + SINGLE_SPARK_SESSION = SingleSparkSession() + + @cell_magic + @magic_arguments.magic_arguments() + @magic_arguments.argument('--start', '-s', action='store_true', help='auto start') + @magic_arguments.argument('--noUI', '-nu', action='store_true', help='no UI') + @magic_arguments.argument('--yarn', '-yr', action='store_true', help='yarn') + def spark(self, line, cell): + self.clear_spark_session() + ipython = get_ipython() + result, err = self._runCellCode(cell, ipython) + if err is not None: + print(err, file=sys.stderr) + return + builder = result.result + ui_options = magic_arguments.parse_argstring(self.spark, line) + factory = self._create_spark_factory( + builder, + IpythonManager(ipython), + BeakerxSparkServerFactory(), + Profile(), + ui_options, + self._display_ui, + SparkexMagics.SINGLE_SPARK_SESSION) + result, err = factory.create_spark() + if err is not None: + print(err, file=sys.stderr) + elif result is not None: + print(result) + return + + def clear_spark_session(self): + SparkSession.builder._options = {} + + def _create_spark_factory(self, + builder, + ipython_manager, + server_factory, + profile, + ui_options, + display_func, + single_spark_session): + factory = SparkFactory(ui_options, + SparkEngine(builder, single_spark_session, SparkSessionFactory()), + ipython_manager, + server_factory, + profile, + display_func) + return factory + + def _display_ui(self, spark_ui): + return display(spark_ui) + + def _runCellCode(self, cell, ipython): + out = widgets.Output(layout={'border': '1px solid black'}) + with out: + result = ipython.run_cell(cell) + if isinstance(result.result, SparkSession.Builder): + return result, None + else: + if result.error_in_exec is not None: + return None, result.error_in_exec + else: + return None, "Spark magic command must return SparkSession.Builder object" + + +def load_ipython_extension(ipython): + ipython.register_magics(SparkexMagics) diff --git a/beakerx_widgets/beakerx_widgets/magics/sql_magic.py b/beakerx_widgets/beakerx_widgets/magics/sql_magic.py new file mode 100644 index 0000000..bcec8e7 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/sql_magic.py @@ -0,0 +1,42 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC # +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from IPython import get_ipython +from IPython.core.magic import (magics_class, cell_magic) +from ipykernel.zmqshell import ZMQInteractiveShell + +from beakerx_widgets.magics.kernel_runner_magic import KernelRunnerMagic + + +@magics_class +class SqlMagics(KernelRunnerMagic): + + def __init__(self, shell): + super(SqlMagics, self).__init__(shell) + + def start(self): + super(SqlMagics, self).start('SQL') + + @cell_magic + def sql(self, line, cell): + return super(SqlMagics, self).kernel("SQL", cell) + + +def load_ipython_extension(ipython): + if isinstance(ipython, ZMQInteractiveShell): + ipython.register_magics(SqlMagics) + + +if __name__ == '__main__': + ip = get_ipython() + ip.register_magics(SqlMagics) diff --git a/beakerx_widgets/beakerx_widgets/magics/tests/__init__.py b/beakerx_widgets/beakerx_widgets/magics/tests/__init__.py new file mode 100644 index 0000000..a10f022 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2018 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/beakerx_widgets/beakerx_widgets/magics/tests/mocks.py b/beakerx_widgets/beakerx_widgets/magics/tests/mocks.py new file mode 100644 index 0000000..cc10017 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/tests/mocks.py @@ -0,0 +1,57 @@ +# Copyright 2018 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from beakerx_widgets.magics.sparkex_magic import SingleSparkSession +from beakerx_widgets.spark.spark_engine import SparkEngine + + +class SingleSparkSessionMock(SingleSparkSession): + + def __init__(self) -> None: + super().__init__() + + +def display_func_mock(spark_ui): + pass + + +class SparkEngineMock(SparkEngine): + def getOrCreate(self): + pass + + def spark_app_id(self): + pass + + def get_ui_web_url(self): + return 'SparkUiWebUrl1' + + def stop(self): + pass + + def configure_listeners(self, engine, server): + pass + + def get_user_spark_config(self): + return {} + + +class BeakerxSparkServerFactoryMock: + def run_new_instance(self, spark_session): + pass + + +class IpythonManagerMock: + + def configure(self, spark_context): + pass diff --git a/beakerx_widgets/beakerx_widgets/magics/tests/test_spark_auto_connect.py b/beakerx_widgets/beakerx_widgets/magics/tests/test_spark_auto_connect.py new file mode 100644 index 0000000..07f1047 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/magics/tests/test_spark_auto_connect.py @@ -0,0 +1,77 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import unittest + +from IPython import InteractiveShell +from beakerx_widgets.magics.sparkex_magic import SparkexMagics +from beakerx_widgets.magics.tests.mocks import SparkEngineMock, IpythonManagerMock, BeakerxSparkServerFactoryMock, \ + display_func_mock +from beakerx_widgets.spark.spark_factory import SparkFactory +from beakerx_widgets.spark.sparkex import SparkUI +from beakerx_widgets.spark.tests.mocks import CommMock, SparkSessionFactoryMock + + +class TestSparkUI(unittest.TestCase): + spark_widget = None + + def test_auto_connect_spark_default(self): + result = self.spark_magic() + self.assertTrue(result is None) + self.assertFalse(TestSparkUI.spark_widget.engine.auto_start) + + def test_auto_connect_spark(self): + result = self.spark_magic("--start") + self.assertTrue(result is None) + self.assertTrue(TestSparkUI.spark_widget.engine.auto_start) + + def spark_magic(self, option=""): + ip = InteractiveShell.instance() + ip.register_magics(SparkexMagicsForTests) + code = " from pyspark.sql import SparkSession" \ + " \n SparkSession.builder.appName('abc')" + result = ip.run_cell_magic("spark", option, code) + return result + + +class SparkexMagicsForTests(SparkexMagics): + + def _create_spark_factory(self, builder, ipython_manager, server_factory, profile, options, display_func, + single_spark_session): + factory = SparkFactoryMock(options, + SparkEngineMock(builder, single_spark_session, SparkSessionFactoryMock()), + IpythonManagerMock(), + BeakerxSparkServerFactoryMock(), + profile, + display_func_mock) + return factory + + +class SparkFactoryMock(SparkFactory): + + def _create_spark_ui(self): + spark_widget = SparkUIMock(self.spark_engine, + self.ipythonManager, + self.server_factory, + self.profile, + CommMock()) + TestSparkUI.spark_widget = spark_widget + return spark_widget + + +class SparkUIMock(SparkUI): + def _get_init_profiles(self): + profiles = json.loads('[]') + return profiles, "" diff --git a/beakerx_widgets/beakerx_widgets/object/__init__.py b/beakerx_widgets/beakerx_widgets/object/__init__.py new file mode 100644 index 0000000..ddba819 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/object/__init__.py @@ -0,0 +1,17 @@ +# Copyright 2014 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ..runtime import BeakerX + +beakerx = BeakerX() diff --git a/beakerx_widgets/beakerx_widgets/outputs/__init__.py b/beakerx_widgets/beakerx_widgets/outputs/__init__.py new file mode 100644 index 0000000..895a1dd --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/outputs/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .outputcontainer import * \ No newline at end of file diff --git a/beakerx_widgets/beakerx_widgets/outputs/outputcontainer.py b/beakerx_widgets/beakerx_widgets/outputs/outputcontainer.py new file mode 100644 index 0000000..3556c4d --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/outputs/outputcontainer.py @@ -0,0 +1,161 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from beakerx_base import BeakerxHTML, BeakerxHTMLPre, CyclingDisplayBox, Tab, BeakerxHBox, GridView +try: + from beakerx_tabledisplay import TableDisplay +except ModuleNotFoundError: + TableDisplay = None + +from ipywidgets import Widget +from abc import abstractmethod +from pandas import DataFrame + + +class OutputContainerLayoutManager: + borderDisplayed = True + + def __init__(self): + pass + + def setBorderDisplayed(self, borderDisplayed): + self.borderDisplayed = borderDisplayed + + @abstractmethod + def display(self, output_container): + return NotImplemented + + def getWidgets(self, container): + collection = [] + for item in container.items: + collection.append(self.toWidget(item)) + return collection + + def toWidget(self, item): + if item is None: + return self.createHTMLPre("None") + + if isinstance(item, DataFrame): + if TableDisplay is not None: + return TableDisplay(item) + else: + return self.createHTMLPre("You need beakerx_tabledisplay to display this") + + if isinstance(item, Widget): + return item + + return self.createHTMLPre(item.__str__()) + + def createHTML(self, value): + label = BeakerxHTML() + label.value = value + return label + + def createHTMLPre(self, value): + pre = BeakerxHTMLPre() + pre.value = value + return pre + + +class SimpleLayoutManager(OutputContainerLayoutManager): + def __init__(self): + super(SimpleLayoutManager, self).__init__() + + def display(self, output_container): + for widget in self.getWidgets(output_container): + widget._ipython_display_() + + +class TabbedOutputContainerLayoutManager(OutputContainerLayoutManager): + def __init__(self): + super(TabbedOutputContainerLayoutManager, self).__init__() + + def display(self, output_container): + widgets = self.getWidgets(output_container) + tab = Tab(widgets, output_container.labels) + tab._ipython_display_() + + +class CyclingOutputContainerLayoutManager(OutputContainerLayoutManager): + period = 5000 + + def __init__(self): + super(CyclingOutputContainerLayoutManager, self).__init__() + + def setPeriod(self, miliseconds): + self.period = miliseconds + + def display(self, output_container): + c = CyclingDisplayBox(self.getWidgets(output_container)) + c.setPeriod(self.period) + c._ipython_display_() + + +class AbstractGridLayoutManager(OutputContainerLayoutManager): + columns = 0 + + def __init__(self, columns): + super(AbstractGridLayoutManager, self).__init__() + self.columns = columns + + +class GridOutputContainerLayoutManager(AbstractGridLayoutManager): + def __init__(self, columns=2): + super(GridOutputContainerLayoutManager, self).__init__(columns) + + def display(self, output_container): + layout = output_container.layoutManager + columns = layout.columns + + items = self.getWidgets(output_container) + rows = [] + for itemIndex in range(0, len(items), columns): + rows.append(BeakerxHBox(self.createRow(columns, items, itemIndex))) + + grid_view = GridView(rows) + grid_view._ipython_display_() + + def createRow(self, columns, items, itemIndex): + row_items = [] + for c in range(itemIndex, itemIndex + columns): + if c < len(items): + row_items.append(items[c]) + else: + row_items.append(self.emptyItem()) + return row_items + + def emptyItem(self): + return BeakerxHBox() + + +class OutputContainer: + layoutManager = SimpleLayoutManager() + items = [] + labels = [] + + def __init__(self, **kwargs): + super(OutputContainer, self).__init__(**kwargs) + self.items = [] + self.labels = [] + + def addItem(self, item, label=None): + self.items.append(item) + self.labels.append(label) + + def setLayoutManager(self, layoutManager): + if layoutManager is not None: + self.layoutManager = layoutManager + + def _ipython_display_(self): + self.layoutManager.display(self) diff --git a/beakerx_widgets/beakerx_widgets/plots/__init__.py b/beakerx_widgets/beakerx_widgets/plots/__init__.py new file mode 100644 index 0000000..0a8184d --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .chart import * +from .legend import * +from .plotitem import * +from .plotitem_treemap import * diff --git a/beakerx_widgets/beakerx_widgets/plots/chart.py b/beakerx_widgets/beakerx_widgets/plots/chart.py new file mode 100644 index 0000000..a17bae2 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/chart.py @@ -0,0 +1,405 @@ +# Copyright 2014 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +from enum import Enum + +from beakerx_base import getValue, Color, date_time_2_millis, BeakerxDOMWidget +from ipykernel.comm import Comm +from pandas import DataFrame, RangeIndex +from traitlets import Dict, Unicode + +from .chart_models import XYChart, CategoryChart, HeatMapChart, HistogramChart, TreeMapChart, CombinedChart, \ + LegendLayout, LegendPosition +from .plotitem import GradientColor, Points, Line + + +class Plot(BeakerxDOMWidget): + _view_name = Unicode('PlotView').tag(sync=True) + _model_name = Unicode('PlotModel').tag(sync=True) + _view_module = Unicode('beakerx_widgets').tag(sync=True) + _model_module = Unicode('beakerx_widgets').tag(sync=True) + model = Dict().tag(sync=True) + + def __init__(self, **kwargs): + super(Plot, self).__init__() + self.chart = XYChart(**kwargs) + self.model = self.chart.transform() + self.on_msg(self._handle_msg) + self.details = GraphicsActionObject(None, {}) + + def add(self, item): + self.chart.add(item) + self.model = self.chart.transform() + return self + + def getYAxes(self): + return self.chart.rangeAxes + + def setShowLegend(self, show): + self.chart.show_legend = show + self.model = self.chart.transform() + return self + + def setXBound(self, *args): + if len(args) == 1 and isinstance(args[0], list): + arg_list = args[0] + if len(arg_list) == 2: + self.chart.setXBound(arg_list[0], arg_list[1]) + else: + raise ValueError('to set the x bound, the list needs to be of size=2.') + else: + self.chart.setXBound(args[0], args[1]) + self.model = self.chart.transform() + return self + + def setYBound(self, *args): + if len(args) == 1 and isinstance(args[0], list): + arg_list = args[0] + if len(arg_list) == 2: + self.chart.setYBound(arg_list[0], arg_list[1]) + else: + raise ValueError('to set the y bound, the list needs to be of size=2.') + else: + self.chart.setYBound(args[0], args[1]) + self.model = self.chart.transform() + return self + + def _ipython_display_(self, **kwargs): + self.model = self.chart.transform() + super(Plot, self)._ipython_display_(**kwargs) + + def _handle_msg(self, msg): + if 'content' in msg['content']['data']: + params = msg['content']['data']['content'] + graphics_object = None + for item in self.chart.graphics_list: + if item.uid == params['itemId']: + graphics_object = item + self.details = GraphicsActionObject(graphics_object, params['params']) + if params['event'] == 'onclick': + self._on_click_action(msg) + elif params['event'] == 'onkey': + self._on_key_action(msg) + elif params['event'] == 'actiondetails': + self._on_action_details(msg) + + def _on_click_action(self, msg): + params = msg['content']['data']['content'] + for item in self.chart.graphics_list: + if item.uid == params['itemId']: + item.fireClick(self.details) + self.model = self.chart.transform() + + def _on_key_action(self, msg): + params = msg['content']['data']['content'] + for item in self.chart.graphics_list: + if item.uid == params['itemId']: + item.fireKey(self.details, params['params']['key']) + self.model = self.chart.transform() + + def _on_action_details(self, msg): + params = msg['content']['data']['content'] + graphics_object = None + for item in self.chart.graphics_list: + if item.uid == params['itemId']: + graphics_object = item + action_type = params['params']['actionType'] + if action_type == 'onclick' or action_type == 'onkey': + self.details = GraphicsActionObject(graphics_object, params['params']) + arguments = dict(target_name='beakerx.tag.run') + comm = Comm(**arguments) + msg = {'runByTag': params['params']['tag']} + state = {'state': msg} + comm.send(data=state, buffers=[]) + + +class GraphicsActionObject: + def __init__(self, graphics_object, params): + self.graphics = graphics_object + self.key = params.get('key') + self.tag = params.get('tag') + self.index = params.get('index') + self.actionType = params.get('actionType') + + +class CategoryPlot(BeakerxDOMWidget): + _view_name = Unicode('PlotView').tag(sync=True) + _model_name = Unicode('PlotModel').tag(sync=True) + _view_module = Unicode('beakerx_widgets').tag(sync=True) + _model_module = Unicode('beakerx_widgets').tag(sync=True) + model = Dict().tag(sync=True) + + def __init__(self, **kwargs): + super(CategoryPlot, self).__init__() + self.chart = CategoryChart(**kwargs) + self.model = self.chart.transform() + + def add(self, item): + self.chart.add(item) + self.model = self.chart.transform() + return self + + def _ipython_display_(self, **kwargs): + self.model = self.chart.transform() + super(CategoryPlot, self)._ipython_display_(**kwargs) + + +class HeatMap(BeakerxDOMWidget): + _view_name = Unicode('PlotView').tag(sync=True) + _model_name = Unicode('PlotModel').tag(sync=True) + _view_module = Unicode('beakerx_widgets').tag(sync=True) + _model_module = Unicode('beakerx_widgets').tag(sync=True) + model = Dict().tag(sync=True) + + def __init__(self, rows_limit=10000, column_limit=100, **kwargs): + super(HeatMap, self).__init__() + if 'data' in kwargs: + data_from_kwargs = kwargs['data'] + if isinstance(data_from_kwargs, DataFrame): + kwargs['graphics'] = data_from_kwargs.values.tolist() + else: + kwargs['graphics'] = data_from_kwargs + if not 'xLowerMargin' in kwargs: + kwargs['xLowerMargin'] = 0.0 + if not 'yLowerMargin' in kwargs: + kwargs['yLowerMargin'] = 0.0 + if not 'yUpperMargin' in kwargs: + kwargs['yUpperMargin'] = 0.0 + if not 'xUpperMargin' in kwargs: + kwargs['xUpperMargin'] = 0.0 + if not 'legendLayout' in kwargs: + kwargs['legendLayout'] = LegendLayout.HORIZONTAL + if not 'legendPosition' in kwargs: + kwargs['legendPosition'] = LegendPosition.BOTTOM_RIGHT + self.rows_limit = rows_limit + self.column_limit = column_limit + self.chart = HeatMapChart(self.rows_limit, self.column_limit, **kwargs) + color = getValue(kwargs, 'color', + ["#FF780004", "#FFF15806", "#FFFFCE1F"]) + + if isinstance(color, GradientColor): + self.chart.color = color.color + else: + self.chart.color = color + + self.chart.type = 'HeatMap' + + self.model = self.chart.transform() + + +class Histogram(BeakerxDOMWidget): + class DisplayMode(Enum): + OVERLAP = 1 + STACK = 2 + SIDE_BY_SIDE = 3 + + _view_name = Unicode('PlotView').tag(sync=True) + _model_name = Unicode('PlotModel').tag(sync=True) + _view_module = Unicode('beakerx_widgets').tag(sync=True) + _model_module = Unicode('beakerx_widgets').tag(sync=True) + model = Dict().tag(sync=True) + + def __init__(self, **kwargs): + super(Histogram, self).__init__() + self.chart = HistogramChart(**kwargs) + data = getValue(kwargs, 'data', []) + if len(data) > 1 and isinstance(data[0], list): + for x in data: + self.chart.graphics_list.append(x) + else: + self.chart.graphics_list.append(data) + self.model = self.chart.transform() + + +class TreeMap(BeakerxDOMWidget): + _view_name = Unicode('PlotView').tag(sync=True) + _model_name = Unicode('PlotModel').tag(sync=True) + _view_module = Unicode('beakerx_widgets').tag(sync=True) + _model_module = Unicode('beakerx_widgets').tag(sync=True) + model = Dict().tag(sync=True) + + def __init__(self, **kwargs): + super(TreeMap, self).__init__() + self.chart = TreeMapChart(**kwargs) + self.model = self.chart.transform() + + def setColorProvider(self, provider): + self.chart.colorProvider = provider + self.model = self.chart.transform() + + +class TimePlot(Plot): + def __init__(self, **kwargs): + super(TimePlot, self).__init__(**kwargs) + self.chart.type = 'TimePlot' + + def getChartColors(self, columnNames, colors): + chartColors = [] + if colors is not None: + for i in range(len(columnNames)): + if i < len(colors): + chartColors.append(self.createChartColor(colors[i])) + return chartColors + + def createChartColor(self, color): + if isinstance(color, list): + try: + return Color(color[0], color[1], color[2]) + except Exception: + raise Exception("Color list too short") + else: + return color + + +class NanoPlot(TimePlot): + def __init__(self, **kwargs): + super(NanoPlot, self).__init__(**kwargs) + self.chart.type = 'NanoPlot' + + def add(self, item): + super(NanoPlot, self).add(item) + for l in self.chart.graphics_list: + convertedx = [] + convertedy = [] + for x in l.x: + convertedx.append(str(x)) + l.x = convertedx + for y in l.y: + convertedy.append(str(y)) + l.y = convertedy + self.model = self.chart.transform() + return self + + +class SimpleTimePlot(TimePlot): + def __init__(self, *args, **kwargs): + super(SimpleTimePlot, self).__init__(**kwargs) + self.chart.type = 'TimePlot' + self.use_tool_tip = True + self.show_legend = True + time_column_default = 'time' + displayNames = getValue(kwargs, 'displayNames') + displayLines = getValue(kwargs, 'displayLines', True) + displayPoints = getValue(kwargs, 'displayPoints', False) + colors = getValue(kwargs, 'colors') + + if len(args) > 0: + tableData = args[0] + else: + tableData = [] + + if len(args) == 2: + columnNames = args[1] + else: + columnNames = [] + + xs = [] + yss = [] + dataColumnsNames = [] + parse_x = True + + if isinstance(tableData, DataFrame): + if tableData.index.name is not None: + time_column_default = tableData.index.name + if not isinstance(tableData.index, RangeIndex): + parse_x = False + xs = tableData.index.to_numpy() + tableData = tableData.to_dict(orient='rows') + + timeColumn = getValue(kwargs, 'timeColumn', time_column_default) + self.chart.domain_axis_label = getValue(kwargs, 'xLabel', timeColumn) + if tableData is not None and columnNames is not None: + dataColumnsNames.extend(list(tableData[0])) + + for row in tableData: + if parse_x: + x = row[timeColumn] + x = date_time_2_millis(x) + xs.append(x) + + for idx in range(len(columnNames)): + column = columnNames[idx] + if (idx >= len(yss)): + yss.append([]) + + yss[idx].append(row[column]) + + colors = self.getChartColors(columnNames, colors) + + for i in range(len(yss)): + ys = yss[i] + if displayLines is True: + line = Line(x=xs, y=ys) + + if displayNames is not None and i < len(displayNames): + line.display_name = displayNames[i] + else: + line.display_name = columnNames[i] + + if i < len(colors): + line.color = colors[i] + + self.add(line) + + if displayPoints is True: + points = Points(x=xs, y=ys) + + if displayNames is not None and i < len(displayNames): + points.display_name = displayNames[i] + else: + points.display_name = columnNames[i] + + if i < len(colors): + points.color = colors[i] + + self.add(points) + + +class CombinedPlot(BeakerxDOMWidget): + _view_name = Unicode('PlotView').tag(sync=True) + _model_name = Unicode('PlotModel').tag(sync=True) + _view_module = Unicode('beakerx_widgets').tag(sync=True) + _model_module = Unicode('beakerx_widgets').tag(sync=True) + model = Dict().tag(sync=True) + + def __init__(self, **kwargs): + super(CombinedPlot, self).__init__() + self.chart = CombinedChart(**kwargs) + self.model = self.chart.transform() + + def add(self, item, weight): + if isinstance(item.chart, XYChart): + self.chart.plots.append(item.chart) + self.chart.weights.append(weight) + elif isinstance(item, list): + for elem in item: + self.chart.add(elem.chart, 1) + else: + raise Exception('CombinedPlot takes XYChart or List of XYChart') + + self.model = self.chart.transform() + return self + + +def parseJSON(out): + return json.loads(out, object_hook=transformBack) + + +def transformBack(obj): + if 'type' in obj: + res = eval(obj['type'])() + res.transformBack(obj) + return res + return obj diff --git a/beakerx_widgets/beakerx_widgets/plots/chart_models.py b/beakerx_widgets/beakerx_widgets/plots/chart_models.py new file mode 100644 index 0000000..7cf403b --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/chart_models.py @@ -0,0 +1,331 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy + +from beakerx_base import BaseObject, getValue, Color + +from .legend import LegendPosition, LegendLayout +from .plotitem import YAxis, Text, ConstantLine, ConstantBand, Graphics +from .plotitem_treemap import RandomColorProvider, ValueAccessor, Mode +from .tree_map_reducer import TreeMapReducer + + +class Chart(BaseObject): + def __init__(self, **kwargs): + super(Chart, self).__init__(**kwargs) + self.init_width = getValue(kwargs, 'initWidth', 640) + self.init_height = getValue(kwargs, 'initHeight', 480) + self.chart_title = getValue(kwargs, 'title') + self.show_legend = getValue(kwargs, 'showLegend') + self.use_tool_tip = getValue(kwargs, 'useToolTip', True) + self.legend_position = getValue(kwargs, 'legendPosition', LegendPosition.TOP_RIGHT) + self.legend_layout = getValue(kwargs, 'legendLayout', LegendLayout.VERTICAL) + self.type = "Plot" + + def transform(self): + self_copy = copy.copy(self) + self_copy.legend_position = {'type': 'LegendPosition', 'position': self_copy.legend_position} + return super(Chart, self_copy).transform() + + +class AbstractChart(Chart): + def __init__(self, **kwargs): + super(AbstractChart, self).__init__(**kwargs) + self.rangeAxes = getValue(kwargs, 'yAxes', []) + if len(self.rangeAxes) == 0: + self.rangeAxes.append(YAxis(**kwargs)) + self.domain_axis_label = getValue(kwargs, 'xLabel') + self.y_label = getValue(kwargs, 'yLabel') + self.x_lower_margin = getValue(kwargs, 'xLowerMargin', 0.05) + self.x_upper_margin = getValue(kwargs, 'xUpperMargin', 0.05) + self.y_auto_range = getValue(kwargs, 'yAutoRange') + self.y_auto_range_includes_zero = getValue(kwargs, + 'yAutoRangeIncludesZero') + self.y_lower_margin = getValue(kwargs, 'yLowerMargin') + self.y_upper_margin = getValue(kwargs, 'yUpperMargin') + self.y_lower_bound = getValue(kwargs, 'yLowerBound') + self.y_upper_bound = getValue(kwargs, 'yUpperBound') + self.log_y = getValue(kwargs, 'logY', False) + self.omit_checkboxes = getValue(kwargs, 'omitCheckboxes', False) + self.crosshair = getValue(kwargs, 'crosshair') + self.timezone = getValue(kwargs, 'timeZone') + self.auto_zoom = getValue(kwargs, 'autoZoom') + + +class XYChart(AbstractChart): + TOO_MANY_ROWS = "tooManyRows" + TOTAL_NUMBER_OF_POINTS = "totalNumberOfPoints" + NUMBER_OF_POINTS_TO_DISPLAY = "numberOfPointsToDisplay" + ROWS_LIMIT_ITEMS = "rowsLimitItems" + + def __init__(self, **kwargs): + super(XYChart, self).__init__(**kwargs) + self.graphics_list = getValue(kwargs, 'graphics', []) + self.constant_lines = getValue(kwargs, 'constantLines', []) + self.constant_bands = getValue(kwargs, 'constantBands', []) + self.texts = getValue(kwargs, 'texts', []) + self.x_auto_range = getValue(kwargs, 'xAutoRange', True) + self.x_lower_bound = getValue(kwargs, 'xLowerBound', 0) + self.x_upper_bound = getValue(kwargs, 'xUpperBound', 0) + self.log_x = getValue(kwargs, 'logX', False) + self.x_log_base = getValue(kwargs, 'xLogBase', 10) + self.lodThreshold = getValue(kwargs, 'lodThreshold') + + def add(self, item): + if isinstance(item, YAxis): + self.rangeAxes.append(item) + elif isinstance(item, Text): + self.texts.append(item) + elif isinstance(item, ConstantLine): + self.constant_lines.append(item) + elif isinstance(item, ConstantBand): + self.constant_bands.append(item) + elif isinstance(item, Graphics): + self.graphics_list.append(item) + elif isinstance(item, list): + for elem in item: + self.add(elem) + return self + + def setYBound(self, lower, upper): + self.y_lower_bound = lower + self.y_upper_bound = upper + self.rangeAxes[0].setBound(lower, upper) + return self + + def setXBound(self, lower, upper): + self.x_auto_range = False + self.x_lower_bound = lower + self.x_upper_bound = upper + return self + + +class HeatMapChart(XYChart): + ROWS_LIMIT = 10000 + COLUMN_LIMIT = 100 + + def __init__(self, rows_limit, column_limit, **kwargs): + super(HeatMapChart, self).__init__(**kwargs) + self.rows_limit = rows_limit + self.column_limit = column_limit + + @staticmethod + def total_points(listOfData): + return sum(map(lambda x: len(x), listOfData)) + + @staticmethod + def find_step_for_column(row): + step = 2 + while (int(len(row) / step)) > HeatMapChart.COLUMN_LIMIT: + step += 1 + return step + + @staticmethod + def limit_column_in_row(row): + if len(row) > HeatMapChart.COLUMN_LIMIT: + step = HeatMapChart.find_step_for_column(row) + limited_row = list(map(lambda index: row[index], + filter(lambda s: s % step == 0, + [index for index in range(len(row))]))) + return limited_row + else: + return row + + @staticmethod + def limit_elements_in_row(listOfData): + return list(map(HeatMapChart.limit_column_in_row, listOfData)) + + @staticmethod + def limit_rows(listOfData): + step = HeatMapChart.find_step_for_column(listOfData) + limited_row = list(map(lambda index: listOfData[index], + filter(lambda s: s % step == 0, + [index for index in range(len(listOfData))]))) + return limited_row + + @staticmethod + def limit_Heatmap(listOfData): + limited_elements_in_row = HeatMapChart.limit_elements_in_row(listOfData) + total_points = HeatMapChart.total_points(limited_elements_in_row) + too_many_rows = total_points > HeatMapChart.ROWS_LIMIT + if too_many_rows: + return HeatMapChart.limit_rows(limited_elements_in_row) + return limited_elements_in_row + + def transform(self): + self_copy = copy.copy(self) + self_copy.totalNumberOfPoints = self.total_points(self_copy.graphics_list) + self_copy.rowsLimitItems = self.rows_limit + too_many_points = self_copy.totalNumberOfPoints > self.rows_limit + + if too_many_points: + limited_heat_map_data = self.limit_Heatmap(self_copy.graphics_list) + self_copy.graphics_list = limited_heat_map_data + self_copy.numberOfPointsToDisplay = self.total_points(self_copy.graphics_list) + + self_copy.numberOfPointsToDisplay = self.total_points(self_copy.graphics_list) + self_copy.tooManyRows = too_many_points + return super(HeatMapChart, self_copy).transform() + + +class HistogramChart(XYChart): + ROWS_LIMIT = 1000000 + ROWS_LIMIT_T0_INDEX = 10000 + + def __init__(self, **kwargs): + self.log = getValue(kwargs, 'log', False) + if self.log: + kwargs['logY'] = True + + super(HistogramChart, self).__init__(**kwargs) + self.type = 'Histogram' + self.bin_count = getValue(kwargs, 'binCount') + self.cumulative = getValue(kwargs, 'cumulative', False) + self.normed = getValue(kwargs, 'normed', False) + + self.range_min = getValue(kwargs, 'rangeMin') + self.range_max = getValue(kwargs, 'rangeMax') + self.names = getValue(kwargs, 'names') + self.displayMode = getValue(kwargs, 'displayMode') + + color = getValue(kwargs, 'color') + if color is not None: + if isinstance(color, Color): + self.colors = [] + self.colors.append(color) + else: + self.colors = color + + @staticmethod + def limit_points(x): + if len(x) >= HistogramChart.ROWS_LIMIT: + return x[0:HistogramChart.ROWS_LIMIT_T0_INDEX] + return x + + @staticmethod + def total_number(listOfData): + return max(list(map(lambda x: len(x), listOfData))) + + def transform(self): + self_copy = copy.copy(self) + self_copy.totalNumberOfPoints = HistogramChart.total_number(self_copy.graphics_list) + self_copy.tooManyRows = self_copy.totalNumberOfPoints >= HistogramChart.ROWS_LIMIT + self_copy.rowsLimitItems = HistogramChart.ROWS_LIMIT + self_copy.numberOfPointsToDisplay = str(HistogramChart.ROWS_LIMIT_T0_INDEX) + " items" + self_copy.graphics_list = list(map(HistogramChart.limit_points, self_copy.graphics_list)) + return super(HistogramChart, self_copy).transform() + + +class CategoryChart(XYChart): + def __init__(self, **kwargs): + super(CategoryChart, self).__init__(**kwargs) + self.type = 'CategoryPlot' + self.categoryNamesLabelAngle = getValue(kwargs, + 'categoryNamesLabelAngle', 0.0) + self.categoryNames = getValue(kwargs, 'categoryNames', []) + self.y_upper_margin = getValue(kwargs, 'upperMargin', 0.0) + self.y_lower_bound = getValue(kwargs, 'lowerMargin', 0.0) + self.x_upper_margin = getValue(kwargs, 'upperMargin', 0.05) + self.x_lower_margin = getValue(kwargs, 'lowerMargin', 0.05) + self.category_margin = getValue(kwargs, 'categoryMargin', 0.2) + self.y_auto_range_includes_zero = getValue(kwargs, + 'y_auto_range_includes_zero', + False) + self.y_auto_range = getValue(kwargs, 'y_auto_range', True) + self.orientation = getValue(kwargs, 'orientation') + + +class TreeMapChart(XYChart): + ROWS_LIMIT = 1000 + + def __init__(self, **kwargs): + super(TreeMapChart, self).__init__(**kwargs) + self.type = 'TreeMap' + self.showLegend = getValue(kwargs, 'showLegend', True) + self.title = getValue(kwargs, 'title', "") + self.colorProvider = getValue(kwargs, 'colorProvider', + RandomColorProvider()) + self.toolTipBuilder = getValue(kwargs, 'toolTipBuilder') + self.mode = getValue(kwargs, 'mode', Mode.SQUARIFY).value + self.ratio = getValue(kwargs, 'ratio') + self.valueAccessor = getValue(kwargs, 'valueAccessor', + ValueAccessor.VALUE) + self.custom_styles = [] + self.element_styles = {} + self.graphics_list = getValue(kwargs, 'root') + + def transform(self): + tree_map = self + tree_map.process(tree_map.graphics_list) + count_nodes = tree_map.count_nodes(self.graphics_list, self.increase_by_one, 0) + to_many_rows = count_nodes > TreeMapChart.ROWS_LIMIT + if to_many_rows: + tree_map = copy.copy(self) + tree_map.totalNumberOfPoints = count_nodes + tree_map.rowsLimitItems = TreeMapChart.ROWS_LIMIT + tree_map.graphics_list = TreeMapReducer.limit_tree_map(TreeMapChart.ROWS_LIMIT, self.graphics_list) + tree_map.numberOfPointsToDisplay = str( + tree_map.count_nodes(tree_map.graphics_list, self.increase_by_one_when_leaf, 0) + ) + " leaves" + tree_map.tooManyRows = to_many_rows + return super(TreeMapChart, tree_map).transform() + + def process(self, node): + children = node.children + + if children is not None: + for child in children: + self.process(child) + node.user_object["isLeaf"] = node.isLeaf() + if node.isLeaf(): + node.color = self.colorProvider.getColor(node) + toolTipBuilder = self.toolTipBuilder + if toolTipBuilder is not None: + node.tooltip = toolTipBuilder.getToolTip(node) + + @staticmethod + def increase_by_one(node, count): + return count + 1 + + @staticmethod + def increase_by_one_when_leaf(node, count): + if node.user_object["isLeaf"]: + count = count + 1 + return count + + def count_nodes(self, node, increase_fun, count): + count = increase_fun(node, count) + children = node.children + if children is not None: + for child in children: + count = self.count_nodes(child, increase_fun, count) + return count + + +class CombinedChart(BaseObject): + def __init__(self, **kwargs): + super(CombinedChart, self).__init__(**kwargs) + self.init_width = getValue(kwargs, 'initWidth', 640) + self.init_height = getValue(kwargs, 'initHeight', 480) + self.title = getValue(kwargs, 'title') + self.x_label = getValue(kwargs, 'xLabel', 'Linear') + self.plots = getValue(kwargs, 'plots', []) + self.weights = getValue(kwargs, 'weights', []) + self.auto_zoom = getValue(kwargs, 'autoZoom') + self.version = 'groovy' + self.type = 'CombinedPlot' + self.y_tickLabels_visible = True + self.x_tickLabels_visible = True + self.plot_type = 'Plot' diff --git a/beakerx_widgets/beakerx_widgets/plots/legend.py b/beakerx_widgets/beakerx_widgets/plots/legend.py new file mode 100644 index 0000000..470d45f --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/legend.py @@ -0,0 +1,31 @@ +# Copyright 2014 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from enum import Enum + + +class LegendLayout(Enum): + HORIZONTAL = "HORIZONTAL" + VERTICAL = "VERTICAL" + + +class LegendPosition(Enum): + TOP = "TOP" + LEFT = "LEFT" + BOTTOM = "BOTTOM" + RIGHT = "RIGHT" + TOP_LEFT = "TOP_LEFT" + TOP_RIGHT = "TOP_RIGHT" + BOTTOM_LEFT = "BOTTOM_LEFT" + BOTTOM_RIGHT = "BOTTOM_RIGHT" diff --git a/beakerx_widgets/beakerx_widgets/plots/plotitem.py b/beakerx_widgets/beakerx_widgets/plots/plotitem.py new file mode 100644 index 0000000..237aa4a --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/plotitem.py @@ -0,0 +1,455 @@ +# Copyright 2014 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License") +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime as dt +import math +import uuid +from enum import Enum + +import numpy as np +import pandas as pd +from beakerx_base import Color, getValue, BaseObject, getColor, datetime_to_number, date_time_2_millis, padYs +from dateutil.parser import parse + + +class ShapeType(Enum): + SQUARE = "SQUARE" + CIRCLE = "CIRCLE" + TRIANGLE = "TRIANGLE" + DIAMOND = "DIAMOND" + DCROSS = "DCROSS" + CROSS = "CROSS" + DEFAULT = "DEFAULT" + LEVEL = "LEVEL" + VLEVEL = "VLEVEL" + LINECROSS = "LINECROSS" + DOWNTRIANGLE = "DOWNTRIANGLE" + + +class StrokeType(Enum): + NONE = "NONE" + SOLID = "SOLID" + DASH = "DASH" + DOT = "DOT" + DASHDOT = "DASHDOT" + LONGDASH = "LONGDASH" + + +class PlotOrientationType(Enum): + VERTICAL = "VERTICAL" + HORIZONTAL = "HORIZONTAL" + + +class LabelPositionType(Enum): + VALUE_OUTSIDE = "VALUE_OUTSIDE" + VALUE_INSIDE = "VALUE_INSIDE" + CENTER = "CENTER" + BASE_OUTSIDE = "BASE_OUTSIDE" + BASE_INSIDE = "BASE_INSIDE" + + +class GradientColor: + def __init__(self, *args): + self.color = args[0] + + +GradientColor.BROWN_RED_YELLOW = GradientColor([Color(120, 0, 4), + Color(241, 88, 6), + Color(255, 206, 31)]) +GradientColor.GREEN_YELLOW_WHITE = GradientColor([Color(0, 170, 0), + Color(102, 204, 0), + Color(238, 238, 0), + Color(238, 187, 68), + Color(238, 187, 153), + Color(255, 255, 255)]) +GradientColor.WHITE_BLUE = GradientColor([Color(255, 255, 217), + Color(237, 248, 177), + Color(199, 233, 180), + Color(127, 205, 187), + Color(65, 182, 196), + Color(29, 145, 192), + Color(34, 94, 168), + Color(37, 52, 148), + Color(8, 29, 88)]) + + +class Graphics(BaseObject): + def __init__(self, **kwargs): + super(Graphics, self).__init__(**kwargs) + self.type = self.__class__.__name__ + self.visible = getValue(kwargs, 'visible', True) + self.yAxis = getValue(kwargs, 'yAxis') + self.clickTag = getValue(kwargs, 'tag', "") + self.hasClickAction = self.clickTag != "" + self.uid = str(uuid.uuid4()) + self.onClickListeners = lambda *args: None + self.onKeyListeners = {} + self.keyTags = {} + self.keys = [] + + def onClick(self, on_click): + if isinstance(on_click, str): + self.clickTag = on_click + else: + self.onClickListeners = on_click + self.hasClickAction = True + return self + + def onKey(self, key, on_key): + if isinstance(on_key, str): + self.keyTags[key] = on_key + else: + self.onKeyListeners[key] = on_key + if key not in self.keys: + self.keys.append(key) + return self + + def fireClick(self, details): + self.onClickListeners(details) + + def fireKey(self, details, key): + self.onKeyListeners.get(key, lambda *args: None)(details) + + +class ConstantLine(Graphics): + def __init__(self, **kwargs): + super(ConstantLine, self).__init__(**kwargs) + self.x = self.transform_value_to_number(getValue(kwargs, 'x')) + self.y = getValue(kwargs, 'y') + self.color = getColor(getValue(kwargs, 'color')) + self.width = getValue(kwargs, 'width', 1.5) + self.style = getValue(kwargs, 'style') + self.showLabel = getValue(kwargs, 'showLabel') + + @staticmethod + def transform_value_to_number(value): + if isinstance(value, dt.datetime): + return datetime_to_number(value) + return value + + +class ConstantBand(Graphics): + def __init__(self, **kwargs): + super(ConstantBand, self).__init__(**kwargs) + self.x = getValue(kwargs, 'x') + self.y = getValue(kwargs, 'y') + self.color = getColor( + getValue(kwargs, 'color', Color(0, 127, 255, 127))) + + +def is_date(string): + try: + parse(string) + return True + except Exception: + return False + + +class XYGraphics(Graphics): + def __init__(self, *args, **kwargs): + super(XYGraphics, self).__init__(**kwargs) + if len(args) > 0 and isinstance(args[0], pd.Series): + defX = args[0].index.tolist() + defY = args[0].tolist() + else: + defY = getValue(kwargs, 'y') + + if defY is not None: + if isinstance(defY, pd.Series) or isinstance(defY, np.ndarray): + defY = defY.tolist() + defX = list(range(0, len(defY))) + else: + defX = [] + + local_x = getValue(kwargs, 'x', defX) + + if local_x is not None: + if isinstance(local_x, pd.Series): + local_x = local_x.tolist() + self.x = [None] * len(local_x) + for idx in range(len(local_x)): + x = local_x[idx] + if isinstance(x, float) and math.isnan(x): + self.x[idx] = "NaN" + elif isinstance(x, dt.date) or isinstance(x, dt.time): + self.x[idx] = date_time_2_millis(x.isoformat()) + elif is_date(x): + self.x[idx] = date_time_2_millis(x) + elif isinstance(x, np.datetime64): + self.x[idx] = date_time_2_millis(x.__str__()) + else: + self.x[idx] = x + + self.y = defY + if self.y is not None: + for idx in range(len(self.y)): + y = self.y[idx] + if isinstance(y, float) and math.isnan(y): + self.y[idx] = "NaN" + + self.display_name = getValue(kwargs, 'displayName') + self.lod_filter = getValue(kwargs, 'lodFilter') + self.tooltips = getValue(kwargs, 'tooltips') + + +class Line(XYGraphics): + def __init__(self, *args, **kwargs): + super(Line, self).__init__(*args, **kwargs) + self.width = getValue(kwargs, 'width', 1.5) + self.style = getValue(kwargs, 'style') + self.interpolation = getValue(kwargs, 'interpolation') + self.color = getColor(getValue(kwargs, 'color')) + + +class BasedXYGraphics(XYGraphics): + def __init__(self, *args, **kwargs): + super(BasedXYGraphics, self).__init__(*args, **kwargs) + base = getValue(kwargs, 'base') + if isinstance(base, list): + self.bases = base + else: + self.bases = getValue(kwargs, 'base', 0) + + +class Bars(BasedXYGraphics): + def __init__(self, *args, **kwargs): + super(Bars, self).__init__(*args, **kwargs) + + width = getValue(kwargs, 'width') + if isinstance(width, list): + self.widths = width + else: + self.width = width + + color = getColor(getValue(kwargs, 'color')) + if isinstance(color, list): + self.colors = color + else: + self.color = color + + outlineColor = getColor(getValue(kwargs, 'outlineColor')) + if isinstance(outlineColor, list): + self.outline_colors = outlineColor + else: + self.outline_color = outlineColor + + +class Points(XYGraphics): + def __init__(self, *args, **kwargs): + super(Points, self).__init__(*args, **kwargs) + + shape = getColor(getValue(kwargs, 'shape')) + if isinstance(shape, list): + self.shapes = shape + else: + self.shape = getValue(kwargs, 'shape', ShapeType.DEFAULT) + + size = getColor(getValue(kwargs, 'size')) + if isinstance(size, list): + self.sizes = size + else: + self.size = getValue(kwargs, 'size', 6) + + fill = getColor(getValue(kwargs, 'fill')) + if isinstance(fill, list): + self.fills = fill + else: + self.fill = fill + + color = getColor(getValue(kwargs, 'color')) + if isinstance(color, list): + self.colors = color + else: + self.color = color + + outlineColor = getColor(getValue(kwargs, 'outlineColor')) + if isinstance(outlineColor, list): + self.outline_colors = outlineColor + else: + self.outline_color = outlineColor + + +class Stems(BasedXYGraphics): + def __init__(self, *args, **kwargs): + super(Stems, self).__init__(*args, **kwargs) + self.width = getValue(kwargs, 'width', 1.5) + color = getColor(getValue(kwargs, 'color')) + if isinstance(color, list): + self.colors = color + else: + self.color = color + + style = getValue(kwargs, 'style') + if isinstance(style, list): + self.styles = style + else: + self.style = getValue(kwargs, 'style', StrokeType.SOLID) + + +class Area(BasedXYGraphics): + def __init__(self, *args, **kwargs): + super(Area, self).__init__(*args, **kwargs) + self.color = getColor(getValue(kwargs, 'color')) + self.interpolation = getValue(kwargs, 'interpolation') + + +class Text(BaseObject): + def __init__(self, **kwargs): + super(Text, self).__init__(**kwargs) + self.x = getValue(kwargs, 'x', 0) + self.y = getValue(kwargs, 'y', 0) + self.color = getColor(getValue(kwargs, 'color')) + self.size = getValue(kwargs, 'size', 13) + self.text = getValue(kwargs, 'text', '') + self.show_pointer = getValue(kwargs, 'show_pointer', True) + self.pointer_angle = getValue(kwargs, 'pointerAngle', + (-0.25) * math.pi) + + +class YAxis(BaseObject): + def __init__(self, **kwargs): + super(YAxis, self).__init__(**kwargs) + self.label = getValue(kwargs, 'label', '') + self.auto_range = getValue(kwargs, 'autoRange', True) + self.auto_range_includes_zero = getValue(kwargs, + 'autoRangeIncludesZero', False) + self.lower_margin = getValue(kwargs, 'lowerMargin', 0.05) + self.upper_margin = getValue(kwargs, 'upperMargin', 0.05) + self.lower_bound = getValue(kwargs, 'lowerBound', 0.0) + self.upper_bound = getValue(kwargs, 'upperBound', 0.0) + self.use_log = getValue(kwargs, 'logY', False) + self.log_base = getValue(kwargs, 'logBase', 10.0) + self.type = 'YAxis' + + def setBound(self, min, max): + self.auto_range = False + self.lower_bound = min + self.upper_bound = max + return self.transform() + + +class XYStacker(BaseObject): + def __init__(self, **kwargs): + super(XYStacker, self).__init__(**kwargs) + + def stack(self, graphicsList): + if graphicsList is None or len(graphicsList) == 1: + return graphicsList + else: + maxel = graphicsList[0] + for i in range(1, len(graphicsList)): + if len(graphicsList[i].y) > len(maxel.y): + maxel = graphicsList[i] + padYs(graphicsList[0], maxel) + stackedList = [graphicsList[0]] + for gIndex in range(1, len(graphicsList)): + current = graphicsList[gIndex] + padYs(current, maxel) + previous = graphicsList[gIndex - 1] + currentYs = current.y + previousYs = previous.y + + for yIndex in range(len(currentYs)): + currentYs[yIndex] = currentYs[yIndex] + previousYs[yIndex] + + current.bases = previousYs + stackedList.append(current) + + return stackedList + + +class Crosshair(BasedXYGraphics): + def __init__(self, *args, **kwargs): + super(Crosshair, self).__init__(*args, **kwargs) + self.width = getValue(kwargs, 'width') + self.style = getValue(kwargs, 'style') + self.color = getColor(getValue(kwargs, 'color')) + + +class CategoryGraphics(Graphics): + def __init__(self, **kwargs): + super(CategoryGraphics, self).__init__(**kwargs) + self.center_series = getValue(kwargs, 'centerSeries', False) + self.use_tool_tip = getValue(kwargs, 'useToolTip', True) + self.showItemLabel = getValue(kwargs, 'showItemLabel', False) + self.outline = getValue(kwargs, 'outline', False) + self.labelPosition = getValue(kwargs, 'labelPosition', "CENTER") + self.fills = getValue(kwargs, 'fill') + self.itemLabels = getValue(kwargs, 'itemLabel') + self.seriesNames = getValue(kwargs, 'seriesNames') + self.style = getValue(kwargs, 'style') + self.size = getValue(kwargs, 'size') + + outline = getValue(kwargs, 'outlineColor') + if isinstance(outline, list): + self.outline_colors = outline + else: + self.outline_color = outline + + drawOutline = getValue(kwargs, 'drawOutline') + if isinstance(drawOutline, list): + self.outlines = drawOutline + else: + self.outline = drawOutline + + base = getValue(kwargs, 'base', 0.0) + if isinstance(base, list): + self.bases = base + else: + self.base = base + + width = getValue(kwargs, 'width') + if isinstance(width, list): + self.widths = width + else: + self.width = width + + style = getValue(kwargs, 'style') + if isinstance(style, list): + self.styles = style + else: + self.style = style + + self.value = getValue(kwargs, 'value', []) + + color = getColor(getValue(kwargs, 'color')) + if isinstance(color, list): + self.colors = color + else: + self.color = color + + +class CategoryBars(CategoryGraphics): + def __init__(self, **kwargs): + super(CategoryBars, self).__init__(**kwargs) + + +class CategoryStems(CategoryGraphics): + def __init__(self, **kwargs): + super(CategoryStems, self).__init__(**kwargs) + + +class CategoryPoints(CategoryGraphics): + def __init__(self, **kwargs): + super(CategoryPoints, self).__init__(**kwargs) + + +class CategoryLines(CategoryGraphics): + def __init__(self, **kwargs): + super(CategoryLines, self).__init__(**kwargs) + + +class CategoryArea(CategoryGraphics): + def __init__(self, **kwargs): + super(CategoryArea, self).__init__(**kwargs) diff --git a/beakerx_widgets/beakerx_widgets/plots/plotitem_treemap.py b/beakerx_widgets/beakerx_widgets/plots/plotitem_treemap.py new file mode 100644 index 0000000..248b734 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/plotitem_treemap.py @@ -0,0 +1,167 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License") +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from enum import Enum + +from beakerx_base import Color, ColorUtils + + +class TreeMapNode: + def __init__(self, *args): + self.type = 'TreeMapNode' + self.weight = 0 + self.color = '#780004' + self.children = None + self.label = args[0] + self.user_object = {} + if len(args) > 1: + self.doubleValue = args[2].value + self.labelValue = args[2].label + self.tooltip = args[2].label + self.weight = args[1] + self.label = args[0] + self.tooltip = args[2].label + + def add(self, item): + if self.children is None: + self.children = [] + item.parent = self + self.children.append(item) + self.weight += item.weight + + def isLeaf(self): + return self.getChildCount() == 0 + + def getChildCount(self): + if self.children is None: + return 0 + else: + return len(self.children) + + +class DefaultValue: + def __init__(self, value): + self.value = value + self.label = str(value) + + +class Mode(Enum): + SQUARIFY = "squarify" # rectangular subdivision; squareness controlled via the target ratio. + SLICE = "slice" # horizontal subdivision. + DICE = "dice" # vertical subdivision. + SLICE_DIC = "slice-dic" # alternating between horizontal and vertical subdivision. + + +class ValueAccessor(Enum): + VALUE = "VALUE" + WEIGHT = "WEIGHT" + + +class ColorProvider: + valueAccessor = ValueAccessor.VALUE + maxValue = None + minValue = None + + def getValue(self, node): + if node is None: + return 0.00 + + if self.valueAccessor == ValueAccessor.VALUE: + return node.doubleValue + else: + return node.weight + + def setValues(self, root): + if root.isLeaf(): + if self.valueAccessor == ValueAccessor.VALUE: + if root.doubleValue is None: + return + value = root.doubleValue + else: + value = root.weight + + if self.maxValue is None or value >= self.maxValue: + self.maxValue = value + + if self.minValue is None or value <= self.minValue: + self.minValue = value + else: + if root.children is not None: + for child in root.children: + self.setValues(child) + + +class RandomColorProvider(ColorProvider): + cursor = 0 + mapping = dict() + COLOURS = [ + Color(33, 87, 141), # blue + Color(140, 29, 23), # red + Color(150, 130, 54), # yellow + Color(20, 30, 120), # violet + Color(54, 100, 54), # green + Color(0, 30, 50), # dark + Color(102, 102, 51), + Color(255, 51, 153), + Color(255, 153, 51), + Color(204, 204, 51), + Color(205, 102, 204), + Color(51, 153, 255), + Color(153, 102, 0)] + groupByParent = False + + def __init__(self, colours=COLOURS): + self.colours = colours + + def setGroupByParent(self, group): + self.groupByParent = group + + def isGroupByParent(self): + return self.groupByParent + + def getColor(self, node): + if self.groupByParent and isinstance(node.parent, TreeMapNode): + value = node.parent.label + else: + value = self.getValue(node) + + if self.mapping.get(value) is None: + + colorValue = self.colours[self.cursor] + if isinstance(colorValue, Color): + self.mapping[value] = colorValue.shorthex() + else: + self.mapping[value] = colorValue + + self.cursor += 1 + if self.cursor == len(self.colours): + self.cursor = 0 + + return self.mapping.get(value) + + +class GradientColorProvider(ColorProvider): + def __init__(self, treeMap, colorStart=Color.RED, colorEnd=Color.GREEN): + self.valueAccessor = treeMap.chart.valueAccessor + self.start = colorStart + self.end = colorEnd + + self.setValues(treeMap.chart.graphics_list) + + def getColor(self, node): + value = self.getValue(node) + + result = (value - self.minValue) / (self.maxValue - self.minValue) + color = ColorUtils.interpolateColor(self.start, self.end, result) + return color.shorthex() diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/__init__.py b/beakerx_widgets/beakerx_widgets/plots/tests/__init__.py new file mode 100644 index 0000000..ef83ece --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/resources/interest-rates.csv b/beakerx_widgets/beakerx_widgets/plots/tests/resources/interest-rates.csv new file mode 100644 index 0000000..993e781 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/resources/interest-rates.csv @@ -0,0 +1,314 @@ +"m3","y30","y1","m6","y2","y10","y3","time","y5","y7","spread" +"7.8981","8.2586","7.9210","7.9562","8.0852","8.2067","8.1324","1990-01-30 19:00:00.000 -0500","8.1195","8.1962","0.3086" +"8.0021","8.5037","8.1111","8.1211","8.3705","8.4732","8.3868","1990-02-27 19:00:00.000 -0500","8.4247","8.4758","0.4711" +"8.1700","8.5632","8.3500","8.2800","8.6268","8.5886","8.6273","1990-03-30 19:00:00.000 -0500","8.6005","8.6482","0.4186" +"8.0405","8.7560","8.4045","8.2700","8.7240","8.7855","8.7825","1990-04-29 20:00:00.000 -0400","8.7680","8.8130","0.7450" +"8.0068","8.7314","8.3164","8.1909","8.6423","8.7582","8.6923","1990-05-30 20:00:00.000 -0400","8.7359","8.7836","0.7514" +"7.9867","8.4576","8.0962","8.0452","8.3510","8.4800","8.3981","1990-06-29 20:00:00.000 -0400","8.4305","8.5176","0.4933" +"7.8748","8.4981","7.9410","7.9233","8.1571","8.4714","8.2648","1990-07-30 20:00:00.000 -0400","8.3310","8.4552","0.5967" +"7.6943","8.8635","7.7791","7.7661","8.0613","8.7526","8.2187","1990-08-30 20:00:00.000 -0400","8.4365","8.6439","1.0583" +"7.5979","9.0289","7.7632","7.6984","8.0795","8.8932","8.2674","1990-09-29 20:00:00.000 -0400","8.5137","8.7868","1.2953" +"7.4000","8.8577","7.5514","7.5295","7.8777","8.7195","8.0686","1990-10-30 19:00:00.000 -0500","8.3277","8.5936","1.3195" +"7.2905","8.5405","7.3135","7.3850","7.5995","8.3920","7.7370","1990-11-29 19:00:00.000 -0500","8.0225","8.2765","1.1015" +"6.9495","8.2370","7.0505","7.0265","7.3140","8.0750","7.4660","1990-12-30 19:00:00.000 -0500","7.7265","8.0005","1.1255" +"6.4105","8.2695","6.6443","6.5757","7.1252","8.0919","7.3776","1991-01-30 19:00:00.000 -0500","7.7000","7.9705","1.6814" +"6.1163","8.0342","6.2668","6.1947","6.8668","7.8547","7.0774","1991-02-27 19:00:00.000 -0500","7.4726","7.7326","1.7384" +"6.0935","8.2880","6.3960","6.1985","7.1030","8.1100","7.3535","1991-03-30 19:00:00.000 -0500","7.7720","8.0025","2.0165" +"5.8255","8.2095","6.2359","5.9768","6.9482","8.0391","7.2318","1991-04-29 20:00:00.000 -0400","7.7009","7.9227","2.2136" +"5.6336","8.2673","6.1305","5.8732","6.7836","8.0677","7.1168","1991-05-30 20:00:00.000 -0400","7.7014","7.9423","2.4341" +"5.7510","8.4720","6.3585","6.0240","6.9560","8.2840","7.3915","1991-06-29 20:00:00.000 -0400","7.9370","8.1710","2.5330" +"5.7509","8.4523","6.3055","5.9745","6.9177","8.2727","7.3759","1991-07-30 20:00:00.000 -0400","7.9114","8.1468","2.5218" +"5.4977","8.1436","5.7782","5.6336","6.4332","7.9000","6.7973","1991-08-30 20:00:00.000 -0400","7.4250","7.7400","2.4023" +"5.3735","7.9480","5.5725","5.4790","6.1815","7.6500","6.5000","1991-09-29 20:00:00.000 -0400","7.1360","7.4765","2.2765" +"5.1441","7.9305","5.3336","5.2591","5.9123","7.5273","6.2291","1991-10-30 19:00:00.000 -0500","6.8714","7.2464","2.3832" +"4.6895","7.9216","4.8895","4.8011","5.5589","7.4174","5.9037","1991-11-29 19:00:00.000 -0500","6.6179","7.0589","2.7279" +"4.1843","7.7019","4.3781","4.2614","5.0257","7.0886","5.3929","1991-12-30 19:00:00.000 -0500","6.1862","6.6852","2.9043" +"3.9062","7.5819","4.1510","4.0100","4.9581","7.0324","5.3957","1992-01-30 19:00:00.000 -0500","6.2429","6.7048","3.1262" +"3.9500","7.8547","4.2874","4.0763","5.2121","7.3379","5.7200","1992-02-28 19:00:00.000 -0500","6.5779","6.9621","3.3879" +"4.1386","7.9695","4.6345","4.3309","5.6850","7.5423","6.1832","1992-03-30 19:00:00.000 -0500","6.9455","7.2573","3.4036" +"3.8390","7.9624","4.2990","4.0029","5.3443","7.4805","5.9252","1992-04-29 20:00:00.000 -0400","6.7843","7.1529","3.6414" +"3.7190","7.8910","4.1895","3.8815","5.2255","7.3920","5.8135","1992-05-30 20:00:00.000 -0400","6.6925","7.0615","3.6730" +"3.7450","7.8418","4.1659","3.8950","5.0482","7.2618","5.5973","1992-06-29 20:00:00.000 -0400","6.4827","6.9045","3.5168" +"3.2795","7.5982","3.5959","3.3832","4.3555","6.8445","4.9064","1992-07-30 20:00:00.000 -0400","5.8368","6.3573","3.5650" +"3.1990","7.3905","3.4719","3.3110","4.1929","6.5857","4.7248","1992-08-30 20:00:00.000 -0400","5.5962","6.1219","3.3867" +"2.9686","7.3410","3.1843","3.0419","3.8943","6.4152","4.4152","1992-09-29 20:00:00.000 -0400","5.3800","5.9610","3.4467" +"2.9329","7.5319","3.3024","3.1276","4.0829","6.5890","4.6381","1992-10-30 19:00:00.000 -0500","5.6005","6.1548","3.6562" +"3.2058","7.6068","3.6821","3.4395","4.5795","6.8732","5.1405","1992-11-29 19:00:00.000 -0500","6.0379","6.4900","3.6674" +"3.2914","7.4364","3.7114","3.4686","4.6736","6.7700","5.2136","1992-12-30 19:00:00.000 -0500","6.0759","6.4568","3.4786" +"3.0711","7.3416","3.4963","3.2379","4.3900","6.6000","4.9326","1993-01-30 19:00:00.000 -0500","5.8337","6.2600","3.5289" +"2.9926","7.0895","3.3858","3.1605","4.0984","6.2589","4.5789","1993-02-27 19:00:00.000 -0500","5.4289","5.8689","3.2663" +"3.0143","6.8217","3.3330","3.1457","3.9496","5.9752","4.4013","1993-03-30 19:00:00.000 -0500","5.1948","5.6557","2.9609" +"2.9305","6.8543","3.2443","3.0629","3.8376","5.9695","4.3024","1993-04-29 20:00:00.000 -0400","5.1329","5.5938","3.0390" +"3.0250","6.9190","3.3635","3.1655","3.9770","6.0355","4.4045","1993-05-30 20:00:00.000 -0400","5.1980","5.6590","3.0105" +"3.1445","6.8073","3.5364","3.2945","4.1636","5.9627","4.5309","1993-06-29 20:00:00.000 -0400","5.2168","5.6118","2.8182" +"3.1110","6.6257","3.4748","3.2576","4.0719","5.8052","4.4295","1993-07-30 20:00:00.000 -0400","5.0910","5.4762","2.6943" +"3.0905","6.3232","3.4436","3.2355","4.0014","5.6777","4.3636","1993-08-30 20:00:00.000 -0400","5.0250","5.3532","2.5873" +"3.0086","5.9971","3.3562","3.1510","3.8471","5.3600","4.1652","1993-09-29 20:00:00.000 -0400","4.7348","5.0762","2.3514" +"3.0920","5.9390","3.3920","3.2190","3.8745","5.3340","4.1750","1993-10-30 20:00:00.000 -0400","4.7070","5.0540","2.2420" +"3.1795","6.2100","3.5790","3.3615","4.1555","5.7240","4.4980","1993-11-29 19:00:00.000 -0500","5.0630","5.4470","2.5445" +"3.1277","6.2518","3.6073","3.3355","4.2123","5.7741","4.5400","1993-12-30 19:00:00.000 -0500","5.1468","5.4841","2.6464" +"3.0445","6.2910","3.5425","3.2530","4.1400","5.7505","4.4790","1994-01-30 19:00:00.000 -0500","5.0870","5.4310","2.7060" +"3.3305","6.4905","3.8658","3.5347","4.4742","5.9732","4.8321","1994-02-27 19:00:00.000 -0500","5.3953","5.7195","2.6426" +"3.5909","6.9065","4.3191","3.9148","4.9952","6.4826","5.3957","1994-03-30 19:00:00.000 -0500","5.9413","6.2800","2.8917" +"3.7805","7.2689","4.8158","4.2474","5.5495","6.9721","5.9942","1994-04-29 20:00:00.000 -0400","6.5242","6.8042","3.1916" +"4.2676","7.4124","5.3143","4.7857","5.9681","7.1833","6.3362","1994-05-30 20:00:00.000 -0400","6.7800","7.0062","2.9157" +"4.2491","7.3950","5.2673","4.7195","5.9314","7.1014","6.2682","1994-06-29 20:00:00.000 -0400","6.6955","6.9068","2.8523" +"4.4565","7.5790","5.4765","4.9535","6.1300","7.2980","6.4760","1994-07-30 20:00:00.000 -0400","6.9095","7.1245","2.8415" +"4.6117","7.4861","5.5622","5.0817","6.1774","7.2361","6.4957","1994-08-30 20:00:00.000 -0400","6.8778","7.0622","2.6243" +"4.7514","7.7124","5.7629","5.2400","6.3924","7.4571","6.6929","1994-09-29 20:00:00.000 -0400","7.0833","7.2833","2.7057" +"5.1020","7.9350","6.1145","5.6210","6.7285","7.7440","7.0415","1994-10-30 19:00:00.000 -0500","7.4030","7.5815","2.6420" +"5.4490","8.0815","6.5400","5.9750","7.1485","7.9550","7.4400","1994-11-29 19:00:00.000 -0500","7.7175","7.8300","2.5060" +"5.7648","7.8714","7.1362","6.5048","7.5895","7.8138","7.7095","1994-12-30 19:00:00.000 -0500","7.7762","7.8014","2.0490" +"5.9015","7.8465","7.0505","6.5060","7.5070","7.7795","7.6625","1995-01-30 19:00:00.000 -0500","7.7560","7.7915","1.8780" +"5.9395","7.6116","6.6968","6.3084","7.1116","7.4695","7.2474","1995-02-27 19:00:00.000 -0500","7.3658","7.4405","1.5300" +"5.9104","7.4478","6.4317","6.1726","6.7757","7.2048","6.8922","1995-03-30 19:00:00.000 -0500","7.0474","7.1426","1.2943" +"5.8379","7.3605","6.2658","6.0463","6.5679","7.0626","6.6842","1995-04-29 20:00:00.000 -0400","6.8600","6.9511","1.2247" +"5.8477","6.9500","5.9977","5.9305","6.1709","6.6327","6.2686","1995-05-30 20:00:00.000 -0400","6.4145","6.5032","0.7850" +"5.6391","6.5732","5.6423","5.6618","5.7159","6.1682","5.7959","1995-06-29 20:00:00.000 -0400","5.9286","6.0473","0.5291" +"5.5930","6.7210","5.5940","5.6185","5.7800","6.2780","5.8855","1995-07-30 20:00:00.000 -0400","6.0085","6.1990","0.6850" +"5.5657","6.8604","5.7526","5.6504","5.9809","6.4883","6.1026","1995-08-30 20:00:00.000 -0400","6.2439","6.4117","0.9226" +"5.4340","6.5490","5.6200","5.5370","5.8060","6.1975","5.8910","1995-09-29 20:00:00.000 -0400","6.0015","6.1315","0.7635" +"5.4443","6.3748","5.5905","5.5648","5.6971","6.0448","5.7719","1995-10-30 19:00:00.000 -0500","5.8629","5.9671","0.6005" +"5.5181","6.2629","5.4329","5.5057","5.4786","5.9305","5.5695","1995-11-29 19:00:00.000 -0500","5.6943","5.8300","0.4124" +"5.2940","6.0625","5.3070","5.3490","5.3245","5.7115","5.3865","1995-12-30 19:00:00.000 -0500","5.5135","5.6315","0.4175" +"5.1505","6.0519","5.0876","5.1329","5.1124","5.6524","5.2048","1996-01-30 19:00:00.000 -0500","5.3624","5.5390","0.5019" +"4.9645","6.2420","4.9420","4.9665","5.0255","5.8055","5.1430","1996-02-28 19:00:00.000 -0500","5.3795","5.6380","0.8410" +"5.0976","6.6043","5.3424","5.1586","5.6610","6.2686","5.7857","1996-03-30 19:00:00.000 -0500","5.9662","6.1895","1.1710" +"5.0877","6.7941","5.5377","5.2677","5.9573","6.5114","6.1077","1996-04-29 20:00:00.000 -0400","6.3009","6.4800","1.4236" +"5.1536","6.9277","5.6368","5.3309","6.0950","6.7368","6.2714","1996-05-30 20:00:00.000 -0400","6.4832","6.6568","1.5832" +"5.2315","7.0590","5.8100","5.4620","6.2970","6.9120","6.4850","1996-06-29 20:00:00.000 -0400","6.6895","6.8340","1.6805" +"5.2982","7.0332","5.8509","5.5241","6.2700","6.8655","6.4536","1996-07-30 20:00:00.000 -0400","6.6359","6.7582","1.5673" +"5.1891","6.8418","5.6668","5.3414","6.0291","6.6355","6.2064","1996-08-30 20:00:00.000 -0400","6.3891","6.5150","1.4464" +"5.2375","7.0260","5.8330","5.4535","6.2340","6.8320","6.4075","1996-09-29 20:00:00.000 -0400","6.5970","6.7300","1.5945" +"5.1232","6.8086","5.5500","5.3164","5.9127","6.5336","6.0786","1996-10-30 19:00:00.000 -0500","6.2705","6.4200","1.4105" +"5.1695","6.4816","5.4232","5.2737","5.7016","6.2037","5.8184","1996-11-29 19:00:00.000 -0500","5.9732","6.0974","1.0342" +"5.0405","6.5524","5.4719","5.2433","5.7819","6.3024","5.9067","1996-12-30 19:00:00.000 -0500","6.0681","6.1990","1.2619" +"5.1657","6.8267","5.6124","5.3076","6.0081","6.5790","6.1557","1997-01-30 19:00:00.000 -0500","6.3333","6.4714","1.4133" +"5.1421","6.6879","5.5253","5.2653","5.8984","6.4195","6.0326","1997-02-27 19:00:00.000 -0500","6.1989","6.3189","1.2774" +"5.2845","6.9325","5.7955","5.4790","6.2220","6.6945","6.3785","1997-03-30 19:00:00.000 -0500","6.5370","6.6535","1.4100" +"5.3045","7.0927","5.9882","5.5950","6.4482","6.8855","6.6050","1997-04-29 20:00:00.000 -0400","6.7591","6.8600","1.5809" +"5.1967","6.9357","5.8695","5.5276","6.2767","6.7110","6.4214","1997-05-30 20:00:00.000 -0400","6.5676","6.6586","1.5143" +"5.0710","6.7724","5.6919","5.3386","6.0948","6.4938","6.2371","1997-06-29 20:00:00.000 -0400","6.3752","6.4581","1.4229" +"5.1945","6.5100","5.5418","5.3295","5.8909","6.2205","6.0014","1997-07-30 20:00:00.000 -0400","6.1209","6.2018","1.0259" +"5.2795","6.5786","5.5648","5.4014","5.9390","6.2986","6.0595","1997-08-30 20:00:00.000 -0400","6.1600","6.2905","1.0190" +"5.0848","6.4952","5.5238","5.2957","5.8819","6.2086","5.9757","1997-09-29 20:00:00.000 -0400","6.1062","6.2010","1.1238" +"5.1127","6.3264","5.4564","5.2991","5.7718","6.0295","5.8391","1997-10-30 19:00:00.000 -0500","5.9282","6.0541","0.9168" +"5.2811","6.1094","5.4572","5.3767","5.7128","5.8750","5.7550","1997-11-29 19:00:00.000 -0500","5.8039","5.9017","0.5939" +"5.3045","5.9900","5.5250","5.4514","5.7150","5.8086","5.7418","1997-12-30 19:00:00.000 -0500","5.7732","5.8250","0.5041" +"5.1790","5.8110","5.2445","5.2305","5.3570","5.5445","5.3750","1998-01-30 19:00:00.000 -0500","5.4160","5.5305","0.3655" +"5.2300","5.8911","5.3084","5.2742","5.4179","5.5747","5.4342","1998-02-27 19:00:00.000 -0500","5.4926","5.6032","0.3447" +"5.1618","5.9482","5.3895","5.2482","5.5614","5.6473","5.5668","1998-03-30 19:00:00.000 -0500","5.6118","5.7091","0.4855" +"5.0829","5.9238","5.3790","5.2629","5.5643","5.6376","5.5810","1998-04-29 20:00:00.000 -0400","5.6119","5.6957","0.5548" +"5.1400","5.9265","5.4400","5.3565","5.5935","5.6525","5.6075","1998-05-30 20:00:00.000 -0400","5.6280","5.7185","0.5125" +"5.1241","5.7036","5.4086","5.3236","5.5200","5.4964","5.5186","1998-06-29 20:00:00.000 -0400","5.5241","5.5632","0.3723" +"5.0945","5.6768","5.3582","5.2277","5.4582","5.4614","5.4664","1998-07-30 20:00:00.000 -0400","5.4614","5.5209","0.3668" +"5.0410","5.5405","5.2052","5.1519","5.2686","5.3419","5.2390","1998-08-30 20:00:00.000 -0400","5.2748","5.3586","0.3010" +"4.7390","5.2048","4.7110","4.8105","4.6662","4.8067","4.6157","1998-09-29 20:00:00.000 -0400","4.6238","4.7619","0.0676" +"4.0705","5.0105","4.1214","4.1962","4.0919","4.5300","4.1790","1998-10-30 19:00:00.000 -0500","4.1843","4.4576","0.4595" +"4.5311","5.2484","4.5253","4.5868","4.5400","4.8274","4.5721","1998-11-29 19:00:00.000 -0500","4.5374","4.7758","0.2963" +"4.4968","5.0591","4.5186","4.5682","4.5064","4.6450","4.4845","1998-12-30 19:00:00.000 -0500","4.4500","4.6491","0.1482" +"4.4463","5.1579","4.5142","4.4884","4.6153","4.7221","4.6116","1999-01-30 19:00:00.000 -0500","4.6005","4.7953","0.2758" +"4.5553","5.3653","4.7026","4.6074","4.8763","4.9989","4.9005","1999-02-27 19:00:00.000 -0500","4.9147","5.0984","0.4437" +"4.5665","5.5804","4.7813","4.6465","5.0530","5.2326","5.1057","1999-03-30 19:00:00.000 -0500","5.1404","5.3600","0.6661" +"4.4091","5.5477","4.6900","4.5445","4.9768","5.1845","5.0323","1999-04-29 20:00:00.000 -0400","5.0795","5.2800","0.7755" +"4.6310","5.8055","4.8490","4.7460","5.2545","5.5395","5.3330","1999-05-30 20:00:00.000 -0400","5.4370","5.6445","0.9085" +"4.7155","6.0418","5.0968","5.0295","5.6186","5.8995","5.6973","1999-06-29 20:00:00.000 -0400","5.8091","6.0477","1.1841" +"4.6857","5.9833","5.0319","4.7538","5.5548","5.7919","5.6181","1999-07-30 20:00:00.000 -0400","5.6786","5.9443","1.1062" +"4.8736","6.0686","5.1977","5.0882","5.6782","5.9391","5.7664","1999-08-30 20:00:00.000 -0400","5.8414","6.1509","1.0655" +"4.8224","6.0714","5.2514","5.0843","5.6619","5.9152","5.7457","1999-09-29 20:00:00.000 -0400","5.8019","6.1190","1.0929" +"5.0185","6.2635","5.4290","5.2005","5.8625","6.1120","5.9425","1999-10-30 20:00:00.000 -0400","6.0335","6.3295","1.0935" +"5.2275","6.1450","5.5535","5.4280","5.8620","6.0340","5.9185","1999-11-29 19:00:00.000 -0500","5.9690","6.1730","0.8065" +"5.3568","6.3514","5.8423","5.6841","6.1045","6.2755","6.1427","1999-12-30 19:00:00.000 -0500","6.1864","6.3768","0.9186" +"5.4990","6.6255","6.1215","5.7595","6.4400","6.6610","6.4890","2000-01-30 19:00:00.000 -0500","6.5795","6.7025","1.1620" +"5.7270","6.2320","6.2180","5.9955","6.6105","6.5195","6.6525","2000-02-28 19:00:00.000 -0500","6.6780","6.7165","0.7925" +"5.8639","6.0535","6.2222","6.1135","6.5283","6.2565","6.5283","2000-03-30 19:00:00.000 -0500","6.5039","6.5065","0.3926" +"5.8216","5.8463","6.1505","6.0695","6.4037","5.9905","6.3563","2000-04-29 20:00:00.000 -0400","6.2626","6.2658","0.1689" +"5.9945","6.1486","6.3264","6.3891","6.8095","6.4405","6.7677","2000-05-30 20:00:00.000 -0400","6.6877","6.6895","0.4459" +"5.8618","5.9264","6.1727","6.2386","6.4818","6.0973","6.4264","2000-06-29 20:00:00.000 -0400","6.3009","6.3268","0.2355" +"6.1425","5.8510","6.0825","6.2735","6.3390","6.0540","6.2770","2000-07-30 20:00:00.000 -0400","6.1790","6.2235","-0.0885" +"6.2774","5.7161","6.1830","6.3548","6.2287","5.8261","6.1743","2000-08-30 20:00:00.000 -0400","6.0609","6.0504","-0.4513" +"6.1750","5.8265","6.1255","6.2495","6.0815","5.7990","6.0165","2000-09-29 20:00:00.000 -0400","5.9345","5.9805","-0.3760" +"6.2948","5.8033","6.0124","6.3157","5.9124","5.7386","5.8457","2000-10-30 19:00:00.000 -0500","5.7829","5.8371","-0.5562" +"6.3562","5.7757","6.0919","6.3390","5.8752","5.7171","5.7867","2000-11-29 19:00:00.000 -0500","5.6976","5.7814","-0.6390" +"5.9370","5.4905","5.6025","5.9230","5.3520","5.2405","5.2595","2000-12-30 19:00:00.000 -0500","5.1680","5.2830","-0.6965" +"5.2852","5.5410","4.8148","5.1462","4.7600","5.1610","4.7743","2001-01-30 19:00:00.000 -0500","4.8586","5.1305","-0.1243" +"5.0053","5.4547","4.6842","4.8889","4.6568","5.0989","4.7079","2001-02-27 19:00:00.000 -0500","4.8863","5.0974","0.0937" +"4.5355","5.3395","4.2986","4.4355","4.3423","4.8855","4.4282","2001-03-30 19:00:00.000 -0500","4.6427","4.8827","0.3500" +"3.9660","5.6460","3.9765","3.9855","4.2340","5.1410","4.4230","2001-04-29 20:00:00.000 -0400","4.7635","5.0345","1.1750" +"3.7032","5.7800","3.7814","3.7382","4.2600","5.3914","4.5073","2001-05-30 20:00:00.000 -0400","4.9277","5.2400","1.6882" +"3.5652","5.6695","3.5762","3.5590","4.0800","5.2843","4.3481","2001-06-29 20:00:00.000 -0400","4.8071","5.1381","1.7190" +"3.5933","5.6133","3.6171","3.5567","4.0386","5.2362","4.3133","2001-07-30 20:00:00.000 -0400","4.7619","5.0610","1.6429" +"3.4378","5.4835","3.4704","3.3865","3.7574","4.9713","4.0378","2001-08-30 20:00:00.000 -0400","4.5739","4.8435","1.5335" +"2.6924","5.4829","2.8247","2.7059","3.1188","4.7318","3.4506","2001-09-29 20:00:00.000 -0400","4.1153","4.5124","2.0394" +"2.1977","5.3155","2.3305","2.1709","2.7259","4.5668","3.1368","2001-10-30 19:00:00.000 -0500","3.9100","4.3073","2.3691" +"1.9065","5.1180","2.1820","1.9240","2.7825","4.6515","3.2235","2001-11-29 19:00:00.000 -0500","3.9725","4.4220","2.7450" +"1.7200","5.4800","2.2155","1.8155","3.1070","5.0875","3.6245","2001-12-30 19:00:00.000 -0500","4.3895","4.8590","3.3675" +"1.6848","5.4452","2.1586","1.7710","3.0281","5.0357","3.5567","2002-01-30 19:00:00.000 -0500","4.3400","4.7895","3.3510" +"1.7568","5.4009","2.2326","1.8637","3.0153","4.9116","3.5463","2002-02-27 19:00:00.000 -0500","4.2984","4.7147","3.1547" +"1.8250","NaN","2.5670","2.0565","3.5575","5.2840","4.1420","2002-03-30 19:00:00.000 -0500","4.7380","5.1365","3.4590" +"1.7450","NaN","2.4759","1.9750","3.4227","5.2109","4.0073","2002-04-29 20:00:00.000 -0400","4.6468","5.0164","3.4659" +"1.7605","NaN","2.3541","1.9114","3.2641","5.1645","3.8018","2002-05-30 20:00:00.000 -0400","4.4945","4.9005","3.4041" +"1.7320","NaN","2.1965","1.8325","2.9930","4.9265","3.4850","2002-06-29 20:00:00.000 -0400","4.1860","4.6010","3.1945" +"1.7136","NaN","1.9609","1.7364","2.5568","4.6532","3.0109","2002-07-30 20:00:00.000 -0400","3.8073","4.3041","2.9395" +"1.6473","NaN","1.7573","1.6373","2.1336","4.2573","2.5223","2002-08-30 20:00:00.000 -0400","3.2945","3.8750","2.6100" +"1.6590","NaN","1.7150","1.6385","2.0045","3.8700","2.3170","2002-09-29 20:00:00.000 -0400","2.9380","3.4960","2.2110" +"1.6100","NaN","1.6500","1.5895","1.9118","3.9409","2.2536","2002-10-30 19:00:00.000 -0500","2.9450","3.5368","2.3309" +"1.2547","NaN","1.4916","1.2979","1.9232","4.0484","2.3153","2002-11-29 19:00:00.000 -0500","3.0547","3.6442","2.7937" +"1.2110","NaN","1.4500","1.2676","1.8362","4.0324","2.2348","2002-12-30 19:00:00.000 -0500","3.0333","3.6267","2.8214" +"1.1890","NaN","1.3643","1.2219","1.7433","4.0486","2.1824","2003-01-30 19:00:00.000 -0500","3.0524","3.6019","2.8595" +"1.1853","NaN","1.2963","1.1953","1.6279","3.9026","2.0505","2003-02-27 19:00:00.000 -0500","2.8979","3.4479","2.7174" +"1.1510","NaN","1.2400","1.1567","1.5738","3.8071","1.9752","2003-03-30 19:00:00.000 -0500","2.7838","3.3448","2.6562" +"1.1524","NaN","1.2671","1.1690","1.6224","3.9586","2.0590","2003-04-29 20:00:00.000 -0400","2.9286","3.4743","2.8062" +"1.0881","NaN","1.1814","1.1062","1.4152","3.5690","1.7476","2003-05-30 20:00:00.000 -0400","2.5157","3.0714","2.4810" +"0.9362","NaN","1.0095","0.9433","1.2271","3.3343","1.5143","2003-06-29 20:00:00.000 -0400","2.2657","2.8395","2.3981" +"0.9205","NaN","1.1164","0.9727","1.4741","3.9755","1.9327","2003-07-30 20:00:00.000 -0400","2.8723","3.4532","3.0550" +"0.9690","NaN","1.3086","1.0524","1.8643","4.4452","2.4352","2003-08-30 20:00:00.000 -0400","3.3700","3.9624","3.4762" +"0.9552","NaN","1.2376","1.0290","1.7062","4.2743","2.2333","2003-09-29 20:00:00.000 -0400","3.1848","3.7443","3.3190" +"0.9418","NaN","1.2541","1.0195","1.7477","4.2905","2.2636","2003-10-30 19:00:00.000 -0500","3.1859","3.7514","3.3486" +"0.9522","NaN","1.3367","1.0422","1.9267","4.3000","2.4506","2003-11-29 19:00:00.000 -0500","3.2872","3.8083","3.3478" +"0.9145","NaN","1.3059","1.0109","1.9073","4.2677","2.4400","2003-12-30 19:00:00.000 -0500","3.2686","3.7905","3.3532" +"0.9005","NaN","1.2395","0.9900","1.7610","4.1505","2.2710","2004-01-30 19:00:00.000 -0500","3.1215","3.6465","3.2500" +"0.9447","NaN","1.2437","1.0111","1.7400","4.0842","2.2468","2004-02-28 19:00:00.000 -0500","3.0679","3.5853","3.1395" +"0.9535","NaN","1.1874","1.0091","1.5778","3.8265","2.0000","2004-03-30 19:00:00.000 -0500","2.7870","3.3061","2.8730" +"0.9581","NaN","1.4338","1.1090","2.0710","4.3476","2.5681","2004-04-29 20:00:00.000 -0400","3.3895","3.8924","3.3895" +"1.0395","NaN","1.7775","1.3335","2.5335","4.7155","3.0975","2004-05-30 20:00:00.000 -0400","3.8505","4.3130","3.6760" +"1.2881","NaN","2.1152","1.6352","2.7633","4.7338","3.2562","2004-06-29 20:00:00.000 -0400","3.9290","4.3524","3.4457" +"1.3576","NaN","2.0952","1.6962","2.6386","4.4981","3.0462","2004-07-30 20:00:00.000 -0400","3.6886","4.1119","3.1405" +"1.5032","NaN","2.0159","1.7600","2.5082","4.2814","2.8845","2004-08-30 20:00:00.000 -0400","3.4745","3.9041","2.7782" +"1.6814","NaN","2.1167","1.9105","2.5252","4.1257","2.8295","2004-09-29 20:00:00.000 -0400","3.3552","3.7514","2.4443" +"1.7945","NaN","2.2280","2.0515","2.5845","4.0970","2.8535","2004-10-30 20:00:00.000 -0400","3.3475","3.7480","2.3025" +"2.1075","NaN","2.5000","2.3245","2.8520","4.1940","3.0935","2004-11-29 19:00:00.000 -0500","3.5250","3.8820","2.0865" +"2.2227","NaN","2.6705","2.4968","3.0118","4.2309","3.2141","2004-12-30 19:00:00.000 -0500","3.5982","3.9277","2.0082" +"2.3710","NaN","2.8605","2.6755","3.2225","4.2215","3.3900","2005-01-30 19:00:00.000 -0500","3.7070","3.9735","1.8505" +"2.5795","NaN","3.0295","2.8458","3.3847","4.1653","3.5353","2005-02-27 19:00:00.000 -0500","3.7663","3.9658","1.5858" +"2.7959","NaN","3.3023","3.0868","3.7268","4.4977","3.9132","2005-03-30 19:00:00.000 -0500","4.1655","4.3277","1.7018" +"2.8367","NaN","3.3167","3.1443","3.6538","4.3410","3.7895","2005-04-29 20:00:00.000 -0400","3.9986","4.1581","1.5043" +"2.9019","NaN","3.3310","3.1710","3.6438","4.1443","3.7248","2005-05-30 20:00:00.000 -0400","3.8529","3.9414","1.2424" +"3.0364","NaN","3.3632","3.2218","3.6405","3.9982","3.6850","2005-06-29 20:00:00.000 -0400","3.7723","3.8605","0.9618" +"3.2875","NaN","3.6410","3.5265","3.8710","4.1775","3.9140","2005-07-30 20:00:00.000 -0400","3.9790","4.0580","0.8900" +"3.5183","NaN","3.8722","3.7848","4.0443","4.2626","4.0787","2005-08-30 20:00:00.000 -0400","4.1222","4.1778","0.7443" +"3.4943","NaN","3.8452","3.7938","3.9462","4.1990","3.9629","2005-09-29 20:00:00.000 -0400","4.0062","4.0805","0.7048" +"3.7925","NaN","4.1755","4.1250","4.2710","4.4635","4.2905","2005-10-30 19:00:00.000 -0500","4.3285","4.3820","0.6710" +"3.9745","NaN","4.3340","4.2950","4.4165","4.5350","4.4335","2005-11-29 19:00:00.000 -0500","4.4525","4.4820","0.5605" +"3.9710","NaN","4.3529","4.3276","4.4043","4.4671","4.3919","2005-12-30 19:00:00.000 -0500","4.3924","4.4138","0.4962" +"4.3360","NaN","4.4450","4.4695","4.3955","4.4160","4.3515","2006-01-30 19:00:00.000 -0500","4.3455","4.3650","0.0800" +"4.5395","4.5369","4.6847","4.6916","4.6684","4.5689","4.6374","2006-02-27 19:00:00.000 -0500","4.5721","4.5642","0.0295" +"4.6278","4.7343","4.7735","4.7922","4.7339","4.7239","4.7383","2006-03-30 19:00:00.000 -0500","4.7161","4.7139","0.0961" +"4.7216","5.0626","4.8974","4.9000","4.8889","4.9905","4.8853","2006-04-29 20:00:00.000 -0400","4.9021","4.9353","0.2689" +"4.8364","5.2014","4.9950","5.0100","4.9682","5.1100","4.9745","2006-05-30 20:00:00.000 -0400","4.9977","5.0323","0.2736" +"4.9177","5.1541","5.1550","5.1727","5.1218","5.1064","5.0882","2006-06-29 20:00:00.000 -0400","5.0673","5.0755","0.1886" +"5.0765","5.1345","5.2175","5.2665","5.1185","5.0875","5.0700","2006-07-30 20:00:00.000 -0400","5.0400","5.0485","0.0110" +"5.0904","4.9961","5.0826","5.1726","4.9035","4.8765","4.8461","2006-08-30 20:00:00.000 -0400","4.8222","4.8287","-0.2139" +"4.9300","4.8520","4.9745","5.0785","4.7690","4.7190","4.6925","2006-09-29 20:00:00.000 -0400","4.6675","4.6760","-0.2110" +"5.0462","4.8548","5.0100","5.1190","4.7957","4.7290","4.7219","2006-10-30 19:00:00.000 -0500","4.6867","4.6900","-0.3171" +"5.0733","4.6857","5.0110","5.1471","4.7405","4.5952","4.6429","2006-11-29 19:00:00.000 -0500","4.5824","4.5819","-0.4781" +"4.9730","4.6825","4.9415","5.0720","4.6735","4.5645","4.5780","2006-12-30 19:00:00.000 -0500","4.5330","4.5355","-0.4085" +"5.1052","4.8519","5.0571","5.1486","4.8762","4.7595","4.7943","2007-01-30 19:00:00.000 -0500","4.7529","4.7524","-0.3457" +"5.1632","4.8216","5.0537","5.1579","4.8489","4.7226","4.7537","2007-02-27 19:00:00.000 -0500","4.7105","4.7111","-0.4405" +"5.0800","4.7218","4.9205","5.0968","4.5736","4.5645","4.5064","2007-03-30 20:00:00.000 -0400","4.4809","4.4986","-0.5155" +"5.0067","4.8662","4.9324","5.0676","4.6662","4.6938","4.6014","2007-04-29 20:00:00.000 -0400","4.5933","4.6167","-0.3129" +"4.8677","4.9014","4.9091","4.9786","4.7664","4.7464","4.6936","2007-05-30 20:00:00.000 -0400","4.6673","4.6855","-0.1214" +"4.7419","5.2033","4.9624","4.9548","4.9805","5.1029","4.9990","2007-06-29 20:00:00.000 -0400","5.0262","5.0538","0.3610" +"4.9610","5.1081","4.9643","5.0352","4.8190","5.0043","4.8238","2007-07-30 20:00:00.000 -0400","4.8848","4.9329","0.0433" +"4.3170","4.9322","4.4722","4.5530","4.3130","4.6748","4.3387","2007-08-30 20:00:00.000 -0400","4.4322","4.5270","0.3578" +"3.9942","4.7932","4.1368","4.2016","4.0116","4.5216","4.0605","2007-09-29 20:00:00.000 -0400","4.1989","4.3300","0.5274" +"4.0027","4.7736","4.0968","4.1641","3.9677","4.5277","4.0109","2007-10-30 20:00:00.000 -0400","4.1982","4.3318","0.5250" +"3.3545","4.5200","3.4990","3.5810","3.3365","4.1485","3.3470","2007-11-29 19:00:00.000 -0500","3.6670","3.8660","0.7940" +"3.0665","4.5270","3.2630","3.3370","3.1170","4.0975","3.1325","2007-12-30 19:00:00.000 -0500","3.4875","3.7430","1.0310" +"2.8200","4.3305","2.7114","2.8362","2.4757","3.7443","2.5114","2008-01-30 19:00:00.000 -0500","2.9805","3.3071","0.9243" +"2.1740","4.5170","2.0535","2.0980","1.9730","3.7375","2.1880","2008-02-28 19:00:00.000 -0500","2.7790","3.2095","1.5635" +"1.2835","4.3930","1.5440","1.5070","1.6170","3.5100","1.7965","2008-03-30 20:00:00.000 -0400","2.4835","2.9320","2.2265" +"1.3118","4.4432","1.7382","1.5827","2.0473","3.6750","2.2327","2008-04-29 20:00:00.000 -0400","2.8414","3.1936","2.3632" +"1.7643","4.5962","2.0557","1.8633","2.4457","3.8800","2.6895","2008-05-30 20:00:00.000 -0400","3.1533","3.4600","2.1157" +"1.8905","4.6890","2.4195","2.1852","2.7738","4.0995","3.0752","2008-06-29 20:00:00.000 -0400","3.4852","3.7338","2.2090" +"1.6550","4.5709","2.2818","1.9759","2.5732","4.0077","2.8727","2008-07-30 20:00:00.000 -0400","3.3032","3.5959","2.3527" +"1.7529","4.5019","2.1771","1.9671","2.4205","3.8857","2.6990","2008-08-30 20:00:00.000 -0400","3.1419","3.4590","2.1329" +"1.1467","4.2690","1.9129","1.6410","2.0762","3.6862","2.3157","2008-09-29 20:00:00.000 -0400","2.8843","3.2486","2.5395" +"0.6859","4.1732","1.4205","1.2259","1.6123","3.8141","1.8645","2008-10-30 20:00:00.000 -0400","2.7264","3.1868","3.1282" +"0.1939","4.0044","1.0667","0.7411","1.2128","3.5267","1.5144","2008-11-29 19:00:00.000 -0500","2.2917","2.8189","3.3328" +"0.0395","2.8700","0.4945","0.2559","0.8209","2.4164","1.0700","2008-12-30 19:00:00.000 -0500","1.5218","1.8927","2.3769" +"0.1295","3.1280","0.4445","0.3045","0.8070","2.5175","1.1340","2009-01-30 19:00:00.000 -0500","1.5965","1.9780","2.3880" +"0.2953","3.5868","0.6226","0.4589","0.9753","2.8700","1.3689","2009-02-27 19:00:00.000 -0500","1.8716","2.3032","2.5747" +"0.2159","3.6432","0.6441","0.4259","0.9314","2.8195","1.3123","2009-03-30 20:00:00.000 -0400","1.8159","2.4177","2.6036" +"0.1581","3.7600","0.5476","0.3505","0.9271","2.9271","1.3167","2009-04-29 20:00:00.000 -0400","1.8581","2.4652","2.7690" +"0.1770","4.2270","0.5015","0.3035","0.9295","3.2930","1.3925","2009-05-30 20:00:00.000 -0400","2.1340","2.8105","3.1160" +"0.1786","4.5164","0.5136","0.3145","1.1841","3.7218","1.7595","2009-06-29 20:00:00.000 -0400","2.7059","3.3650","3.5432" +"0.1836","4.4068","0.4786","0.2795","1.0191","3.5623","1.5482","2009-07-30 20:00:00.000 -0400","2.4627","3.1382","3.3786" +"0.1719","4.3710","0.4590","0.2690","1.1152","3.5871","1.6486","2009-08-30 20:00:00.000 -0400","2.5710","3.2138","3.4152" +"0.1233","4.1857","0.4048","0.2071","0.9562","3.4019","1.4776","2009-09-29 20:00:00.000 -0400","2.3690","3.0167","3.2786" +"0.0743","4.1886","0.3748","0.1605","0.9490","3.3876","1.4638","2009-10-30 20:00:00.000 -0400","2.3329","2.9633","3.3133" +"0.0521","4.3147","0.3132","0.1547","0.8032","3.4026","1.3163","2009-11-29 19:00:00.000 -0500","2.2305","2.9216","3.3505" +"0.0545","4.4941","0.3714","0.1677","0.8745","3.5900","1.3832","2009-12-30 19:00:00.000 -0500","2.3405","3.0745","3.5355" +"0.0616","4.6047","0.3458","0.1484","0.9289","3.7332","1.4921","2010-01-30 19:00:00.000 -0500","2.4842","3.2089","3.6716" +"0.1089","4.6195","0.3458","0.1811","0.8568","3.6911","1.4011","2010-02-27 19:00:00.000 -0500","2.3637","3.1226","3.5821" +"0.1504","4.6448","0.3957","0.2257","0.9587","3.7274","1.5057","2010-03-30 20:00:00.000 -0400","2.4330","3.1617","3.5770" +"0.1623","4.6932","0.4450","0.2441","1.0605","3.8468","1.6382","2010-04-29 20:00:00.000 -0400","2.5814","3.2818","3.6845" +"0.1600","4.2860","0.3710","0.2235","0.8305","3.4200","1.3190","2010-05-30 20:00:00.000 -0400","2.1800","2.8610","3.2600" +"0.1236","4.1277","0.3182","0.1909","0.7245","3.2041","1.1745","2010-06-29 20:00:00.000 -0400","1.9964","2.6559","3.0805" +"0.1576","3.9943","0.2914","0.2005","0.6167","3.0114","0.9786","2010-07-30 20:00:00.000 -0400","1.7629","2.4343","2.8538" +"0.1550","3.8032","0.2591","0.1914","0.5205","2.6986","0.7836","2010-08-30 20:00:00.000 -0400","1.4655","2.1005","2.5436" +"0.1519","3.7733","0.2567","0.1933","0.4800","2.6476","0.7419","2010-09-29 20:00:00.000 -0400","1.4105","2.0514","2.4957" +"0.1345","3.8725","0.2280","0.1765","0.3760","2.5400","0.5685","2010-10-30 20:00:00.000 -0400","1.1825","1.8515","2.4055" +"0.1430","4.1860","0.2520","0.1820","0.4540","2.7630","0.6740","2010-11-29 19:00:00.000 -0500","1.3500","2.0235","2.6200" +"0.1409","4.4177","0.2941","0.1932","0.6182","3.2909","0.9932","2010-12-30 19:00:00.000 -0500","1.9345","2.6577","3.1500" +"0.1520","4.5230","0.2735","0.1815","0.6145","3.3940","1.0275","2011-01-30 19:00:00.000 -0500","1.9945","2.7235","3.2420" +"0.1321","4.6521","0.2863","0.1684","0.7726","3.5763","1.2832","2011-02-27 19:00:00.000 -0500","2.2579","2.9616","3.4442" +"0.1004","4.5139","0.2591","0.1565","0.6974","3.4143","1.1743","2011-03-30 20:00:00.000 -0400","2.1135","2.7991","3.3139" +"0.0585","4.5015","0.2465","0.1195","0.7345","3.4550","1.2100","2011-04-29 20:00:00.000 -0400","2.1690","2.8370","3.3965" +"0.0410","4.2933","0.1881","0.0867","0.5552","3.1686","0.9352","2011-05-30 20:00:00.000 -0400","1.8424","2.5138","3.1276" +"0.0373","4.2327","0.1809","0.1014","0.4105","3.0023","0.7114","2011-06-29 20:00:00.000 -0400","1.5800","2.2868","2.9650" +"0.0375","4.2705","0.1850","0.0840","0.4065","3.0030","0.6800","2011-07-30 20:00:00.000 -0400","1.5410","2.2770","2.9655" +"0.0243","3.6513","0.1148","0.0622","0.2304","2.3030","0.3830","2011-08-30 20:00:00.000 -0400","1.0213","1.6291","2.2787" +"0.0138","3.1824","0.1048","0.0433","0.2114","1.9752","0.3538","2011-09-29 20:00:00.000 -0400","0.9005","1.4195","1.9614" +"0.0190","3.1280","0.1145","0.0545","0.2805","2.1520","0.4710","2011-10-30 20:00:00.000 -0400","1.0615","1.6190","2.1330" +"0.0140","3.0155","0.1120","0.0465","0.2535","2.0135","0.3935","2011-11-29 19:00:00.000 -0500","0.9080","1.4520","1.9995" +"0.0114","2.9824","0.1152","0.0486","0.2571","1.9781","0.3871","2011-12-30 19:00:00.000 -0500","0.8914","1.4290","1.9667" +"0.0345","3.0260","0.1150","0.0655","0.2405","1.9665","0.3580","2012-01-30 19:00:00.000 -0500","0.8350","1.3805","1.9320" +"0.0920","3.1090","0.1610","0.1240","0.2780","1.9675","0.3845","2012-02-28 19:00:00.000 -0500","0.8310","1.3735","1.8755" +"0.0841","3.2814","0.1900","0.1423","0.3445","2.1727","0.5077","2012-03-30 20:00:00.000 -0400","1.0173","1.5645","2.0886" +"0.0838","3.1843","0.1833","0.1390","0.2919","2.0529","0.4319","2012-04-29 20:00:00.000 -0400","0.8948","1.4252","1.9690" +"0.0895","2.9309","0.1918","0.1455","0.2850","1.8032","0.3882","2012-05-30 20:00:00.000 -0400","0.7618","1.2132","1.7136" +"0.0914","2.6981","0.1890","0.1481","0.2919","1.6224","0.3890","2012-06-29 20:00:00.000 -0400","0.7114","1.0800","1.5310" +"0.0971","2.5900","0.1857","0.1457","0.2481","1.5267","0.3310","2012-07-30 20:00:00.000 -0400","0.6195","0.9843","1.4295" +"0.1026","2.7709","0.1835","0.1404","0.2687","1.6783","0.3678","2012-08-30 20:00:00.000 -0400","0.7139","1.1352","1.5757" +"0.1053","2.8816","0.1758","0.1374","0.2553","1.7232","0.3384","2012-09-29 20:00:00.000 -0400","0.6689","1.1184","1.6179" +"0.1048","2.9005","0.1795","0.1490","0.2771","1.7462","0.3690","2012-10-30 20:00:00.000 -0400","0.7086","1.1457","1.6414" +"0.0935","2.8035","0.1785","0.1445","0.2680","1.6540","0.3550","2012-11-29 19:00:00.000 -0500","0.6650","1.0765","1.5605" +"0.0700","2.8835","0.1585","0.1195","0.2570","1.7190","0.3540","2012-12-30 19:00:00.000 -0500","0.6960","1.1345","1.6490" +"0.0743","3.0805","0.1452","0.1076","0.2652","1.9148","0.3905","2013-01-30 19:00:00.000 -0500","0.8052","1.2986","1.8405" +"0.0989","3.1653","0.1574","0.1242","0.2674","1.9842","0.3984","2013-02-27 19:00:00.000 -0500","0.8463","1.3484","1.8853" +"0.0870","3.1625","0.1475","0.1145","0.2560","1.9575","0.3860","2013-03-30 20:00:00.000 -0400","0.8185","1.3175","1.8705" +"0.0600","2.9327","0.1245","0.0941","0.2323","1.7591","0.3405","2013-04-29 20:00:00.000 -0400","0.7105","1.1523","1.6991" +"0.0441","3.1127","0.1186","0.0809","0.2505","1.9282","0.3959","2013-05-30 20:00:00.000 -0400","0.8409","1.3109","1.8841" +"0.0505","3.4000","0.1420","0.0875","0.3335","2.3000","0.5770","2013-06-29 20:00:00.000 -0400","1.2035","1.7140","2.2495" +"0.0355","3.6050","0.1218","0.0736","0.3409","2.5823","0.6441","2013-07-30 20:00:00.000 -0400","1.4032","1.9914","2.5468" +"0.0436","3.7577","0.1268","0.0718","0.3564","2.7373","0.7045","2013-08-30 20:00:00.000 -0400","1.5205","2.1523","2.6936" +"0.0160","3.7870","0.1185","0.0415","0.4040","2.8095","0.7795","2013-09-29 20:00:00.000 -0400","1.5960","2.2165","2.7935" +"0.0473","3.6759","0.1214","0.0759","0.3355","2.6159","0.6277","2013-10-30 20:00:00.000 -0400","1.3668","1.9936","2.5686" +"0.0679","3.8000","0.1216","0.0984","0.3037","2.7184","0.5800","2013-11-29 19:00:00.000 -0500","1.3711","2.0695","2.6505" +"0.0667","3.8890","0.1329","0.0952","0.3400","2.9019","0.6852","2013-12-30 19:00:00.000 -0500","1.5762","2.2857","2.8352" +"0.0433","3.7690","0.1162","0.0690","0.3938","2.8581","0.7795","2014-01-30 19:00:00.000 -0500","1.6467","2.2938","2.8148" +"0.0526","3.6626","0.1168","0.0811","0.3268","2.7095","0.6884","2014-02-27 19:00:00.000 -0500","1.5158","2.1526","2.6568" +"0.0524","3.6210","0.1281","0.0786","0.3990","2.7233","0.8167","2014-03-30 20:00:00.000 -0400","1.6395","2.2333","2.6710" +"0.0310","3.5176","0.1076","0.0533","0.4171","2.7052","0.8848","2014-04-29 20:00:00.000 -0400","1.7010","2.2695","2.6743" +"0.0324","3.3900","0.0967","0.0519","0.3890","2.5590","0.8262","2014-05-30 20:00:00.000 -0400","1.5929","2.1210","2.5267" +"0.0357","3.4200","0.1048","0.0605","0.4524","2.5986","0.9048","2014-06-29 20:00:00.000 -0400","1.6790","2.1933","2.5629" +"0.0264","3.3318","0.1114","0.0595","0.5064","2.5423","0.9736","2014-07-30 20:00:00.000 -0400","1.6995","2.1727","2.5159" +"0.0329","3.2010","0.1071","0.0524","0.4724","2.4200","0.9276","2014-08-30 20:00:00.000 -0400","1.6314","2.0805","2.3871" +"0.0200","3.2600","0.1095","0.0438","0.5667","2.5343","1.0510","2014-09-29 20:00:00.000 -0400","1.7738","2.2205","2.5143" +"0.0168","3.0400","0.1045","0.0505","0.4455","2.3041","0.8750","2014-10-30 20:00:00.000 -0400","1.5459","1.9805","2.2873" +"0.0217","3.0383","0.1344","0.0694","0.5272","2.3256","0.9628","2014-11-29 19:00:00.000 -0500","1.6206","2.0289","2.3039" +"0.0291","2.8332","0.2145","0.1064","0.6355","2.2073","1.0641","2014-12-30 19:00:00.000 -0500","1.6400","1.9805","2.1782" +"0.0275","2.4550","0.1955","0.0830","0.5515","1.8815","0.8970","2015-01-30 19:00:00.000 -0500","1.3745","1.6715","1.8540" +"0.0179","2.5663","0.2242","0.0711","0.6189","1.9753","0.9916","2015-02-27 19:00:00.000 -0500","1.4726","1.7879","1.9574" +"0.0277","2.6264","0.2536","0.1100","0.6405","2.0427","1.0168","2015-03-30 20:00:00.000 -0400","1.5191","1.8423","2.0150" +"0.0232","2.5859","0.2336","0.0936","0.5400","1.9350","0.8655","2015-04-29 20:00:00.000 -0400","1.3545","1.6909","1.9118" +"0.0165","2.9550","0.2410","0.0795","0.6090","2.1975","0.9770","2015-05-30 20:00:00.000 -0400","1.5385","1.9330","2.1810" +"0.0150","3.1118","0.2755","0.0868","0.6886","2.3636","1.0650","2015-06-29 20:00:00.000 -0400","1.6836","2.0995","2.3486" +"0.0323","3.0664","0.2968","0.1164","0.6677","2.3245","1.0264","2015-07-30 20:00:00.000 -0400","1.6323","2.0418","2.2923" +"0.0719","2.8557","0.3757","0.2200","0.6976","2.1671","1.0300","2015-08-30 20:00:00.000 -0400","1.5414","1.9081","2.0952" +"0.0243","2.9529","0.3733","0.1795","0.7133","2.1729","1.0133","2015-09-29 20:00:00.000 -0400","1.4900","1.8762","2.1486" +"0.0167","2.8881","0.2633","0.1138","0.6448","2.0700","0.9267","2015-10-30 20:00:00.000 -0400","1.3857","1.7624","2.0533" +"0.1263","3.0300","0.4768","0.3279","0.8847","2.2632","1.2042","2015-11-29 19:00:00.000 -0500","1.6711","2.0247","2.1368" +"0.2286","2.9700","0.6536","0.4991","0.9827","2.2427","1.2805","2015-12-30 19:00:00.000 -0500","1.6986","2.0382","2.0141" +"0.2200","2.9800","0.6100","0.4900","1.0200","2.2400","1.3100","2016-01-30 19:00:00.000 -0500","1.7300","2.0600","2.0200" diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/test_category_plot.py b/beakerx_widgets/beakerx_widgets/plots/tests/test_category_plot.py new file mode 100644 index 0000000..b1af6fa --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/test_category_plot.py @@ -0,0 +1,57 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import unittest + +from ..chart import CategoryPlot +from ..plotitem import CategoryBars, LabelPositionType + + +class TestCategoryPlot(unittest.TestCase): + + def test_category_plot(self): + # given + # when + cplot = CategoryPlot(title="Hello CategoryPlot!", + xLabel="Categories", + yLabel="Values") + # then + model = cplot.model + self.assertEqual(model['chart_title'], "Hello CategoryPlot!") + self.assertEqual(model['domain_axis_label'], "Categories") + self.assertEqual(model['y_label'], "Values") + self.assertEqual(len(model['rangeAxes']), 1) + self.assertEqual(len(model['texts']), 0) + self.assertEqual(len(model['constant_lines']), 0) + self.assertEqual(len(model['constant_bands']), 0) + self.assertEqual(len(model['graphics_list']), 0) + + def test_add_item_to_category_plot(self): + # given + cplot = CategoryPlot() + # when + cplot.add(CategoryBars(value=[[1, 2, 3], [1, 3, 5]])) + # then + model = cplot.model + self.assertEqual(len(model['graphics_list']), 1) + + def test_label_position(self): + # given + cplot = CategoryPlot() + # when + cplot.add(CategoryBars(value=[[1, 2, 3], [1, 3, 5]], labelPosition=LabelPositionType.VALUE_INSIDE)) + # then + item = cplot.model['graphics_list'][0] + self.assertEqual(item['labelPosition'], "VALUE_INSIDE") diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/test_combined_plot.py b/beakerx_widgets/beakerx_widgets/plots/tests/test_combined_plot.py new file mode 100644 index 0000000..1f23ff1 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/test_combined_plot.py @@ -0,0 +1,49 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import math +import unittest + +from ..chart import CombinedPlot, Plot, Line + + +class TestCombinedPlot(unittest.TestCase): + + def test_empty_category_plot(self): + # given + # when + cplot = CombinedPlot(xLabel="Linear") + # then + model = cplot.model + self.assertEqual(len(model['plots']), 0) + + def test_add_category_plot(self): + # given + points = 100 + logBase = 10 + expys = [] + xs = [] + for i in range(0, points): + xs.append(i / 15.0) + expys.append(math.exp(xs[i])) + + cplot = CombinedPlot(xLabel="Linear") + logYPlot = Plot(title="Linear x, Log y", yLabel="Log", logY=True, yLogBase=logBase) + logYPlot.add(Line(x=xs, y=expys, displayName="f(x) = exp(x)")) + # when + cplot.add(logYPlot, 4) + # then + model = cplot.model + self.assertEqual(len(model['plots']), 1) diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/test_data_frame_support_in_category_bars.py b/beakerx_widgets/beakerx_widgets/plots/tests/test_data_frame_support_in_category_bars.py new file mode 100644 index 0000000..c98be8d --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/test_data_frame_support_in_category_bars.py @@ -0,0 +1,38 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import unittest + +import pandas as pd + +from ..chart import CategoryPlot +from ..plotitem import CategoryBars + + +class TestSupportDataFrameInCategoryBars(unittest.TestCase): + + def test_data_frame_for_category_bars(self): + # given + a = [1, 2, 3] + b = [4, 5, 6] + cat_df = pd.DataFrame({'a': a, 'b': b}) + bars = CategoryBars(value=[cat_df['a'], cat_df['b']]) + plot = CategoryPlot() + # when + plot.add(bars) + # then + value = plot.model['graphics_list'][0]['value'] + self.assertEqual(value[0], [1, 2, 3]) + self.assertEqual(value[1], [4, 5, 6]) diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/test_data_frame_support_in_plots.py b/beakerx_widgets/beakerx_widgets/plots/tests/test_data_frame_support_in_plots.py new file mode 100644 index 0000000..667ccba --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/test_data_frame_support_in_plots.py @@ -0,0 +1,59 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import pandas as pd + +from ..chart import Plot +from ..plotitem import Area, Stems + + +class TestSupportDataFrameInPlot(unittest.TestCase): + + def test_data_frame_base_attribute_for_area(self): + # given + x = [1, 3, 5, 7, 10] + y = [100, 120, 90, 100, 80] + base = [50, 60, 45, 50, 40] + plot_df = pd.DataFrame({'x': x, 'y': y, 'base': base}) + # when + plot = Plot(title='Areas') \ + .add(Area(displayName="Area", + x=plot_df.x, + y=plot_df.y, + base=plot_df.base, + width=1)) + + # then + area = plot.model['graphics_list'][0] + self.assertEqual(area['bases'], base) + + def test_data_frame_base_attribute_for_stems(self): + # given + x = [1, 3, 5, 7, 10] + y = [100, 120, 90, 100, 80] + base = [50, 60, 45, 50, 40] + plot_df = pd.DataFrame({'x': x, 'y': y, 'base': base}) + # when + plot = Plot(title='Areas') \ + .add(Stems(displayName="Area", + x=plot_df.x, + y=plot_df.y, + base=plot_df.base, + width=1)) + + # then + area = plot.model['graphics_list'][0] + self.assertEqual(area['bases'], base) diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/test_heatmap.py b/beakerx_widgets/beakerx_widgets/plots/tests/test_heatmap.py new file mode 100644 index 0000000..2fd30f4 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/test_heatmap.py @@ -0,0 +1,132 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import unittest +from random import randint + +import pandas as pd + +from ..chart import HeatMap, XYChart +from ..legend import LegendPosition + + +class TestHeatMap(unittest.TestCase): + + def test_empty_data(self): + # given + # when + widget = HeatMap(data=[]) + # then + model = widget.model + self.assertFalse(model[XYChart.TOO_MANY_ROWS]) + self.assertEqual(model[XYChart.TOTAL_NUMBER_OF_POINTS], 0) + self.assertEqual(model[XYChart.NUMBER_OF_POINTS_TO_DISPLAY], 0) + self.assertEqual(model[XYChart.ROWS_LIMIT_ITEMS], widget.rows_limit) + + def test_xLowerMargin(self): + # given + # when + widget = HeatMap(data=[], xLowerMargin=1.0) + # then + model = widget.model + self.assertEqual(model["x_lower_margin"], 1.0) + + def test_yLowerMargin(self): + # given + # when + widget = HeatMap(data=[], yLowerMargin=2.0) + # then + model = widget.model + self.assertEqual(model["y_lower_margin"], 2.0) + + def test_yUpperMargin(self): + # given + # when + widget = HeatMap(data=[], yUpperMargin=3.0) + # then + model = widget.model + self.assertEqual(model["y_upper_margin"], 3.0) + + def test_should_not_limit_data(self): + # given + maxdepth = 10 + data = [[randint(1, 100) for x in range(maxdepth)] for y in range(maxdepth)] + # when + widget = HeatMap(data=data) + # then + model = widget.model + self.assertFalse(model[XYChart.TOO_MANY_ROWS]) + self.assertEqual(model[XYChart.TOTAL_NUMBER_OF_POINTS], 100) + self.assertEqual(model[XYChart.NUMBER_OF_POINTS_TO_DISPLAY], 100) + self.assertEqual(model[XYChart.ROWS_LIMIT_ITEMS], widget.rows_limit) + + def test_should_limit_data(self): + # given + maxdepth = 1001 + data = [[randint(1, 100) for x in range(maxdepth)] for y in range(maxdepth)] + # when + widget = HeatMap(100, 10, data=data) + # then + model = widget.model + self.assertTrue(model[XYChart.TOO_MANY_ROWS]) + self.assertEqual(model[XYChart.TOTAL_NUMBER_OF_POINTS], 1002001) + self.assertEqual(model[XYChart.NUMBER_OF_POINTS_TO_DISPLAY], 10201) + self.assertEqual(model[XYChart.ROWS_LIMIT_ITEMS], 100) + + def test_support_data_frame_series(self): + # given + maxdepth = 1001 + data = [[randint(1, 100) for x in range(maxdepth)] for y in range(maxdepth)] + heat_map_df = pd.DataFrame({'data': data}) + # when + widget = HeatMap(100, 10, data=heat_map_df['data']) + # then + self.assertEqual(len(widget.model['graphics_list'][0]), 101) + model = widget.model + self.assertTrue(model[XYChart.TOO_MANY_ROWS]) + self.assertEqual(model[XYChart.TOTAL_NUMBER_OF_POINTS], 1002001) + self.assertEqual(model[XYChart.NUMBER_OF_POINTS_TO_DISPLAY], 10201) + self.assertEqual(model[XYChart.ROWS_LIMIT_ITEMS], 100) + + def test_support_data_frame(self): + # given + maxdepth = 1001 + data = [[randint(1, 100) for x in range(maxdepth)] for y in range(maxdepth)] + heat_map_df = pd.DataFrame(data) + # when + widget = HeatMap(100, 10, data=heat_map_df) + # then + model = widget.model + self.assertTrue(model[XYChart.TOO_MANY_ROWS]) + self.assertEqual(model[XYChart.TOTAL_NUMBER_OF_POINTS], 1002001) + self.assertEqual(model[XYChart.NUMBER_OF_POINTS_TO_DISPLAY], 10201) + self.assertEqual(model[XYChart.ROWS_LIMIT_ITEMS], 100) + + def test_legend_default_position(self): + # given + # when + widget = HeatMap(data=[], legendPosition=LegendPosition.TOP) + # then + model = widget.model + self.assertEqual(model['legend_position']['position'], "TOP") + self.assertEqual(model['legend_position']['type'], "LegendPosition") + + def test_legend_default_layout(self): + # given + # when + widget = HeatMap(data=[]) + # then + model = widget.model + self.assertEqual(model['legend_layout'], "HORIZONTAL") diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/test_histogram.py b/beakerx_widgets/beakerx_widgets/plots/tests/test_histogram.py new file mode 100644 index 0000000..db3e026 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/test_histogram.py @@ -0,0 +1,85 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import random +import unittest + +import pandas as pd + +from ..chart import Histogram, HistogramChart, XYChart + + +class TestHistogram(unittest.TestCase): + + def setUp(self): + self.data1 = [] + self.data2 = [] + for x in range(1, 10000): + self.data1.append(random.gauss(0, 1)) + self.data2.append(random.gauss(0, 1)) + + def test_empty_data(self): + # given + data = [] + # when + histogram = Histogram(data=data) + # then + model = histogram.model + self.assertFalse(model[XYChart.TOO_MANY_ROWS]) + self.assertEqual(model[XYChart.TOTAL_NUMBER_OF_POINTS], 0) + self.assertEqual(model[XYChart.NUMBER_OF_POINTS_TO_DISPLAY], str(HistogramChart.ROWS_LIMIT_T0_INDEX) + " items") + self.assertEqual(model[XYChart.ROWS_LIMIT_ITEMS], HistogramChart.ROWS_LIMIT) + + def test_should_not_limit_data(self): + # given + data = [] + for x in range(1, 10000): + data.append(random.gauss(0, 1)) + # when + histogram = Histogram(data=data) + # then + model = histogram.model + self.assertFalse(model[XYChart.TOO_MANY_ROWS]) + self.assertEqual(model[XYChart.TOTAL_NUMBER_OF_POINTS], 9999) + self.assertEqual(model[XYChart.NUMBER_OF_POINTS_TO_DISPLAY], str(10000) + " items") + self.assertEqual(model[XYChart.ROWS_LIMIT_ITEMS], HistogramChart.ROWS_LIMIT) + + def test_should_limit_data(self): + # given + data = [] + for x in range(1, 1000002): + data.append(random.gauss(0, 1)) + # when + histogram = Histogram(data=data) + # then + model = histogram.model + self.assertTrue(model[XYChart.TOO_MANY_ROWS]) + self.assertEqual(model[XYChart.TOTAL_NUMBER_OF_POINTS], 1000001) + self.assertEqual(model[XYChart.NUMBER_OF_POINTS_TO_DISPLAY], str(10000) + " items") + self.assertEqual(model[XYChart.ROWS_LIMIT_ITEMS], HistogramChart.ROWS_LIMIT) + + def test_support_data_frame(self): + # given + df = pd.DataFrame({'data1': self.data1, 'data2': self.data2}) + # when + histogram = Histogram(data=df['data1']) + # then + self.assertEqual(len(histogram.model['graphics_list'][0]), 9999) + + def test_legend_default_position(self): + # given + # when + histogram = Histogram(data=[self.data1, self.data2]) + # then + self.assertEqual(histogram.model['legend_position']['position'], "TOP_RIGHT") diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/test_nano_plot.py b/beakerx_widgets/beakerx_widgets/plots/tests/test_nano_plot.py new file mode 100644 index 0000000..1e2a9b3 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/test_nano_plot.py @@ -0,0 +1,51 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import time +import unittest + +from ..chart import NanoPlot, Points + + +class TestNanoPlot(unittest.TestCase): + + def test_plot(self): + # given + # when + plot = NanoPlot() + + # then + model = plot.model + self.assertEqual(len(model['rangeAxes']), 1) + self.assertEqual(len(model['texts']), 0) + self.assertEqual(len(model['constant_lines']), 0) + self.assertEqual(len(model['constant_bands']), 0) + self.assertEqual(len(model['graphics_list']), 0) + + def test_add_Line_to_plot(self): + # given + millis = int(round(time.time() * 1000)) + nanos = millis * 1000 * 1000 + xs = [] + ys = [] + for i in range(11): + xs.append(nanos + 7 * i) + ys.append(i) + plot = NanoPlot() + # when + plot.add(Points(x=xs, y=ys)) + # then + model = plot.model + self.assertEqual(len(model['graphics_list']), 1) diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/test_plot.py b/beakerx_widgets/beakerx_widgets/plots/tests/test_plot.py new file mode 100644 index 0000000..cdae662 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/test_plot.py @@ -0,0 +1,170 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import unittest + +from beakerx_base import Color + +from ..chart import Plot +from ..legend import LegendPosition +from ..plotitem import StrokeType, Crosshair, Points, ShapeType, YAxis, Text, ConstantLine, ConstantBand + + +class TestPlot(unittest.TestCase): + + def test_plot(self): + # given + # when + plot = Plot(title="Title", + xLabel="Horizontal", + yLabel="Vertical", + initWidth=500, + initHeight=200) + + # then + model = plot.model + self.assertEqual(model['chart_title'], "Title") + self.assertEqual(model['domain_axis_label'], "Horizontal") + self.assertEqual(model['y_label'], "Vertical") + + self.assertEqual(len(model['rangeAxes']), 1) + self.assertEqual(len(model['texts']), 0) + self.assertEqual(len(model['constant_lines']), 0) + self.assertEqual(len(model['constant_bands']), 0) + self.assertEqual(len(model['graphics_list']), 0) + self.assertFalse('crosshair' in plot.model) + + def test_add_YAxis_to_plot(self): + # given + plot = Plot() + # when + plot.add(YAxis(label="Right yAxis")) + # then + self.assertEqual(len(plot.model['rangeAxes']), 2) + + def test_add_Text_to_plot(self): + # given + plot = Plot() + # when + plot.add(Text(text="Hello")) + # then + self.assertEqual(len(plot.model['texts']), 1) + + def test_add_ConstantLine_to_plot(self): + # given + plot = Plot() + # when + plot.add(ConstantLine(x=0.65)) + # then + self.assertEqual(len(plot.model['constant_lines']), 1) + + def test_add_ConstantBand_to_plot(self): + # given + plot = Plot() + # when + plot.add(ConstantBand(x=[1, 2])) + # then + self.assertEqual(len(plot.model['constant_bands']), 1) + + def test_add_list_of_ConstantBand_to_plot(self): + # given + plot = Plot() + list_of_constant_bands = [ConstantBand(x=[1, 2]), ConstantBand(x=[3, 4])] + # when + plot.add(list_of_constant_bands) + # then + self.assertEqual(len(plot.model['constant_bands']), len(list_of_constant_bands)) + + def test_should_setXBound(self): + # given + plot = Plot() + # when + plot.setXBound([-2, 10]) + # then + self.assertEqual(plot.model['x_lower_bound'], -2) + self.assertEqual(plot.model['x_upper_bound'], 10) + + def test_should_setYBound(self): + # given + plot = Plot() + # when + plot.setYBound([2, 6]) + # then + self.assertEqual(plot.model['y_lower_bound'], 2) + self.assertEqual(plot.model['y_upper_bound'], 6) + + def test_should_rise_ValueError_when_setXBound(self): + # given + plot = Plot() + # when + try: + plot.setXBound([-2, 10, 11]) + except ValueError as ex: + # then + self.assertEqual(ex.args[0], "to set the x bound, the list needs to be of size=2.") + + def test_should_rise_ValueError_when_setYBound(self): + # given + plot = Plot() + # when + try: + plot.setYBound([-2, 10, 11]) + except ValueError as ex: + # then + self.assertEqual(ex.args[0], "to set the y bound, the list needs to be of size=2.") + + def test_should_setShowLegend(self): + # given + plot = Plot() + # when + plot.setShowLegend(True) + # then + self.assertEqual(plot.model['show_legend'], True) + + def test_should_set_crosshair(self): + # given + ch = Crosshair(color=Color.black, width=2) + # when + plot = Plot(crosshair=ch) + # then + self.assertTrue('crosshair' in plot.model) + + def test_should_set_stroke_type(self): + # given + ch = Crosshair(color=Color.black, width=2, style=StrokeType.DOT) + # when + plot = Plot(crosshair=ch) + # then + self.assertEqual(plot.model['crosshair']['style'], 'DOT') + + def test_set_shape_type(self): + # given + plot = Plot() + # when + plot.add(Points(y=[1, 3, 6, 3, 1], + x=[1, 2, 3, 4, 5], + size=10, + shape=ShapeType.DIAMOND)) + # then + item = plot.model['graphics_list'][0] + self.assertEqual(item['shape'], "DIAMOND") + + def test_should_set_legend_position(self): + # given + # when + plot = Plot(legendPosition=LegendPosition.LEFT) + # then + self.assertEqual(plot.model['legend_position']['type'], "LegendPosition") + self.assertEqual(plot.model['legend_position']['position'], "LEFT") diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/test_simple_time_plot.py b/beakerx_widgets/beakerx_widgets/plots/tests/test_simple_time_plot.py new file mode 100644 index 0000000..fd523a5 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/test_simple_time_plot.py @@ -0,0 +1,77 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import unittest + +import pandas as pd +from beakerx_base import Color + +from ..chart import SimpleTimePlot + + +class TestPlot(unittest.TestCase): + + def test_plot(self): + # given + tableRows = pd.read_csv(os.path.dirname(__file__) + "/resources/" + 'interest-rates.csv') + # when + plot = SimpleTimePlot(tableRows, + ["y1", "y10"], + timeColumn="time", + yLabel="Price", + displayNames=["1 Year", "10 Year"], + colors=[[216, 154, 54], Color.lightGray]) + # then + model = plot.model + self.assertEqual(len(model['rangeAxes']), 1) + self.assertEqual(len(model['texts']), 0) + self.assertEqual(len(model['constant_lines']), 0) + self.assertEqual(len(model['constant_bands']), 0) + self.assertEqual(len(model['graphics_list']), 2) + + def test_displayLines(self): + # given + tableRows = pd.read_csv(os.path.dirname(__file__) + "/resources/" + 'interest-rates.csv') + # when + plot = SimpleTimePlot(tableRows, + ["y1", "y10"], + timeColumn="time", + yLabel="Price", + displayNames=["1 Year", "10 Year"], + colors=[[216, 154, 54], Color.lightGray], + displayLines=True, + displayPoints=False) + # then + model = plot.model + self.assertEqual(len(model['graphics_list']), 2) + self.assertEqual(model['graphics_list'][0]['type'], "Line") + + def test_displayPoints(self): + # given + tableRows = pd.read_csv(os.path.dirname(__file__) + "/resources/" + 'interest-rates.csv') + # when + plot = SimpleTimePlot(tableRows, + ["y1", "y10"], + timeColumn="time", + yLabel="Price", + displayNames=["1 Year", "10 Year"], + colors=[[216, 154, 54], Color.lightGray], + displayLines=False, + displayPoints=True) + # then + model = plot.model + self.assertEqual(len(model['graphics_list']), 2) + self.assertEqual(model['graphics_list'][0]['type'], "Points") diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/test_stacking.py b/beakerx_widgets/beakerx_widgets/plots/tests/test_stacking.py new file mode 100644 index 0000000..3e4164d --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/test_stacking.py @@ -0,0 +1,36 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import unittest + +from ..chart import Plot +from ..plotitem import XYStacker, Area + + +class TestStacking(unittest.TestCase): + + def test_plot(self): + # given + y1 = [1, 5, 3, 2, 3] + y2 = [7, 2, 4, 1, 3] + plot = Plot() + a1 = Area(y=y1, displayName='y1') + a2 = Area(y=y2, displayName='y2') + stacker = XYStacker() + # when + plot.add(stacker.stack([a1, a2])) + # then + model = plot.model + self.assertEqual(len(model['graphics_list']), 2) diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/test_time_plot.py b/beakerx_widgets/beakerx_widgets/plots/tests/test_time_plot.py new file mode 100644 index 0000000..9e28572 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/test_time_plot.py @@ -0,0 +1,56 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import unittest + +import pandas as pd + +from ..chart import TimePlot, Line +from ..plotitem import ConstantLine + + +class TestTimePlot(unittest.TestCase): + + def test_plot(self): + # given + # when + plot = TimePlot(timeZone="America/New_York") + + # then + model = plot.model + self.assertEqual(model['timezone'], "America/New_York") + + self.assertEqual(len(model['rangeAxes']), 1) + self.assertEqual(len(model['texts']), 0) + self.assertEqual(len(model['constant_lines']), 0) + self.assertEqual(len(model['constant_bands']), 0) + self.assertEqual(len(model['graphics_list']), 0) + + def test_add_Line_to_plot(self): + # given + plot = TimePlot() + # when + plot.add(Line()) + # then + model = plot.model + self.assertEqual(len(model['graphics_list']), 1) + + def test_add_ConstantLine_to_plot_with_pandas_date_time(self): + # given + plot = TimePlot() + # when + plot.add(ConstantLine(x=pd.to_datetime('2015-02-04 15:00:00'))) + # then + self.assertEqual(plot.model['constant_lines'][0]['x'], 1423062000000) diff --git a/beakerx_widgets/beakerx_widgets/plots/tests/test_treemap.py b/beakerx_widgets/beakerx_widgets/plots/tests/test_treemap.py new file mode 100644 index 0000000..7d10fc6 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tests/test_treemap.py @@ -0,0 +1,65 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import unittest + +from ..chart import TreeMap, XYChart +from ..plotitem_treemap import TreeMapNode, DefaultValue, ValueAccessor + + +class TestTreeMap(unittest.TestCase): + + def test_should_not_limit_data(self): + # given + menu_node = self.create_tree_map(100) + + # when + widget = TreeMap(root=menu_node) + # then + model = widget.model + self.assertFalse(model[XYChart.TOO_MANY_ROWS]) + + def test_should_limit_data(self): + # given + menu_node = self.create_tree_map(500) + + # when + widget = TreeMap(root=menu_node) + # then + model = widget.model + self.assertTrue(model[XYChart.TOO_MANY_ROWS]) + self.assertEqual(model[XYChart.TOTAL_NUMBER_OF_POINTS], 1498) + self.assertEqual(model[XYChart.NUMBER_OF_POINTS_TO_DISPLAY], str(998) + " leaves") + self.assertEqual(model[XYChart.ROWS_LIMIT_ITEMS], 1000) + + def test_should_set_value_accessor(self): + # given + menu_node = self.create_tree_map(100) + # when + widget = TreeMap(root=menu_node, + valueAccessor=ValueAccessor.WEIGHT) + # then + model = widget.model + self.assertFalse(model[XYChart.TOO_MANY_ROWS]) + + @staticmethod + def create_tree_map(number_of_nodes): + menu_node = TreeMapNode("0") + for i in range(1, number_of_nodes): + nodeX = TreeMapNode("X" + str(i)) + nodeX.add(TreeMapNode("a" + str(i), i, DefaultValue(i))) + nodeX.add(TreeMapNode("b" + str(i), i, DefaultValue(i))) + menu_node.add(nodeX) + return menu_node diff --git a/beakerx_widgets/beakerx_widgets/plots/tree_map_reducer.py b/beakerx_widgets/beakerx_widgets/plots/tree_map_reducer.py new file mode 100644 index 0000000..825ed73 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/plots/tree_map_reducer.py @@ -0,0 +1,123 @@ +# Copyright 2018 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy + + +class TreeMapReducer: + + @staticmethod + def limit_tree_map(limit, root): + tree_layers = TreeMapReducer.create_tree_layers(root) + mapper = TreeMapReducer.reduce_tree_map_children(limit, tree_layers) + return mapper[root] + + @staticmethod + def create_tree_layers(root): + tree_layers = [] + tree_layer = TreeLayer() + tree_layer.add_node_layer(NodeLayer(root, root.children)) + tree_layers.append(tree_layer) + TreeMapReducer.create_next_tree_layer(tree_layer, tree_layers) + return tree_layers + + @staticmethod + def create_next_tree_layer(tree_layer, tree_layers): + new_tree_layer = TreeMapReducer.create_tree_layer(tree_layer.get_node_layers()) + if len(new_tree_layer.get_node_layers()) != 0: + tree_layers.append(new_tree_layer) + TreeMapReducer.create_next_tree_layer(new_tree_layer, tree_layers) + + @staticmethod + def create_tree_layer(node_layers): + new_tree_layer = TreeLayer() + for nl in node_layers: + children = nl.children + if children is not None: + for child in children: + node_layer = NodeLayer(child, child.children) + new_tree_layer.add_node_layer(node_layer) + return new_tree_layer + + @staticmethod + def reduce_tree_map_children(limit, tree_layers): + mapper = {} + tree_counter = TreeCounter() + for tl in tree_layers: + number_of_nodes_changed = True + while number_of_nodes_changed and tree_counter.get_count() <= limit: + number_of_nodes_changed = tl.add_child_to_node_layers(tree_counter, mapper, limit) + return mapper + + +class TreeLayer: + + def __init__(self): + self.node_layers = [] + + def add_node_layer(self, node_layer): + self.node_layers.append(node_layer) + + def get_node_layers(self): + return self.node_layers + + def add_child_to_node_layers(self, tree_counter, mapper, limit): + at_least_one_child_added = False + for nl in self.get_node_layers(): + if tree_counter.get_count() <= limit: + added = nl.add_child(tree_counter, mapper) + if added: + at_least_one_child_added = True + return at_least_one_child_added + + +class NodeLayer: + + def __init__(self, node, children): + self.node = node + self.children = children + if self.children is None: + self.iter = iter(()) + else: + self.iter = iter(self.children) + + def add_child(self, tree_counter, mapper): + child = next(self.iter, None) + if child is not None: + if self.node not in mapper: + cloned_node = copy.copy(self.node) + if cloned_node.children is not None: + cloned_node.children = [] + mapper[self.node] = cloned_node + cloned_child = copy.copy(child) + if cloned_child.children is not None: + cloned_child.children = [] + mapper[self.node].add(cloned_child) + mapper[child] = cloned_child + if child.isLeaf(): + tree_counter.increase() + return True + return False + + +class TreeCounter: + + def __init__(self): + self.count = 1 + + def increase(self): + self.count = self.count + 1 + + def get_count(self): + return self.count diff --git a/beakerx_widgets/beakerx_widgets/runtime.py b/beakerx_widgets/beakerx_widgets/runtime.py new file mode 100644 index 0000000..9dc56ce --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/runtime.py @@ -0,0 +1,728 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import base64 +import datetime +import json +import math +import os +import time +import urllib.error +import urllib.parse +import urllib.request + +import IPython +import numpy +import pandas +import requests +from IPython import get_ipython +from IPython.display import display_html +from beakerx_base import BaseObject +try: + from beakerx_tabledisplay import TableDisplay +except ModuleNotFoundError: + TableDisplay = None +from beakerx_widgets.plots import chart +from beakerx_widgets.forms import easyforms +from ipykernel.comm import Comm +from traitlets import Unicode + + +class OutputContainer: + def __init__(self): + self.items = [] + + def clear(self): + self.items = [] + + def addItem(self, obj): + self.items.append(obj) + + def getItems(self): + return self.items + + +class BeakerCodeCell: + def __init__(self, cellId, evaluatorId): + self.cellId = cellId + self.evaluatorId = evaluatorId + self.code = '' + self.outputtype = '' + self.output = None + self.tags = '' + + def getCellId(self): + return self.cellId + + def getEvaluatorId(self): + return self.evaluatorId + + def getCode(self): + return self.code + + def getOutputType(self): + return self.outputtype + + def getOutput(self): + return self.output + + def getTags(self): + return self.tags + + +def convertTypeName(typ): + if typ.startswith("float"): + return "double" + if typ.startswith("int") or typ.startswith("uint") or typ.startswith("short") or typ.startswith( + "ushort") or typ.startswith("long") or typ.startswith("ulong"): + return "integer" + if typ.startswith("bool"): + return "boolean" + if typ.startswith("date") or typ.startswith("Time"): + return "datetime" + return "string" + + +def isPrimitiveType(typ): + if typ.startswith("float"): + return True + if typ.startswith("int") or typ.startswith("uint") or typ.startswith("short") or typ.startswith( + "ushort") or typ.startswith("long") or typ.startswith("ulong"): + return True + if typ.startswith("bool"): + return True + if typ.startswith("date") or typ.startswith("Time"): + return True + if typ.startswith("str"): + return True + return False + + +def isListOfMaps(data): + if type(data) != list: + return False + for w in data: + if type(w) != dict: + return False + for v in w.values(): + if not isPrimitiveType(type(v).__name__): + return False + return True + + +def isDictionary(data): + if type(data) != dict: + return False + for v in data.values(): + if not isPrimitiveType(type(v).__name__): + return False + return True + + +def transformNaN(obj): + if not isinstance(obj, float): + return obj + if math.isnan(obj): + return "Nan"; + if math.isinf(obj): + if obj > 0: + return "Infinity" + else: + return "-Infinity" + return obj + + +def transformNaNs(obj): + for x in range(0, len(obj)): + i = obj[x]; + if not isinstance(i, float): + continue + if math.isnan(i): + obj[x] = "NaN"; + if math.isinf(i): + if i > 0: + obj[x] = "Infinity" + else: + obj[x] = "-Infinity" + + +def fixNaNBack(obj): + if not isinstance(obj, str): + return obj + if obj == "NaN": + return float('nan') + if obj == "Infinity": + return float('inf') + if obj == "-Infinity": + return float('-inf') + return obj + + +def fixNaNsBack(obj): + for x in range(0, len(obj)): + i = obj[x]; + if not isinstance(i, str): + continue + if i == "NaN": + obj[x] = float('nan') + if i == "Infinity": + obj[x] = float('inf') + if i == "-Infinity": + obj[x] = float('-inf') + + +def transform(obj): + if type(obj) == bytes: + return str(obj) + if isListOfMaps(obj): + out = {} + out['type'] = "TableDisplay" + out['subtype'] = "ListOfMaps" + cols = [] + for l in obj: + cols.extend(l.keys()) + cols = list(set(cols)) + out['columnNames'] = cols + vals = [] + for l in obj: + row = [] + for r in cols: + if r in l: + row.append(transform(l[r])) + else: + row.append('') + vals.append(row) + out['values'] = vals + return out + if isDictionary(obj): + out = {} + out['type'] = "TableDisplay" + out['subtype'] = "Dictionary" + out['columnNames'] = ["Key", "Value"] + values = [] + for k, v in obj.items(): + values.append([k, transform(v)]) + out['values'] = values + return out + if type(obj) == dict: + out = {} + for k, v in obj.items(): + out[k] = transformNR(v) + return out + if type(obj) == list: + out = [] + for v in obj: + out.append(transformNR(v)) + return out + if isinstance(obj, OutputContainer): + out = {} + out['type'] = "OutputContainer" + items = [] + for v in obj.getItems(): + items.append(transform(v)) + out['items'] = items + return out + if isinstance(obj, BeakerCodeCell): + out = {} + out['type'] = "BeakerCodeCell" + out['cellId'] = obj.getCellId() + out['evaluatorId'] = obj.getEvaluatorId() + out['code'] = obj.getCode() + out['outputtype'] = obj.getOutputType() + out['output'] = transformNR(obj.getOutput()) + out['tags'] = obj.getTags() + return out + if isinstance(obj, BaseObject): + return obj.transform() + return transformNaN(obj) + + +def transformNR(obj): + if type(obj) == bytes: + return str(obj) + if type(obj) == dict: + out = {} + for k, v in obj.items(): + out[k] = transformNR(v) + return out + if type(obj) == list: + out = [] + for v in obj: + out.append(transformNR(v)) + return out + if isinstance(obj, OutputContainer): + out = {} + out['type'] = "OutputContainer" + items = [] + for v in obj.getItems(): + items.append(transform(v)) + out['items'] = items + return out + if isinstance(obj, BeakerCodeCell): + out = {} + out['type'] = "BeakerCodeCell" + out['cellId'] = obj.getCellId() + out['evaluatorId'] = obj.getEvaluatorId() + out['code'] = obj.getCode() + out['outputtype'] = obj.getOutputType() + out['output'] = transformNR(obj.getOutput()) + out['tags'] = obj.getTags() + return out + if isinstance(obj, BaseObject): + return obj.transform() + return transformNaN(obj) + + +def transformBack(obj): + if type(obj) == dict: + out = {} + for k, v in obj.items(): + out[str(k)] = transformBack(v) + if "type" in out: + if out['type'] == "Plot" \ + or out['type'] == "TimePlot" \ + or out['type'] == "NanoPlot" \ + or out['type'] == "SimpleTimePlot" \ + or out['type'] == "CombinedPlot": + return chart.transformBack(out) + if out['type'] == 'EasyForm': + return easyforms.transformBack(out) + if out['type'] == "BeakerCodeCell": + c = BeakerCodeCell(out['cellId'], out['evaluatorId']) + if 'code' in out: + c.code = out['code'] + if 'outputtype' in out: + c.outputtype = out['outputtype'] + if 'output' in out: + c.output = transformBack(out['output']) + if 'tags' in out: + c.tags = out['tags'] + return c + if out['type'] == "OutputContainer": + c = OutputContainer() + if 'items' in out: + for i in out['items']: + c.addItem(i) + return c + if out['type'] == "Date": + return datetime.fromtimestamp(out["timestamp"] / 1000) + if out['type'] == "TableDisplay": + if 'subtype' in out: + if out['subtype'] == "Dictionary": + out2 = {} + for r in out['values']: + out2[r[0]] = fixNaNBack(r[1]) + if out['columnNames'][0] == "Index": + return pandas.Series(out2) + return out2 + if out['subtype'] == "Matrix": + vals = out['values'] + fixNaNsBack(vals) + return numpy.matrix(vals) + if out['subtype'] == "ListOfMaps": + out2 = [] + cnames = out['columnNames'] + for r in out['values']: + out3 = {} + for i in range(len(cnames)): + if r[i] != '': + out3[cnames[i]] = r[i] + out2.append(out3) + return out2 + # transform to dataframe + if ('hasIndex' in out) and (out['hasIndex'] == "true"): + # first column becomes the index + vals = out['values'] + cnames = out['columnNames'][1:] + index = [] + for x in range(0, len(vals)): + index.append(transformBack(vals[x][0])) + v = vals[x][1:] + fixNaNsBack(v) + vals[x] = v + if len(out['indexName']) > 1: + index = pandas.MultiIndex.from_tuples(index, names=(out['indexName'])) + else: + index = pandas.Index(index, name=', '.join((out['indexName']))) + frame = pandas.DataFrame(data=vals, columns=cnames, index=index) + return frame + else: + vals = out['values'] + cnames = out['columnNames'] + for x in range(0, len(vals)): + v = vals[x] + fixNaNsBack(v) + vals[x] = v + return pandas.DataFrame(data=vals, columns=cnames) + return out + if type(obj) == list: + out = [] + for v in obj: + out.append(transformBack(v)) + return out + try: + if type(obj) == bytes: + obj = str(obj) + except Exception as e: + return obj + return obj + + +# should be inner class to BeakerX +class DataFrameEncoder(json.JSONEncoder): + def default(self, obj): + # similarly handle Panels. + # make this extensible by the user to handle their own types. + if isinstance(obj, numpy.generic): + return transformNaN(obj.item()) + if isinstance(obj, numpy.ndarray) and obj.ndim == 2: + out = {} + out['type'] = "TableDisplay" + out['subtype'] = "Matrix" + cols = [] + for i in range(obj.shape[1]): + cols.append("c" + str(i)) + out['columnNames'] = cols + vars = obj.tolist() + for x in range(0, len(vars)): + transformNaNs(vars[x]) + out['values'] = vars + return out + if isinstance(obj, numpy.ndarray): + ret = obj.tolist() + transformNaNs(ret) + return ret + if type(obj) == datetime or type(obj) == datetime.date or type(obj).__name__ == 'Timestamp': + out = {} + out['type'] = "Date" + out['timestamp'] = time.mktime(obj.timetuple()) * 1000 + return out + if type(obj) == pandas.core.frame.DataFrame: + out = {} + out['type'] = "TableDisplay" + out['subtype'] = "TableDisplay" + out['hasIndex'] = "true" + out['columnNames'] = (['Index'] if obj.index.name is None else obj.index.names) + obj.columns.tolist() + out['indexName'] = ['index'] if (len(obj.index.names) == 1) and ( + obj.index.names[0] is None) else obj.index.names + vals = obj.values.tolist() + idx = obj.index.tolist() + for x in range(0, len(vals)): + vals[x] = [idx[x]] + vals[x] + ty = [] + num = len(obj.columns.tolist()) + x = 0; + for x in range(0, num + 1): + ty.append(convertTypeName(type(vals[0][x]).__name__)) + out['types'] = ty + for x in range(0, len(vals)): + transformNaNs(vals[x]) + out['values'] = vals + return out + if type(obj) == pandas.core.series.Series: + basict = True + for i in range(len(obj)): + if not isPrimitiveType(type(obj[i]).__name__): + basict = False + break + if basict: + out = {} + out['type'] = "TableDisplay" + out['subtype'] = "Dictionary" + out['columnNames'] = ["Index", "Value"] + values = [] + for k, v in obj.items(): + values.append([k, transform(v)]) + out['values'] = values + return out + return obj.to_dict() + if type(obj).__name__ == 'Timedelta' or type(obj).__name__ == 'TimedeltaIndex': + return + return json.JSONEncoder.default(self, obj) + + +class MyJSONFormatter(IPython.core.formatters.BaseFormatter): + format_type = Unicode('application/json') + + def __call__(self, obj): + try: + obj = transform(obj) + return json.dumps(obj, cls=DataFrameEncoder) + except Exception as e: + # print(e) + # traceback.print_exc() + return None + + +class TableDisplayWrapper(object): + def __get__(self, model_instance, model_class): + def f(): + if TableDisplay is not None: + display_html(TableDisplay(model_instance)) + else: + display_html("You need beakerx_tabledisplay to display this") + + return f + + +from .beakerx_server import BeakerxZMQServer +from queue import Queue + + +class BeakerX: + + def __init__(self): + BeakerX.pandas_display_table() + self._comm = None + self._queue = Queue() + self._server = BeakerxZMQServer(self._queue) + self._url = self._server.url + + @staticmethod + def pandas_display_default(): + pandas.DataFrame._ipython_display_ = None + + @staticmethod + def pandas_display_table(): + pandas.DataFrame._ipython_display_ = TableDisplayWrapper() + + def set4(self, var, val, unset, sync): + args = {'name': var, 'sync': sync} + if not unset: + val = transform(val) + args['value'] = json.dumps(val, cls=DataFrameEncoder) + state = {'state': args} + if self._comm is None: + self.init_autotranslation_comm() + self._comm.send(data=state) + + def init_autotranslation_comm(self): + self._comm = Comm(target_name='beakerx_widgets.autotranslation') + self._comm.open() + + def get(self, var): + result = autotranslation_get(var) + if result == 'undefined': + raise NameError('name \'' + var + '\' is not defined on the beakerx_widgets object') + return transformBack(json.loads(result)) + + def set_session(self, id): + self.session_id = id + + def register_output(self): + ip = IPython.InteractiveShell.instance() + ip.display_formatter.formatters['application/json'] = MyJSONFormatter(parent=ip.display_formatter) + + def set(self, var, val): + autotranslation_update(var, val) + return self.set4(var, val, False, True) + + def unset(self, var): + return self.set4(var, None, True, True) + + def isDefined(self, var): + return autotranslation_get(var) != 'undefined' + + def createOutputContainer(self): + return OutputContainer() + + def showProgressUpdate(self): + return "WARNING: python3 language plugin does not support progress updates" + + def evaluate(self, filter): + args = {'filter': filter, 'session': self.session_id} + req = urllib.request.Request(self.core_url + '/rest/notebookctrl/evaluate', + urllib.parse.urlencode(args).encode('utf8')) + conn = self._beaker_url_opener.open(req) + result = json.loads(conn.read().decode()) + return transformBack(result) + + def evaluateCode(self, evaluator, code): + args = {'evaluator': evaluator, 'code': code, 'session': self.session_id} + req = urllib.request.Request(self.core_url + '/rest/notebookctrl/evaluateCode', + urllib.parse.urlencode(args).encode('utf8')) + conn = self._beaker_url_opener.open(req) + result = json.loads(conn.read().decode()) + return transformBack(result) + + def showStatus(self, msg): + args = {'msg': msg, 'session': self.session_id} + req = urllib.request.Request(self.core_url + '/rest/notebookctrl/showStatus', + urllib.parse.urlencode(args).encode('utf8')) + conn = self._beaker_url_opener.open(req) + result = conn.read() + return result == "true" + + def clearStatus(self, msg): + args = {'msg': msg, 'session': self.session_id} + req = urllib.request.Request(self.core_url + '/rest/notebookctrl/clearStatus', + urllib.parse.urlencode(args).encode('utf8')) + conn = self._beaker_url_opener.open(req) + result = conn.read() + return result == "true" + + def showTransientStatus(self, msg): + args = {'msg': msg, 'session': self.session_id} + req = urllib.request.Request(self.core_url + '/rest/notebookctrl/showTransientStatus', + urllib.parse.urlencode(args).encode('utf8')) + conn = self._beaker_url_opener.open(req) + result = conn.read() + return result == "true" + + def getEvaluators(self): + req = urllib.request.Request(self.core_url + '/rest/notebookctrl/getEvaluators?' + + urllib.parse.urlencode({ + 'session': self.session_id})) + conn = self._beaker_url_opener.open(req) + result = json.loads(conn.read().decode()) + return transformBack(result) + + def getVersion(self): + req = urllib.request.Request( + self.core_url + '/rest/util/version?' + urllib.parse.urlencode({'session': self.session_id})) + conn = self._beaker_url_opener.open(req) + return transformBack(conn.read().decode()) + + def getVersionNumber(self): + req = urllib.request.Request( + self.core_url + '/rest/util/getVersionInfo?' + urllib.parse.urlencode({'session': self.session_id})) + conn = self._beaker_url_opener.open(req) + result = json.loads(conn.read().decode()) + return transformBack(result['version']) + + def getCodeCells(self, filter): + req = urllib.request.Request(self.core_url + '/rest/notebookctrl/getCodeCells?' + + urllib.parse.urlencode({ + 'filter': filter})) + conn = self._beaker_url_opener.open(req) + result = json.loads(conn.read().decode()) + return transformBack(result) + + def setCodeCellBody(self, name, body): + args = {'name': name, 'body': body, 'session': self.session_id} + req = urllib.request.Request(self.core_url + '/rest/notebookctrl/setCodeCellBody', + urllib.parse.urlencode(args).encode('utf8')) + conn = self._beaker_url_opener.open(req) + result = conn.read() + return result == "true" + + def setCodeCellEvaluator(self, name, evaluator): + args = {'name': name, 'evaluator': evaluator, 'session': self.session_id} + req = urllib.request.Request(self.core_url + '/rest/notebookctrl/setCodeCellEvaluator', + urllib.parse.urlencode(args).encode('utf8')) + conn = self._beaker_url_opener.open(req) + result = conn.read() + return result == "true" + + def setCodeCellTags(self, name, tags): + args = {'name': name, 'tags': tags, 'session': self.session_id} + req = urllib.request.Request(self.core_url + '/rest/notebookctrl/setCodeCellTags', + urllib.parse.urlencode(args).encode('utf8')) + conn = self._beaker_url_opener.open(req) + result = conn.read() + return result == "true" + + def runByTag(self, tag): + arguments = dict(target_name='beakerx_widgets.tag.run') + comm = Comm(**arguments) + msg = {'runByTag': tag} + state = {'state': msg} + comm.send(data=state, buffers=[]) + + def urlArg(self, argName): + arguments = dict(target_name='beakerx_widgets.geturlarg') + comm = Comm(**arguments) + state = { + 'name': 'URL_ARG', + 'arg_name': argName + } + data = { + 'state': state, + 'url': self._url, + 'type': 'python' + } + + comm.send(data=data, buffers=[]) + data = self._queue.get() + params = json.loads(data) + return params['argValue'] + + def __setattr__(self, name, value): + if 'session_id' == name: + self.__dict__['session_id'] = value + return + if '_comm' == name: + self.__dict__['_comm'] = value + return + if '_url' == name: + self.__dict__['_url'] = value + return + if '_queue' == name: + self.__dict__['_queue'] = value + return + if '_server' == name: + self.__dict__['_server'] = value + return + return self.set(name, value) + + def __getattr__(self, name): + if '_comm' == name: + return self.__dict__['_comm'] + if '_url' == name: + return self.__dict__['_url'] + if '_queue' == name: + return self.__dict__['_queue'] + if '_server' == name: + return self.__dict__['_server'] + return self.get(name) + + def __contains__(self, name): + return self.isDefined(name) + + def __delattr__(self, name): + return self.unset(name) + + +def autotranslation_update(var, val): + session_id = get_context_session() + port = os.environ["BEAKERX_AUTOTRANSLATION_PORT"] + url = 'http://localhost:{0}/autotranslation/'.format(port) + json_data = json.dumps(transform(val), cls=DataFrameEncoder) + data = {} + data["name"] = var + data["json"] = json_data + data["sessionId"] = session_id + requests.post(url, data=json.dumps(data), headers={'Authorization': get_auth_token()}) + + +def autotranslation_get(var): + port = os.environ["BEAKERX_AUTOTRANSLATION_PORT"] + session_id = get_context_session() + url = 'http://localhost:{0}/autotranslation/{1}/{2}'.format(port, session_id, var) + result = requests.get(url, headers={'Authorization': get_auth_token()}) + return transformBack(result.content.decode()) + + +def get_auth_token(): + token_string = 'beakerx:' + os.environ['BEAKERX_AUTOTRANSLATION_PASSWORD'] + return 'Basic ' + base64.b64encode(token_string.encode('utf-8')).decode() + + +def get_context_session(): + kernel = get_ipython().kernel + # if subkernel get session from extra start parameters + if len(kernel.parent.argv) == 3: + context_json = base64.b64decode(kernel.parent.argv[2]).decode('UTF-8') + return json.loads(context_json)['contextId'] + return kernel.session.session diff --git a/beakerx_widgets/beakerx_widgets/setup.cfg b/beakerx_widgets/beakerx_widgets/setup.cfg new file mode 100644 index 0000000..3c6e79c --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal=1 diff --git a/beakerx_widgets/beakerx_widgets/spark/__init__.py b/beakerx_widgets/beakerx_widgets/spark/__init__.py new file mode 100644 index 0000000..d6838ef --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/beakerx_widgets/beakerx_widgets/spark/button_widget.py b/beakerx_widgets/beakerx_widgets/spark/button_widget.py new file mode 100644 index 0000000..a344b5f --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/button_widget.py @@ -0,0 +1,27 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC # +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from beakerx_base import BeakerxButton +from traitlets import Unicode + + +class RESTButton(BeakerxButton): + _view_name = Unicode('RESTButtonView').tag(sync=True) + _model_name = Unicode('RESTButtonModel').tag(sync=True) + _view_module = Unicode('beakerx_widgets').tag(sync=True) + _model_module = Unicode('beakerx_widgets').tag(sync=True) + tooltip = Unicode('tooltip').tag(sync=True) + url = Unicode('url').tag(sync=True) + + def __init__(self, **kwargs): + super(RESTButton, self).__init__(**kwargs) diff --git a/beakerx_widgets/beakerx_widgets/spark/profile.py b/beakerx_widgets/beakerx_widgets/spark/profile.py new file mode 100644 index 0000000..94b1abc --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/profile.py @@ -0,0 +1,62 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import os +from pathlib import Path + + +def _get_path_to_beakerx_json(): + if "JUPYTER_CONFIG_DIR" in os.environ: + path_to_beakerx_json = os.environ['JUPYTER_CONFIG_DIR'] + else: + path_to_beakerx_json = str(Path.home()) + os.path.sep + ".jupyter" + return path_to_beakerx_json + os.path.sep + "beakerx.json" + + +class Profile: + ERR = None + + def __init__(self, path_to_beakerx_json=None): + if path_to_beakerx_json is None: + path_to_beakerx_json = _get_path_to_beakerx_json() + + self.path_to_beakerx_json = path_to_beakerx_json + + def save(self, profile_to_save): + result, err = self.load_beakerx_json() + result["beakerx"]["spark_options"]["profiles"] = profile_to_save + with open(self.path_to_beakerx_json, 'w') as outfile: + self._dump(outfile, result) + return True, Profile.ERR + + def load_beakerx_json(self): + with open(self.path_to_beakerx_json) as json_file: + data = json.load(json_file) + return data, Profile.ERR + + def load_profiles(self): + with open(self.path_to_beakerx_json) as json_file: + data = json.load(json_file) + return data["beakerx"]["spark_options"], Profile.ERR + + def save_current_profile(self, current_profile): + result, err = self.load_beakerx_json() + result["beakerx"]["spark_options"]["current_profile"] = current_profile + with open(self.path_to_beakerx_json, 'w') as outfile: + self._dump(outfile, result) + return True, Profile.ERR + + def _dump(self, outfile, result): + json.dump(result, outfile, indent=2, sort_keys=True) diff --git a/beakerx_widgets/beakerx_widgets/spark/spark_engine.py b/beakerx_widgets/beakerx_widgets/spark/spark_engine.py new file mode 100644 index 0000000..fae144c --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/spark_engine.py @@ -0,0 +1,107 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# See the License for the specific language governing permissions and +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import os +import sys + +from beakerx_widgets.spark.spark_listener import SparkListener +from beakerx_widgets.spark.spark_progress_bar import SparkStateProgressUiManager + + +class SparkEngine: + STOP = "stop" + STOP_FROM_UI = "stop_from_spark_ui_form_button" + + def __init__(self, builder, single_spark_session, spark_session_factory): + self.spark_session_factory = spark_session_factory + self.single_spark_session = single_spark_session + if builder is None: + raise Exception('value can not be None') + else: + self.user_builder = builder + self.auto_start = False + self.additional_spark_options = {} + self.builder = None + self.uiWebUrlFunc = self._create_ui_web_url() + self.stop_context = SparkEngine.STOP + + def new_spark_builder(self): + self.stop_context = SparkEngine.STOP + self.uiWebUrlFunc = self._create_ui_web_url() + self.builder = self.spark_session_factory.builder() + + def get_user_spark_config(self): + return copy.deepcopy(self.user_builder._options) + + def add_additional_spark_options(self, options): + self.additional_spark_options = options + + def get_additional_spark_options(self): + return copy.deepcopy(self.additional_spark_options) + + def config(self, name, value): + self.builder.config(name, value) + + def getOrCreate(self): + return self.builder.getOrCreate() + + def spark_app_id(self): + return self.getOrCreate().sparkContext._jsc.sc().applicationId() + + def get_ui_web_url(self): + return self.uiWebUrlFunc(self.getOrCreate()) + + def stop(self): + self.stop_context = SparkEngine.STOP_FROM_UI + self.getOrCreate().sparkContext.stop() + + def is_auto_start(self): + return self.auto_start + + def configure_auto_start(self): + self.auto_start = True + + def is_active_spark_session(self): + return self.single_spark_session.active + + def activate_spark_session(self): + self.single_spark_session.active = True + + def inactivate_spark_session(self): + self.single_spark_session.active = False + + def configure_listeners(self, sparkui, server): + spark_session = sparkui.engine.getOrCreate() + spark_context = spark_session.sparkContext + spark_context._gateway.start_callback_server() + spark_context._jsc.sc().addSparkListener( + SparkListener(sparkui, SparkStateProgressUiManager(sparkui.engine, server))) + + def _create_ui_web_url(self): + return lambda spark_session: spark_session.sparkContext.uiWebUrl + + def _configure_yarn(self): + path_to_python = sys.executable + os.environ["PYSPARK_PYTHON"] = path_to_python + os.environ["PYSPARK_DRIVER_PYTHON"] = path_to_python + self.uiWebUrlFunc = lambda spark_session: spark_session.sparkContext._conf._jconf.get( + "spark.org.apache.hadoop.yarn.server.webproxy.amfilter.AmIpFilter.param.PROXY_URI_BASES") + + def configure_runtime(self): + if "spark.master" in self.builder._options and "yarn" in self.builder._options["spark.master"]: + self._configure_yarn() + return None diff --git a/beakerx_widgets/beakerx_widgets/spark/spark_factory.py b/beakerx_widgets/beakerx_widgets/spark/spark_factory.py new file mode 100644 index 0000000..7755f8f --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/spark_factory.py @@ -0,0 +1,69 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +from beakerx_widgets.spark.spark_wihtout_ui import SparkWithoutUI +from beakerx_widgets.spark.sparkex import SparkUI + + +class SparkFactory: + + def __init__(self, options, spark_engine, ipython_manager, server_factory, profile, display_func): + self.options = options + self.spark_engine = spark_engine + self.display_func = display_func + self.profile = profile + self.server_factory = server_factory + self.ipythonManager = ipython_manager + + def create_spark(self): + err = self._execute_options(self.spark_engine, self.options) + if err is not None: + return None, err + if self._is_no_ui(self.options): + return self._create_spark_without_ui() + else: + spark_widget = self._create_spark_ui() + self.display_func(spark_widget) + spark_widget.after_display() + return None, None + + def _create_spark_without_ui(self): + swui = SparkWithoutUI(self.spark_engine, self.ipythonManager) + return swui.create_spark() + + def _create_spark_ui(self): + spark_widget = SparkUI(self.spark_engine, + self.ipythonManager, + self.server_factory, + self.profile) + return spark_widget + + def _execute_options(self, engine, options): + if "start" in options and options.start: + engine.configure_auto_start() + return None + if "yarn" in options and options.yarn: + if "HADOOP_CONF_DIR" not in os.environ: + return """'HADOOP_CONF_DIR' is not set,\nplease use os.environ['HADOOP_CONF_DIR'] = PATH_TO_HADOOP_CONF_DIR""" + self.spark_engine.add_additional_spark_options({ + "spark.executor.cores": "4", + "spark.executor.memory": "1g", + "spark.master": "yarn" + }) + return None + + def _is_no_ui(self, options): + return "noUI" in options and options.noUI diff --git a/beakerx_widgets/beakerx_widgets/spark/spark_listener.py b/beakerx_widgets/beakerx_widgets/spark/spark_listener.py new file mode 100644 index 0000000..9d7070d --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/spark_listener.py @@ -0,0 +1,99 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from IPython.display import display +from beakerx_widgets.spark.spark_progress_bar import SparkFoldout, SparkStateProgressWidget, SparkStateGroupPanel + + +class SparkListener: + + def __init__(self, sparkui, manager): + self.manager = manager + self.sparkui = sparkui + self.group = None + self.bar = None + self.jobPanel = None + + def onApplicationEnd(self, applicationEnd): + self.sparkui.end_application() + + def onApplicationStart(self, applicationStart): + pass + + def onStageSubmitted(self, stageSubmitted): + self.jobPanel = SparkFoldout() + display(self.jobPanel) + numberOfTasks = stageSubmitted.stageInfo().numTasks() + stageId = stageSubmitted.stageInfo().stageId() + self.bar = SparkStateProgressWidget(numberOfTasks, + stageId, + stageId, + self.manager.job_link(stageId), + self.manager.stage_link(stageId)) + self.bar.init() + xButton = self.manager.create_cancelled_jobs_button(stageId) + # bgButton = self.manager.create_bg_jobs_button(stageId) + self.group = SparkStateGroupPanel() + self.group.children += (self.bar, xButton) + # self.group.children += (self.bar, xButton,bgButton) + self.jobPanel.children += (self.group,) + + def onTaskStart(self, taskStart): + self.bar.addActive() + + def onTaskEnd(self, taskEnd): + reason = taskEnd.reason().toString() + if reason == "Success": + self.bar.addDone() + elif "Stage cancelled" in reason: + self.bar.addCancelled() + + def onBlockManagerRemoved(self, blockManagerRemoved): + pass + + def onBlockUpdated(self, blockUpdated): + pass + + def onEnvironmentUpdate(self, environmentUpdate): + pass + + def onExecutorAdded(self, executorAdded): + pass + + def onExecutorMetricsUpdate(self, executorMetricsUpdate): + pass + + def onExecutorRemoved(self, executorRemoved): + pass + + def onJobEnd(self, jobEnd): + pass + + def onJobStart(self, jobStart): + pass + + def onOtherEvent(self, event): + pass + + def onStageCompleted(self, stageCompleted): + pass + + def onTaskGettingResult(self, taskGettingResult): + pass + + def onUnpersistRDD(self, unpersistRDD): + pass + + class Java: + implements = ["org.apache.spark.scheduler.SparkListenerInterface"] diff --git a/beakerx_widgets/beakerx_widgets/spark/spark_progress_bar.py b/beakerx_widgets/beakerx_widgets/spark/spark_progress_bar.py new file mode 100644 index 0000000..ded664a --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/spark_progress_bar.py @@ -0,0 +1,117 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from beakerx_base import BeakerxBox, BaseObject, BeakerxHBox +from beakerx_widgets.spark.button_widget import RESTButton +from traitlets import Unicode, Dict, Bool + + +class SparkStateProgress(BaseObject): + + def __init__(self, numberOfTasks, jobId, stageId, jobLink, stageLink, **kwargs): + self.active = 0 + self.done = 0 + self.numberOfTasks = numberOfTasks + self.cancelled = 0 + self.jobId = jobId + self.stageId = stageId + self.jobLink = jobLink + self.stageLink = stageLink + + +class SparkStateProgressWidget(BeakerxBox): + _view_name = Unicode('SparkStateProgressView').tag(sync=True) + _model_name = Unicode('SparkStateProgressModel').tag(sync=True) + _view_module = Unicode('beakerx_widgets').tag(sync=True) + _model_module = Unicode('beakerx_widgets').tag(sync=True) + _model_module_version = Unicode('*').tag(sync=True) + _view_module_version = Unicode('*').tag(sync=True) + + state = Dict().tag(sync=True) + + def __init__(self, numberOfTasks, jobId, stageId, jobLink, stageLink, **kwargs): + super(SparkStateProgressWidget, self).__init__(**kwargs) + self.bar = SparkStateProgress(numberOfTasks, jobId, stageId, jobLink, stageLink, **kwargs) + self.state = self.bar.transform() + + def init(self): + self.bar.done = 0 + self.bar.active = 0 + self.state = self.bar.transform() + return self + + def addActive(self): + self.bar.active += 1 + self.state = self.bar.transform() + return self + + def addDone(self): + self.bar.done += 1 + self.bar.active -= 1 + self.state = self.bar.transform() + return self + + def addCancelled(self): + self.bar.cancelled += 1 + self.bar.active -= 1 + self.state = self.bar.transform() + return self + + +class SparkFoldout(BeakerxBox): + _view_name = Unicode('SparkFoldoutView').tag(sync=True) + _model_name = Unicode('SparkFoldoutModel').tag(sync=True) + _view_module = Unicode('beakerx_widgets').tag(sync=True) + _model_module = Unicode('beakerx_widgets').tag(sync=True) + hidePreview = Bool(False).tag(sync=True) + headerLabel = Unicode('Spark progress').tag(sync=True) + + +class SparkStateGroupPanel(BeakerxHBox): + _dom_classes = ["bx-spark-stageGroupPanel"] + + +class SparkStateProgressUiManager: + CANCELLED_SPARK_JOBS = "cancelledSparkJobs" + PUT_SPARK_JOBS_IN_THE_BACKGROUND = "putSparkJobsInTheBackground" + + def __init__(self, engine, spark_server): + self.engine = engine + self.spark_server = spark_server + + def job_link(self, job_id): + if self.engine.get_ui_web_url() is not None: + return self.engine.get_ui_web_url() + "/jobs/job/?id=" + str(job_id) + else: + return "" + + def stage_link(self, stageId): + if self.engine.get_ui_web_url() is not None: + return self.engine.get_ui_web_url() + "/stages/stage/?id=" + str(stageId) + "&attempt=0" + else: + return "" + + def create_cancelled_jobs_button(self, stageId): + xButton = RESTButton() + xButton.url = self.spark_server.getURL() + self.CANCELLED_SPARK_JOBS + "/" + str(stageId) + xButton.tooltip = "interrupt spark job" + xButton._dom_classes = ["bx-button", "icon-close"] + return xButton + + def create_bg_jobs_button(self, stageId): + xButton = RESTButton() + xButton.url = self.spark_server.getURL() + self.PUT_SPARK_JOBS_IN_THE_BACKGROUND + xButton.tooltip = "put spark job in the background, let it complete asynchronously" + xButton._dom_classes = ["bx-button", "icon-bg"] + return xButton diff --git a/beakerx_widgets/beakerx_widgets/spark/spark_server.py b/beakerx_widgets/beakerx_widgets/spark/spark_server.py new file mode 100644 index 0000000..e490425 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/spark_server.py @@ -0,0 +1,49 @@ +# Copyright 2019 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import socket + +from bottle import Bottle, run + + +class BeakerxSparkServer: + + def __init__(self, spark_context): + self.spark_context = spark_context + self.app = Bottle() + self.port = self.get_free_tcp_port() + self.hostname = "http://localhost:" + str(self.port) + "/" + self.jobGroup = "jobGroup" + str(self.port) + self.spark_context.setJobGroup(self.jobGroup, self.jobGroup) + self.app.route('/cancelledSparkJobs/', method="POST", callback=self.cancelSparkJobs) + self.app.route('/putSparkJobsInTheBackground', method="POST", callback=self.putSparkJobsInTheBackground) + + def get_free_tcp_port(self): + tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + tcp.bind(('localhost', 0)) + addr, port = tcp.getsockname() + tcp.close() + return port + + def run(self): + run(self.app, host='localhost', port=self.port, quiet=True) + + def getURL(self): + return self.hostname + + def cancelSparkJobs(self, stageId): + self.spark_context.cancelJobGroup(self.jobGroup) + + def putSparkJobsInTheBackground(self): + pass diff --git a/beakerx_widgets/beakerx_widgets/spark/spark_wihtout_ui.py b/beakerx_widgets/beakerx_widgets/spark/spark_wihtout_ui.py new file mode 100644 index 0000000..c7e0ddc --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/spark_wihtout_ui.py @@ -0,0 +1,25 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class SparkWithoutUI: + + def __init__(self, engine, ipython_manager): + self.ipython_manager = ipython_manager + self.engine = engine + + def create_spark(self): + self.engine.new_spark_builder() + self.ipython_manager.configure(self.engine) + return "SparkSession is available by 'spark'", None diff --git a/beakerx_widgets/beakerx_widgets/spark/sparkex.py b/beakerx_widgets/beakerx_widgets/spark/sparkex.py new file mode 100644 index 0000000..5e1706a --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/sparkex.py @@ -0,0 +1,214 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy + +from beakerx_base import BeakerxBox +from beakerx_widgets.spark.spark_server import BeakerxSparkServer +from traitlets import Unicode, List, Bool, Dict + + +class SparkUI(BeakerxBox): + ONE_SPARK_SESSION_MSG_ERROR = "Cannot have more than one Spark session open in the same notebook." + + _view_name = Unicode('SparkUIView').tag(sync=True) + _model_name = Unicode('SparkUIModel').tag(sync=True) + _view_module = Unicode('beakerx_widgets').tag(sync=True) + _model_module = Unicode('beakerx_widgets').tag(sync=True) + profiles = List().tag(sync=True) + current_profile = Unicode("").tag(sync=True) + is_auto_start = Bool().tag(sync=True) + user_spark_conf = Dict().tag(sync=True) + + def __init__(self, engine, ipython_manager, spark_server_factory, profile, comm=None, **kwargs): + if comm is not None: + self.comm = comm + self.engine = self.check_is_None(engine) + self.ipython_manager = self.check_is_None(ipython_manager) + self.spark_server_factory = self.check_is_None(spark_server_factory) + self.profile = self.check_is_None(profile) + self.on_msg(self.handle_msg) + self.profiles, self.current_profile = self._get_init_profiles() + self.user_spark_conf = self.get_user_spark_conf() + self.is_auto_start = self.engine.is_auto_start() + super(SparkUI, self).__init__(**kwargs) + + def get_user_spark_conf(self): + spark_options = self._get_current_profile() + spark_options.update(self.engine.get_additional_spark_options()) + spark_options.update(self.engine.get_user_spark_config()) + return spark_options + + def handle_msg(self, _, content, buffers=None): + try: + if content['event'] == "start": + self._handle_start(content) + elif content['event'] == "stop": + self._handle_stop(content) + elif content['event'] == "save_profiles": + self._handle_save_profile(content) + except Exception as err: + self.send_error(str(err)) + + def _handle_save_profile(self, content): + payload = content["payload"] + result, err = self.profile.save(payload) + if result: + msg = { + 'method': 'update', + 'event': { + "save_profiles": "done" + } + } + self.comm.send(data=msg) + + def _handle_stop(self, content): + self.engine.stop() + + def end_application(self): + self.engine.inactivate_spark_session() + msg = { + 'method': 'update', + 'event': { + self.engine.stop_context: "done" + } + } + self.comm.send(data=msg) + + def _handle_auto_start(self): + spark_options = self._get_current_profile() + spark_options.update(self.engine.get_user_spark_config()) + self.engine.new_spark_builder() + for key, value in spark_options.items(): + if key == "properties": + for item in value: + self.engine.config(item['name'], item['value']) + else: + self.engine.config(key, value) + err = self._on_start() + if err is not None: + self.send_error(err) + return + self._send_start_done_event("auto_start") + + def _handle_start(self, content): + current_profile = content['payload']['current_profile'] + spark_options = content['payload']['spark_options'] + self.engine.new_spark_builder() + for key, value in spark_options.items(): + if key == "properties": + for item in value: + self.engine.config(item['name'], item['value']) + else: + self.engine.config(key, value) + err = self._on_start() + if err is not None: + self.send_error(err) + return + self._send_start_done_event("start") + self.profile.save_current_profile(current_profile) + + def send_error(self, message): + msg = { + 'method': 'update', + 'error': { + "message": message + } + } + self.comm.send(data=msg) + + def _on_start(self): + if self.engine.is_active_spark_session(): + return SparkUI.ONE_SPARK_SESSION_MSG_ERROR + self.engine.configure_runtime() + self.ipython_manager.configure(self.engine) + server = self.spark_server_factory.run_new_instance(self.engine) + self.engine.configure_listeners(self, server) + self.engine.activate_spark_session() + return None + + def _send_start_done_event(self, event_name): + msg = { + 'method': 'update', + 'event': { + event_name: "done", + "sparkAppId": self.engine.spark_app_id(), + "sparkUiWebUrl": self.engine.get_ui_web_url() + } + } + self.comm.send(data=msg) + + def after_display(self): + if self.engine.is_auto_start(): + self._handle_auto_start() + + def check_is_None(self, value): + if value is None: + raise Exception('value can not be None') + return value + + def _get_init_profiles(self): + data, err = self.profile.load_profiles() + if err is None: + return data["profiles"], data["current_profile"] + return [], "", err + + def _get_current_profile(self): + spark_options = list(filter(lambda x: x['name'] == self.current_profile, self.profiles)) + if len(spark_options) > 0: + return copy.deepcopy(spark_options.pop(0)) + else: + return {} + + +class SparkJobRunner: + def _task(self, spark_job, ipython, builder, spark_server): + spark_job(ipython, builder, spark_server) + + def run(self, spark_job, ipython, builder, spark_server): + self._task(spark_job, ipython, builder, spark_server) + + +class ServerRunner: + + def _start_server(self, server): + server.run() + + def run(self, server): + from threading import Thread + t = Thread(target=self._start_server, args=(server,)) + t.daemon = True + t.start() + + +class BeakerxSparkServerFactory: + + def run_new_instance(self, engine): + spark_session = engine.getOrCreate() + spark_context = spark_session.sparkContext + server = BeakerxSparkServer(spark_context) + ServerRunner().run(server) + return server + + +class IpythonManager: + def __init__(self, ipython): + self.ipython = ipython + + def configure(self, engine): + spark_session = engine.getOrCreate() + sc = spark_session.sparkContext + self.ipython.push({"spark": spark_session}) + self.ipython.push({"sc": sc}) + return sc diff --git a/beakerx_widgets/beakerx_widgets/spark/tests/__init__.py b/beakerx_widgets/beakerx_widgets/spark/tests/__init__.py new file mode 100644 index 0000000..eb83728 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/beakerx_widgets/beakerx_widgets/spark/tests/mocks.py b/beakerx_widgets/beakerx_widgets/spark/tests/mocks.py new file mode 100644 index 0000000..bac5028 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/tests/mocks.py @@ -0,0 +1,142 @@ +# Copyright 2018 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from beakerx_widgets.magics.sparkex_magic import SingleSparkSession +from beakerx_widgets.spark.spark_engine import SparkEngine +from ipykernel.comm import Comm + + +class SingleSparkSessionMock(SingleSparkSession): + + def __init__(self) -> None: + super().__init__() + + +class CommMock(Comm): + + def __init__(self): + self.message = None + + def send(self, *args, **kwargs): + self.message = kwargs["data"] + + +class BuilderMock: + + def __init__(self): + self._options = {} + + def getOrCreate(self): + return SparkSessionMock() + + def config(self, key=None, value=None, conf=None): + pass + + +class SparkEngineMock(SparkEngine): + + def __init__(self, builder, single_spark_session, spark_session_factory): + super().__init__(builder, single_spark_session, spark_session_factory) + self.sparkui = None + + def spark_app_id(self): + return 'appIdLocal1' + + def configure_listeners(self, sparkui, server): + self.sparkui = sparkui + + def get_user_spark_config(self): + return { + "prop_1": "user_value_1" + } + + def getOrCreate(self): + return {} + + def stop(self): + self.sparkui.end_application() + + def get_ui_web_url(self): + return 'SparkUiWebUrl1' + + +class SparkSessionMock: + def __init__(self): + pass + + @property + def sparkContext(self): + return SparkContextMock() + + +class SparkContextMock: + def __init__(self): + pass + + def stop(self): + pass + + +class IpythonManagerMock: + + def __init__(self): + pass + + def configure(self, spark): + pass + + +class SparkServerFactoryMock: + + def __init__(self): + pass + + def run_new_instance(self, spark_context): + pass + + +class ProfileMock: + err = None + + def __init__(self): + self.spark_options = { + "current_profile": "", + "profiles": [ + { + "name": "", + "prop_1": "init_value_1" + } + ] + } + + def save(self, content): + self.spark_options["profiles"] = content + return True, ProfileMock.err + + def load_profiles(self): + return self.spark_options, ProfileMock.err + + def save_current_profile(self, current_profile): + self.spark_options["current_profile"] = current_profile + return True, ProfileMock.err + + +class SparkSessionFactoryMock: + def builder(self): + return BuilderMock() + + +class SparkStateProgressUiManagerMock: + def __init__(self, engine): + self.engine = engine diff --git a/beakerx_widgets/beakerx_widgets/spark/tests/resources/beakerxMock.json b/beakerx_widgets/beakerx_widgets/spark/tests/resources/beakerxMock.json new file mode 100644 index 0000000..31287cc --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/tests/resources/beakerxMock.json @@ -0,0 +1,64 @@ +{ + "beakerx": { + "jvm_options": { + "heap_GB": null, + "other": [], + "properties": [] + }, + "spark_options": { + "current_profile": "", + "profiles": [ + { + "spark.executor.memory": "1g", + "spark.master": "local[4]", + "name": "", + "spark.executor.cores": "4", + "properties": [ + { + "name": "prop1", + "value": "value1" + }, + { + "name": "prop2", + "value": "value2" + } + ] + }, + { + "spark.executor.memory": "1g", + "spark.master": "local[4]", + "name": "profile1", + "spark.executor.cores": "4", + "properties": [ + { + "name": "prop3", + "value": "value3" + } + ] + }, + { + "spark.executor.memory": "1g", + "spark.master": "local[4]", + "name": "profile2", + "spark.executor.cores": "4", + "properties": [ + { + "name": "prop4", + "value": "value4" + } + ] + } + ] + }, + "ui_options": { + "auto_close": true, + "auto_save": true, + "improve_fonts": true, + "show_catalog": true, + "show_publication": true, + "use_data_grid": true, + "wide_cells": true + }, + "version": 2 + } +} \ No newline at end of file diff --git a/beakerx_widgets/beakerx_widgets/spark/tests/test_profile.py b/beakerx_widgets/beakerx_widgets/spark/tests/test_profile.py new file mode 100644 index 0000000..15d1250 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/tests/test_profile.py @@ -0,0 +1,83 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import pathlib +import unittest +from shutil import copyfile + +from beakerx_widgets.spark.profile import Profile + + +class TestSparkUIProfile(unittest.TestCase): + MOCK_TEST_JSON = str( + pathlib.Path(__file__).parent.absolute()) + os.path.sep + "resources" + os.path.sep + "beakerxMock2.json" + MOCK_JSON = str( + pathlib.Path(__file__).parent.absolute()) + os.path.sep + "resources" + os.path.sep + "beakerxMock.json" + + def test_should_load_profiles(self): + # given + sut = Profile(path_to_beakerx_json=TestSparkUIProfile.MOCK_JSON) + # when + result, err = sut.load_profiles() + # then + self.assertTrue(err is None) + self.assertTrue("profiles" in result) + + def test_should_save_profile(self): + # given + copyfile(TestSparkUIProfile.MOCK_JSON, TestSparkUIProfile.MOCK_TEST_JSON) + sut = Profile(path_to_beakerx_json=TestSparkUIProfile.MOCK_TEST_JSON) + profile_to_save = [ + { + 'name': 'pf_new1', + 'spark.executor.memory': '8g', + 'spark.master': 'local[10]', + 'spark.executor.cores': '10', + 'properties': [] + }, + { + 'name': 'pf_new2', + 'spark.executor.memory': '8g', + 'spark.master': 'local[10]', + 'spark.executor.cores': '10', + 'properties': [{ + "name": "prop1", + "value": "value1" + }] + } + ] + # when + result, err = sut.save(profile_to_save) + # then + self.assertTrue(err is None) + self.assertTrue(result) + result, err = sut.load_profiles() + profiles = result["profiles"] + self.assertTrue("pf_new" in profiles[0]["name"]) + os.remove(TestSparkUIProfile.MOCK_TEST_JSON) + + def test_should_save_current_profile(self): + # given + copyfile(TestSparkUIProfile.MOCK_JSON, TestSparkUIProfile.MOCK_TEST_JSON) + sut = Profile(path_to_beakerx_json=TestSparkUIProfile.MOCK_TEST_JSON) + # when + result, err = sut.save_current_profile("new_profile_name1") + # then + self.assertTrue(err is None) + self.assertTrue(result) + result, err = sut.load_profiles() + current_profile = result["current_profile"] + self.assertTrue(current_profile == "new_profile_name1") + os.remove(TestSparkUIProfile.MOCK_TEST_JSON) diff --git a/beakerx_widgets/beakerx_widgets/spark/tests/test_spark.py b/beakerx_widgets/beakerx_widgets/spark/tests/test_spark.py new file mode 100644 index 0000000..ea3abda --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/tests/test_spark.py @@ -0,0 +1,230 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from beakerx_widgets.spark.sparkex import SparkUI +from beakerx_widgets.spark.tests.mocks import BuilderMock, SingleSparkSessionMock, SparkEngineMock, \ + SparkSessionFactoryMock, IpythonManagerMock, SparkServerFactoryMock, ProfileMock, CommMock + + +class TestSparkUI(unittest.TestCase): + + def test_should_load_profile_on_widget_creation(self): + # given + builder = BuilderMock() + spark_session_mock = SingleSparkSessionMock() + engine = SparkEngineMock(builder, spark_session_mock, SparkSessionFactoryMock()) + ipython_manager = IpythonManagerMock() + spark_server_factory = SparkServerFactoryMock() + profile = ProfileMock() + # when + sui = SparkUI(engine, ipython_manager, spark_server_factory, profile, CommMock()) + # then + self.assertTrue(sui.profiles == [ + { + "name": "", + "prop_1": "init_value_1" + } + ]) + self.assertTrue(sui.current_profile == "") + + def test_should_create_spark_conf_based_on_user_conf_when_widget_creation(self): + # given + builder = BuilderMock() + spark_session_mock = SingleSparkSessionMock() + engine = SparkEngineMock(builder, spark_session_mock, SparkSessionFactoryMock()) + ipython_manager = IpythonManagerMock() + spark_server_factory = SparkServerFactoryMock() + profile = ProfileMock() + # when + sui = SparkUI(engine, ipython_manager, spark_server_factory, profile, CommMock()) + # then + self.assertTrue(sui.user_spark_conf == { + "name": "", + "prop_1": "user_value_1" + }) + + def test_should_save_profiles(self): + # given + builder = BuilderMock() + spark_session_mock = SingleSparkSessionMock() + engine = SparkEngineMock(builder, spark_session_mock, SparkSessionFactoryMock()) + ipython_manager = IpythonManagerMock() + spark_server_factory = SparkServerFactoryMock() + profile = ProfileMock() + sui = SparkUI(engine, ipython_manager, spark_server_factory, profile, CommMock()) + msg_save_profile = { + "event": "save_profiles", + "payload": [ + { + "spark.executor.memory": "8g", + "spark.master": "local[10]", + "name": "new_prof_1", + "spark.executor.cores": "10", + "properties": [] + } + ] + } + # when + sui.handle_msg(sui, msg_save_profile) + # then + result, err = profile.load_profiles() + self.assertTrue(result["profiles"][0]["name"] == "new_prof_1") + self.assertTrue(err is None) + self.assertTrue(sui.comm.message["method"] == "update") + event = sui.comm.message["event"] + self.assertTrue(event["save_profiles"] == "done") + + def test_should_send_done_message_when_sc_stops(self): + # given + builder = BuilderMock() + spark_session_mock = SingleSparkSessionMock() + engine = SparkEngineMock(builder, spark_session_mock, SparkSessionFactoryMock()) + ipython_manager = IpythonManagerMock() + spark_server_factory = SparkServerFactoryMock() + profile = ProfileMock() + sui = SparkUI(engine, ipython_manager, spark_server_factory, profile, CommMock()) + msg_start = self.create_msg_start() + sui.handle_msg(sui, msg_start) + msg_stop = { + 'event': 'stop' + } + # when + sui.handle_msg(sui, msg_stop) + # then + self.assertTrue(sui.comm.message["method"] == "update") + event = sui.comm.message["event"] + self.assertTrue(event["stop"] == "done") + + def test_should_send_done_message_when_sc_starts(self): + # given + builder = BuilderMock() + spark_session_mock = SingleSparkSessionMock() + engine = SparkEngineMock(builder, spark_session_mock, SparkSessionFactoryMock()) + ipython_manager = IpythonManagerMock() + spark_server_factory = SparkServerFactoryMock() + profile = ProfileMock() + sui = SparkUI(engine, ipython_manager, spark_server_factory, profile, CommMock()) + msg_start = self.create_msg_start() + # when + sui.handle_msg(sui, msg_start) + # then + self.assertTrue(sui.comm.message["method"] == "update") + event = sui.comm.message["event"] + self.assertTrue(event["start"] == "done") + self.assertTrue(event["sparkAppId"] == "appIdLocal1") + self.assertTrue(event["sparkUiWebUrl"] == "SparkUiWebUrl1") + + def create_msg_start(self): + return { + 'event': 'start', + 'payload': { + "current_profile": "profile1", + "spark_options": { + 'spark.executor.memory': '8g', + 'spark.master': 'local[10]', + 'properties': [ + { + "name": "wwww", + "value": "wwwww" + } + ] + } + } + } + + def test_should_save_current_profile_when_sc_starts(self): + # given + builder = BuilderMock() + spark_session_mock = SingleSparkSessionMock() + engine = SparkEngineMock(builder, spark_session_mock, SparkSessionFactoryMock()) + ipython_manager = IpythonManagerMock() + spark_server_factory = SparkServerFactoryMock() + profile = ProfileMock() + sui = SparkUI(engine, ipython_manager, spark_server_factory, profile, CommMock()) + msg_start = { + 'event': 'start', + 'payload': { + "current_profile": "profile1", + "spark_options": { + 'spark.executor.memory': '8g', + 'spark.master': 'local[10]', + 'properties': [] + } + } + } + # when + sui.handle_msg(sui, msg_start) + # then + self.assertTrue(profile.spark_options["current_profile"] == "profile1") + + def test_should_not_create_sc_when_builder_is_None(self): + # given + engine = None + spark_server_factory = SparkServerFactoryMock() + ipython = IpythonManagerMock() + profile = ProfileMock() + # when + try: + SparkUI(engine, ipython, spark_server_factory, profile) + self.fail("builder is None") + except Exception as err: + self.assertTrue("value can not be None" in str(err), "Should not create SparkUI when builder is None") + # then + + def test_should_not_create_sc_when_ipython_is_None(self): + # given + builder = BuilderMock() + spark_session_mock = SingleSparkSessionMock() + engine = SparkEngineMock(builder, spark_session_mock, SparkSessionFactoryMock()) + spark_server_factory = SparkServerFactoryMock() + profile = ProfileMock() + ipython = None + # when + try: + SparkUI(engine, ipython, spark_server_factory, profile) + self.fail("ipython is None") + except Exception as err: + self.assertTrue("value can not be None" in str(err), "Should not create SparkUI when ipython is None") + # then + + def test_should_not_create_sc_when_factory_is_None(self): + # given + builder = BuilderMock() + spark_session_mock = SingleSparkSessionMock() + engine = SparkEngineMock(builder, spark_session_mock, SparkSessionFactoryMock()) + ipython = IpythonManagerMock() + profile = ProfileMock() + spark_server_factory = None + # when + try: + SparkUI(engine, ipython, spark_server_factory, profile) + self.fail("spark server factory is None") + except Exception as err: + self.assertTrue("value can not be None" in str(err), "Should not create SparkUI when factory is None") + # then + + def test_should_create_sc(self): + # given + spark_server_factory = SparkServerFactoryMock() + builder = BuilderMock() + spark_session_mock = SingleSparkSessionMock() + engine = SparkEngineMock(builder, spark_session_mock, SparkSessionFactoryMock()) + ipython = IpythonManagerMock() + profile = ProfileMock() + # when + spark_ui = SparkUI(engine, ipython, spark_server_factory, profile, CommMock()) + # then + self.assertTrue(spark_ui) diff --git a/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_auto_connect.py b/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_auto_connect.py new file mode 100644 index 0000000..64db2a6 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_auto_connect.py @@ -0,0 +1,43 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from beakerx_widgets.spark.sparkex import SparkUI +from beakerx_widgets.spark.tests.mocks import IpythonManagerMock, BuilderMock, SparkServerFactoryMock, ProfileMock, \ + CommMock, SingleSparkSessionMock, SparkEngineMock, SparkSessionFactoryMock + + +class TestSparkAutoConnect(unittest.TestCase): + + def test_auto_connect_spark(self): + # given + ipython_manager = IpythonManagerMock() + builder = BuilderMock() + spark_server_factory = SparkServerFactoryMock() + profile = ProfileMock() + comm_mock = CommMock() + spark_session_mock = SingleSparkSessionMock() + engine = SparkEngineMock(builder, spark_session_mock, SparkSessionFactoryMock()) + engine.configure_auto_start() + sui = SparkUI(engine, ipython_manager, spark_server_factory, profile, comm_mock) + # when + sui.after_display() + # then + self.assertTrue(sui.is_auto_start) + self.assertTrue(sui.comm.message["method"] == "update") + event = sui.comm.message["event"] + self.assertTrue(event["auto_start"] == "done") + self.assertTrue(event["sparkAppId"] == "appIdLocal1") + self.assertTrue(event["sparkUiWebUrl"] == "SparkUiWebUrl1") diff --git a/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_engine.py b/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_engine.py new file mode 100644 index 0000000..2cae03a --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_engine.py @@ -0,0 +1,42 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from beakerx_widgets.spark.spark_engine import SparkEngine +from beakerx_widgets.spark.tests.mocks import SingleSparkSessionMock, BuilderMock, SparkSessionFactoryMock + + +class TestSparkEngine(unittest.TestCase): + + def test_should_reset_spark_stop_context(self): + # given + builder = BuilderMock() + single_spark_session = SingleSparkSessionMock + sut = SparkEngine(builder, single_spark_session, SparkSessionFactoryMock()) + # when + sut.new_spark_builder() + # then + self.assertTrue(sut.stop_context == SparkEngine.STOP) + + def test_should_set_spark_stop_context_to_from_ui_when_stop(self): + # given + builder = BuilderMock() + single_spark_session = SingleSparkSessionMock + sut = SparkEngine(builder, single_spark_session, SparkSessionFactoryMock()) + sut.new_spark_builder() + # when + sut.stop() + # then + self.assertTrue(sut.stop_context == SparkEngine.STOP_FROM_UI) diff --git a/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_listener.py b/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_listener.py new file mode 100644 index 0000000..293bab6 --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_listener.py @@ -0,0 +1,41 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from beakerx_widgets.spark.spark_listener import SparkListener +from beakerx_widgets.spark.sparkex import SparkUI +from beakerx_widgets.spark.tests.mocks import BuilderMock, SingleSparkSessionMock, SparkEngineMock, \ + SparkStateProgressUiManagerMock, CommMock, IpythonManagerMock, SparkServerFactoryMock, ProfileMock, \ + SparkSessionFactoryMock + + +class TestSparkListener(unittest.TestCase): + + def test_should_inactivate_single_spark_session_when_application_end(self): + # given + builder = BuilderMock() + spark_session_mock = SingleSparkSessionMock() + engine = SparkEngineMock(builder, spark_session_mock, SparkSessionFactoryMock()) + engine.activate_spark_session() + self.assertTrue(engine.is_active_spark_session()) + ipython_manager = IpythonManagerMock() + spark_server_factory = SparkServerFactoryMock() + profile = ProfileMock() + sparkUi = SparkUI(engine, ipython_manager, spark_server_factory, profile, CommMock()) + listener = SparkListener(sparkUi, SparkStateProgressUiManagerMock(engine)) + # when + listener.onApplicationEnd(None) + # then + self.assertFalse(engine.is_active_spark_session()) diff --git a/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_no_ui.py b/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_no_ui.py new file mode 100644 index 0000000..31a270e --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_no_ui.py @@ -0,0 +1,95 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import unittest + +from beakerx_widgets.magics.tests.mocks import display_func_mock +from beakerx_widgets.spark.spark_factory import SparkFactory +from beakerx_widgets.spark.tests.mocks import ProfileMock, SparkServerFactoryMock + + +class TestSparkNoUI(unittest.TestCase): + + def test_empty_options(self): + # given + parser = argparse.ArgumentParser(description='spark options.') + options = parser.parse_args() + sut = SparkFactory(options, + SparkEngineMock(), + IpythonManagerMock(), + SparkServerFactoryMock(), + ProfileMock(), + display_func_mock) + # when + spark_message = sut.create_spark() + # then + self.assertEqual(spark_message, (None, None)) + + def test_auto_connect_when_no_ui(self): + # given + parser = argparse.ArgumentParser(description='spark options') + parser.add_argument('--noUI', '-nu', action='store_true', help='no UI') + options = parser.parse_args() + options.noUI = True + ipython_manager_mock = IpythonManagerMock() + sut = SparkFactory(options, SparkEngineMock(), ipython_manager_mock, SparkServerFactoryMock(), ProfileMock(), + display_func_mock) + # when + spark_message = sut.create_spark() + # then + self.assertTrue(ipython_manager_mock.configured) + self.assertEqual(spark_message, ("SparkSession is available by 'spark'", None)) + + +class SparkEngineMock: + + def __init__(self): + self.auto_start = False + + def new_spark_builder(self): + pass + + def getOrCreate(self): + pass + + def spark_app_id(self): + pass + + def ui_web_url(self): + pass + + def stop(self): + pass + + def is_auto_start(self): + return self.auto_start + + def configure_auto_start(self): + self.auto_start = True + + def get_additional_spark_options(self): + return {} + + def get_user_spark_config(self): + return {} + + +class IpythonManagerMock: + + def __init__(self): + self.configured = False + + def configure(self, engine): + self.configured = True diff --git a/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_single_session.py b/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_single_session.py new file mode 100644 index 0000000..745c81c --- /dev/null +++ b/beakerx_widgets/beakerx_widgets/spark/tests/test_spark_single_session.py @@ -0,0 +1,74 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from beakerx_widgets.spark.sparkex import SparkUI +from beakerx_widgets.spark.tests.mocks import BuilderMock, SingleSparkSessionMock, SparkEngineMock, \ + SparkSessionFactoryMock, IpythonManagerMock, SparkServerFactoryMock, ProfileMock, CommMock + + +class TestSparkUI(unittest.TestCase): + + def test_should_send_single_spark_session_error_message_when_second_sc_starts(self): + # given + builder = BuilderMock() + spark_session_mock = SingleSparkSessionMock() + engine = SparkEngineMock(builder, spark_session_mock, SparkSessionFactoryMock()) + engine.activate_spark_session() + ipython_manager = IpythonManagerMock() + spark_server_factory = SparkServerFactoryMock() + profile = ProfileMock() + sui = SparkUI(engine, ipython_manager, spark_server_factory, profile, CommMock()) + msg_start = { + 'event': 'start', + 'payload': { + "current_profile": "profile1", + "spark_options": { + 'spark.executor.memory': '8g', + 'spark.master': 'local[10]', + 'properties': [ + { + "name": "wwww", + "value": "wwwww" + } + ] + } + } + } + # when + sui.handle_msg(sui, msg_start) + # then + self.assertTrue(sui.comm.message["method"] == "update") + error = sui.comm.message["error"] + self.assertTrue(error["message"] == SparkUI.ONE_SPARK_SESSION_MSG_ERROR) + + def test_should_send_single_spark_session_error_message_when_auto_connect_spark_try_start_second_spark(self): + # given + ipython_manager = IpythonManagerMock() + builder = BuilderMock() + spark_server_factory = SparkServerFactoryMock() + profile = ProfileMock() + comm_mock = CommMock() + spark_session_mock = SingleSparkSessionMock() + engine = SparkEngineMock(builder, spark_session_mock, SparkSessionFactoryMock()) + engine.activate_spark_session() + engine.configure_auto_start() + sui = SparkUI(engine, ipython_manager, spark_server_factory, profile, comm_mock) + # when + sui.after_display() + # then + self.assertTrue(sui.comm.message["method"] == "update") + error = sui.comm.message["error"] + self.assertTrue(error["message"] == SparkUI.ONE_SPARK_SESSION_MSG_ERROR) diff --git a/beakerx_widgets/conda_recipe/meta.yaml b/beakerx_widgets/conda_recipe/meta.yaml new file mode 100644 index 0000000..a1fd2e8 --- /dev/null +++ b/beakerx_widgets/conda_recipe/meta.yaml @@ -0,0 +1,37 @@ +{% set name = "beakerx_widgets" %} +{% set version = "1.6.0" %} + +package: + name: "{{ name|lower }}" + version: "{{ version }}" + +source: + git_url: https://github.com/twosigma/beakerx_widgets.git + git_tag: master + +source: + url: https://files.pythonhosted.org/packages/source/b/beakerx/{{ name }}-{{ version }}.tar.gz + #url: file:///YOUR_LOCAL_PATH/beakerx_widgets/beakerx/dist/{{ name }}-{{ version }}.tar.gz + +build: + number: 1 + script: pip install --no-deps . + entry_points: + - beakerx = beakerx:run + +requirements: + build: + - python + - pip + run: + - beakerx_base={{ version }} + - beakerx_tabledisplay={{ version }} + - python >=3 + - notebook >=5.7.4 + - ipywidgets >=7.0 + - widgetsnbextension + - pyspark + - bottle + - requests + - numpy + - pandas diff --git a/beakerx_widgets/conda_recipe/post-link.bat b/beakerx_widgets/conda_recipe/post-link.bat new file mode 100644 index 0000000..fce36e8 --- /dev/null +++ b/beakerx_widgets/conda_recipe/post-link.bat @@ -0,0 +1,5 @@ +@echo off +( + REM Uninstall BeakerX notebook extension + "%PREFIX%\Scripts\beakerx.exe" "install" +) >>"%PREFIX%\.messages.txt" 2>&1 diff --git a/beakerx_widgets/conda_recipe/post-link.sh b/beakerx_widgets/conda_recipe/post-link.sh new file mode 100644 index 0000000..c534362 --- /dev/null +++ b/beakerx_widgets/conda_recipe/post-link.sh @@ -0,0 +1,4 @@ +{ + # Run BeakerX install script + "${PREFIX}/bin/beakerx" "install" +} >> "${PREFIX}/.messages.txt" 2>&1 diff --git a/beakerx_widgets/requirements.txt b/beakerx_widgets/requirements.txt new file mode 100644 index 0000000..d6e1198 --- /dev/null +++ b/beakerx_widgets/requirements.txt @@ -0,0 +1 @@ +-e . diff --git a/beakerx_widgets/run_beakerx_python_tests.sh b/beakerx_widgets/run_beakerx_python_tests.sh new file mode 100755 index 0000000..28cbd69 --- /dev/null +++ b/beakerx_widgets/run_beakerx_python_tests.sh @@ -0,0 +1,16 @@ +# Copyright 2020 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#!/bin/bash +(python -m unittest) diff --git a/beakerx_widgets/setup.py b/beakerx_widgets/setup.py new file mode 100644 index 0000000..9aa6418 --- /dev/null +++ b/beakerx_widgets/setup.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# coding: utf-8 + +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +from setupbase import ( + create_cmdclass, + install_node_modules, + get_version, + here +) +from setuptools import setup, find_packages + +cmdclass = create_cmdclass(develop_wrappers=[ + 'js' +], distribute_wrappers=[ + 'js' +]) +cmdclass['js'] = install_node_modules( + path='../js', + build_dir=os.path.join(here, '../js', 'dist'), + source_dir=os.path.join(here, '../js', 'src') +) + +setup_args = dict( + name='beakerx_widgets', + description='BeakerX: Beaker Extensions for Jupyter Notebook', + long_description='BeakerX: Beaker Extensions for Jupyter Notebook', + version=get_version(os.path.join('beakerx_widgets', '_version.py')), + author='Two Sigma Open Source, LLC', + author_email='beakerx-feedback@twosigma.com', + url='http://beakerx.com', + keywords=[ + 'ipython', + 'jupyter', + 'widgets' + ], + classifiers=[ + 'Development Status :: 4 - Beta', + 'Framework :: IPython', + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'Topic :: Multimedia :: Graphics', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7' + ], + entry_points={ + 'console_scripts': [ + 'beakerx_widgets = beakerx_widgets:run' + ] + }, + install_requires=[ + 'beakerx_base', + 'pandas' + ], + python_requires='>=3', + zip_safe=False, + include_package_data=True, + packages=find_packages(), + cmdclass=cmdclass +) + +if __name__ == '__main__': + setup(**setup_args) diff --git a/beakerx_widgets/setupbase.py b/beakerx_widgets/setupbase.py new file mode 100644 index 0000000..698ec88 --- /dev/null +++ b/beakerx_widgets/setupbase.py @@ -0,0 +1,445 @@ +#!/usr/bin/env python +# coding: utf-8 + +# Copyright 2017 TWO SIGMA OPEN SOURCE, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This file originates from the 'jupyter-packaging' package, and +contains a set of useful utilities for installing node modules +within a Python package. +""" + +import functools +import os +import pipes +import sys +from distutils import log +from subprocess import check_call + +from setuptools import Command +from setuptools.command.bdist_egg import bdist_egg +from setuptools.command.develop import develop +from setuptools.command.sdist import sdist + +try: + from wheel.bdist_wheel import bdist_wheel +except ImportError: + bdist_wheel = None + +if sys.platform == 'win32': + from subprocess import list2cmdline +else: + def list2cmdline(cmd_list): + return ' '.join(map(pipes.quote, cmd_list)) + +# --------------------------------------------------------------------------- +# Top Level Variables +# --------------------------------------------------------------------------- + +here = os.path.abspath(os.path.dirname(sys.argv[0])) +root = os.path.abspath(os.path.join(here, os.pardir)) +kernel_path = os.path.join(root, 'kernel') +kernel_source = os.path.join(kernel_path, 'base', 'src', 'main', 'java') +is_repo = os.path.exists(os.path.join(root, '.git')) +node_modules = os.path.join(here, 'js', 'node_modules') +node_modules_path = ':'.join([ + os.path.join(node_modules, '.bin'), + os.environ.get('PATH', os.defpath), +]) + +if "--skip-yarn" in sys.argv: + print("Skipping yarn install as requested.") + skip_yarn = True + sys.argv.remove("--skip-yarn") +else: + skip_yarn = False + + +# --------------------------------------------------------------------------- +# Public Functions +# --------------------------------------------------------------------------- +def _classpath_for(kernel): + return pkg_resources.resource_filename( + 'beakerx', os.path.join('kernel', kernel, 'lib', '*')) + + +def get_version(path): + version = {} + with open(os.path.join(here, path)) as f: + exec(f.read(), {}, version) + return version['__version__'] + + +def get_data_files(top): + """Get data files""" + + data_files = [] + ntrim = len(here + os.path.sep) + + for (d, _, filenames) in os.walk(top): + data_files.append(( + d[ntrim:], + [os.path.join(d, f) for f in filenames] + )) + print("--------------------------------------------------------------------------------------------") + print(data_files) + print("-------------------------------------------") + return data_files + + +def find_packages(top): + """ + Find all of the packages. + """ + packages = [] + for d, dirs, _ in os.walk(top, followlinks=True): + if os.path.exists(os.path.join(d, '__init__.py')): + packages.append(os.path.relpath(d, top).replace(os.path.sep, '.')) + elif d != top: + # Do not look for packages in subfolders if current is not a package + dirs[:] = [] + return packages + + +def update_package_data(distribution): + """update build_py options to get package_data changes""" + build_py = distribution.get_command_obj('build_py') + build_py.finalize_options() + + +def create_cmdclass(develop_wrappers=None, distribute_wrappers=None, data_dirs=None): + """Create a command class with the given optional wrappers. + Parameters + ---------- + develop_wrapper: list(str), optional + The cmdclass names to run before running other commands + distribute_wrappers: list(str), optional + The cmdclass names to run before running other commands + data_dirs: list(str), optional. + The directories containing static data. + """ + develop_wrappers = develop_wrappers or [] + distribute_wrappers = distribute_wrappers or [] + data_dirs = data_dirs or [] + develop_wrapper = functools.partial(wrap_command, develop_wrappers, data_dirs) + distribute_wrapper = functools.partial(wrap_command, distribute_wrappers, data_dirs) + cmdclass = dict( + develop=develop_wrapper(develop, strict=True), + sdist=distribute_wrapper(sdist, strict=True), + bdist_egg=bdist_egg if 'bdist_egg' in sys.argv else bdist_egg_disabled + ) + if bdist_wheel: + cmdclass['bdist_wheel'] = bdist_wheel + return cmdclass + + +def run(cmd, *args, **kwargs): + """Echo a command before running it. Defaults to repo as cwd""" + log.info('> ' + list2cmdline(cmd)) + kwargs.setdefault('cwd', here) + kwargs.setdefault('shell', sys.platform == 'win32') + if not isinstance(cmd, list): + cmd = cmd.split() + return check_call(cmd, *args, **kwargs) + + +def is_stale(target, source): + """Test whether the target file/directory is stale based on the source + file/directory. + """ + if not os.path.exists(target): + return True + target_mtime = recursive_mtime(target) or 0 + return compare_recursive_mtime(source, cutoff=target_mtime) + + +class BaseCommand(Command): + """Empty command because Command needs subclasses to override too much""" + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def get_inputs(self): + return [] + + def get_outputs(self): + return [] + + +def combine_commands(*commands): + """Return a Command that combines several commands.""" + + class CombinedCommand(Command): + + def initialize_options(self): + self.commands = [] + for C in commands: + self.commands.append(C(self.distribution)) + for c in self.commands: + c.initialize_options() + + def finalize_options(self): + for c in self.commands: + c.finalize_options() + + def run(self): + for c in self.commands: + c.run() + + return CombinedCommand + + +def compare_recursive_mtime(path, cutoff, newest=True): + """Compare the newest/oldest mtime for all files in a directory. + Cutoff should be another mtime to be compared against. If an mtime that is + newer/older than the cutoff is found it will return True. + E.g. if newest=True, and a file in path is newer than the cutoff, it will + return True. + """ + if os.path.isfile(path): + mt = mtime(path) + if newest: + if mt > cutoff: + return True + elif mt < cutoff: + return True + for dirname, _, filenames in os.walk(path, topdown=False): + for filename in filenames: + mt = mtime(os.path.join(dirname, filename)) + if newest: # Put outside of loop? + if mt > cutoff: + return True + elif mt < cutoff: + return True + return False + + +def recursive_mtime(path, newest=True): + """Gets the newest/oldest mtime for all files in a directory.""" + if os.path.isfile(path): + return mtime(path) + current_extreme = None + for dirname, _, filenames in os.walk(path, topdown=False): + for filename in filenames: + mt = mtime(os.path.join(dirname, filename)) + if newest: # Put outside of loop? + if mt >= (current_extreme or mt): + current_extreme = mt + elif mt <= (current_extreme or mt): + current_extreme = mt + return current_extreme + + +def mtime(path): + """shorthand for mtime""" + return os.stat(path).st_mtime + + +def install_node_modules(path=None, build_dir=None, source_dir=None, build_cmd='build', force=False): + """Return a Command for managing an node_modules installation. + Note: The command is skipped if the `--skip-yarn` flag is used. + + Parameters + ---------- + path: str, optional + The base path of the node package. Defaults to the repo root. + build_dir: str, optional + The target build directory. If this and source_dir are given, + the JavaScript will only be build if necessary. + source_dir: str, optional + The source code directory. + build_cmd: str, optional + The yarn command to build assets to the build_dir. + """ + + class Yarn(BaseCommand): + description = 'install package.json dependencies using yarn' + + def run(self): + if skip_yarn: + log.info('Skipping yarn-installation') + return + node_package = path or here + node_modules = os.path.join(node_package, 'node_modules') + + if not which("yarn"): + log.error("`yarn` unavailable. If you're running this command " + "using sudo, make sure `yarn` is available to sudo") + return + if force or is_stale(node_modules, os.path.join(node_package, 'package.json')): + log.info('Installing build dependencies with yarn. This may ' + 'take a while...') + run(['yarn', 'install'], cwd=node_package) + if build_dir and source_dir and not force: + should_build = is_stale(build_dir, source_dir) + else: + should_build = True + if should_build: + run(['yarn', 'run', build_cmd], cwd=node_package) + + return Yarn + + +def run_gradle(path=kernel_path, cmd='build', skip_tests=False): + """Return a Command for running gradle scripts. + Parameters + ---------- + path: str, optional + The base path of the node package. Defaults to the repo root. + cmd: str, optional + The command to run with gradlew. + """ + + class Gradle(BaseCommand): + description = 'Run gradle script' + + def skip_test_option(self, skip): + if skip: + return '-Dskip.tests=True' + else: + return '-Dskip.tests=False' + + def run(self): + run([('' if sys.platform == 'win32' else './') + 'gradlew', '--no-daemon', cmd, + self.skip_test_option(skip_tests)], cwd=path) + + return Gradle + + +def ensure_targets(targets): + """Return a Command that checks that certain files exist. + Raises a ValueError if any of the files are missing. + Note: The check is skipped if the `--skip-yarn` flag is used. + """ + + class TargetsCheck(BaseCommand): + def run(self): + if skip_yarn: + log.info('Skipping target checks') + return + missing = [t for t in targets if not os.path.exists(t)] + if missing: + raise ValueError(('missing files: %s' % missing)) + + return TargetsCheck + + +# `shutils.which` function copied verbatim from the Python-3.3 source. +def which(cmd, mode=os.F_OK | os.X_OK, path=None): + """Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result + of os.environ.get("PATH"), or can be overridden with a custom search + path. + """ + + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return (os.path.exists(fn) and os.access(fn, mode) and + not os.path.isdir(fn)) + + # Short circuit. If we're given a full path which matches the mode + # and it exists, we're done here. + if _access_check(cmd, mode): + return cmd + + path = (path or os.environ.get("PATH", os.defpath)).split(os.pathsep) + + if sys.platform == "win32": + # The current directory takes precedence on Windows. + if os.curdir not in path: + os.sys.path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given "python.exe". + matches = [cmd for ext in pathext if cmd.lower().endswith(ext.lower())] + # If it does match, only test that one, otherwise we have to try + # others. + files = [cmd] if matches else [cmd + ext.lower() for ext in pathext] + else: + # On other platforms you don't have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + dir = os.path.normcase(dir) + if dir not in seen: + seen.add(dir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None + + +# --------------------------------------------------------------------------- +# Private Functions +# --------------------------------------------------------------------------- + + +def wrap_command(cmds, data_dirs, cls, strict=True): + """Wrap a setup command + Parameters + ---------- + cmds: list(str) + The names of the other commands to run prior to the command. + strict: boolean, optional + Wether to raise errors when a pre-command fails. + """ + + class WrappedCommand(cls): + + def run(self): + if not getattr(self, 'uninstall', None): + try: + [self.run_command(cmd) for cmd in cmds] + except Exception: + if strict: + raise + else: + pass + + result = cls.run(self) + data_files = [] + for dname in data_dirs: + data_files.extend(get_data_files(dname)) + # update data-files in case this created new files + self.distribution.data_files = data_files + # also update package data + update_package_data(self.distribution) + return result + + return WrappedCommand + + +class bdist_egg_disabled(bdist_egg): + """Disabled version of bdist_egg + Prevents setup.py install performing setuptools' default easy_install, + which it should never ever do. + """ + + def run(self): + sys.exit("Aborting implicit building of eggs. Use `pip install .` " + + " to install from source.") diff --git a/configuration.yml b/configuration.yml new file mode 100644 index 0000000..4d4108e --- /dev/null +++ b/configuration.yml @@ -0,0 +1,11 @@ +name: beakerx_widgets +channels: + - conda-forge +dependencies: + - bottle + - ipywidgets>=7.5.1,<8 + - pandas + - py4j + - pyspark + - python=3.7.5 + - requests diff --git a/doc/AutoTranslation.ipynb b/doc/AutoTranslation.ipynb new file mode 100644 index 0000000..fc92ab3 --- /dev/null +++ b/doc/AutoTranslation.ipynb @@ -0,0 +1,226 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Autotranslation: Python to JavaScript and D3\n", + "\n", + "Generate a random graph with Python, then visualize it with a [D3](http://d3js.org/) interactive, force-directed graph.\n", + "\n", + "The first cell imports the BeakerX package and initializes the runtime. \n", + "\n", + "Then we generates the graph (one made of nodes and edges, like a social network graph)\n", + "and store it in the BeakerX object.\n", + "\n", + "Then we load D3 and set its styles.\n", + "\n", + "Finally, a JavaScript cell gets the data from the BeakerX object and renders it with D3.\n", + "\n", + "This final cell was\n", + "copied almost verbatim from the [D3 documentation](http://bl.ocks.org/mbostock/4062045). Other D3 examples\n", + "should be similarly easy to get working in BeakerX." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from beakerx_widgets.object import beakerx" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from random import randrange\n", + "import math\n", + "\n", + "nnodes = 100\n", + "\n", + "nodes = []\n", + "links = []\n", + "\n", + "for i in range(0, nnodes):\n", + " nodes.append({\"name\": str(i), \"group\": int(i*7/nnodes)})\n", + "\n", + "for i in range(0, int(nnodes*1.15)):\n", + " source = i % nnodes\n", + " target = int(math.log(1 + randrange(nnodes), 1.3))\n", + " value = 10.0 / (1 + abs(source - target))\n", + " links.append({\"source\": source, \"target\": target, \"value\": value * value})\n", + "\n", + "beakerx.graph = {\"nodes\":nodes, \"links\":links}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "%%javascript\n", + "require.config({\n", + " paths: {\n", + " d3: '//cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min'\n", + " }});" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%html\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%javascript\n", + "\n", + "beakerx.displayHTML(this, '
');\n", + "\n", + "var graph = beakerx.graph\n", + "\n", + "var d3 = require(['d3'], function (d3) {\n", + " \n", + " var width = 600,\n", + " height = 500;\n", + "\n", + " var color = d3.scaleOrdinal(d3.schemeCategory20);\n", + "\n", + " var simulation = d3.forceSimulation()\n", + " .force(\"link\", d3.forceLink().distance(30))\n", + " .force(\"charge\", d3.forceManyBody().strength(-200))\n", + " .force(\"center\", d3.forceCenter(width / 2, height / 2))\n", + " .force(\"y\", d3.forceY(width / 2).strength(0.3))\n", + " .force(\"x\", d3.forceX(height / 2).strength(0.3));\n", + "\n", + " var svg = d3.select(\"#fdg\")\n", + " .append(\"svg\")\n", + " .attr(\"width\", width)\n", + " .attr(\"height\", height)\n", + " .attr(\"transform\", \"translate(\"+[100, 0]+\")\");\n", + "\n", + " simulation\n", + " .nodes(graph.nodes)\n", + " .force(\"link\")\n", + " .links(graph.links);\n", + "\n", + " var link = svg.selectAll(\".link\")\n", + " .data(graph.links)\n", + " .enter().append(\"line\")\n", + " .attr(\"class\", \"link\")\n", + " .style(\"stroke-width\", function(d) { return Math.sqrt(d.value); });\n", + "\n", + " var node = svg.selectAll(\".node\")\n", + " .data(graph.nodes)\n", + " .enter().append(\"circle\")\n", + " .attr(\"class\", \"node\")\n", + " .attr(\"r\", 10)\n", + " .style(\"fill\", function(d) { return color(d.group); });\n", + "\n", + " node.append(\"title\")\n", + " .text(function(d) { return d.name; });\n", + "\n", + " simulation.on(\"tick\", function() {\n", + " link.attr(\"x1\", function(d) { return d.source.x; })\n", + " .attr(\"y1\", function(d) { return d.source.y; })\n", + " .attr(\"x2\", function(d) { return d.target.x; })\n", + " .attr(\"y2\", function(d) { return d.target.y; });\n", + "\n", + " node.attr(\"cx\", function(d) { return d.x; })\n", + " .attr(\"cy\", function(d) { return d.y; });\n", + " });\n", + " \n", + " node.call(d3.drag()\n", + " .on(\"start\", dragstarted)\n", + " .on(\"drag\", dragged)\n", + " .on(\"end\", dragended)\n", + " );\n", + " \n", + " function dragstarted(d) {\n", + " if (!d3.event.active) simulation.alphaTarget(0.3).restart();\n", + " d.fx = d.x;\n", + " d.fy = d.y;\n", + " }\n", + "\n", + " function dragged(d) {\n", + " d.fx = d3.event.x;\n", + " d.fy = d3.event.y;\n", + " }\n", + "\n", + " function dragended(d) {\n", + " if (!d3.event.active) simulation.alphaTarget(0);\n", + " d.fx = null;\n", + " d.fy = null;\n", + " }\n", + "});" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": false, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/doc/CategoryPlot.ipynb b/doc/CategoryPlot.ipynb new file mode 100644 index 0000000..ffbf36d --- /dev/null +++ b/doc/CategoryPlot.ipynb @@ -0,0 +1,447 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Category Plots, aka Bar Charts" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from beakerx_widgets.plots import *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bars = CategoryBars(value= [[1, 2, 3], [1, 3, 5]])\n", + "plot = CategoryPlot()\n", + "\n", + "plot.add(bars)\n", + "plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cplot = CategoryPlot(initWidth= 400, initHeight= 200)\n", + "bars = CategoryBars(value=[[1, 2, 3], [1, 3, 5]])\n", + "cplot.add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cplot = CategoryPlot(title= \"Hello CategoryPlot!\",\n", + " xLabel= \"Categories\",\n", + " yLabel= \"Values\")\n", + "cplot.add(CategoryBars(value=[[1, 2, 3], [1, 3, 5]]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cplot = CategoryPlot(categoryNames= [\"Helium\", \"Neon\", \"Argon\"])\n", + "cplot.add(CategoryBars(value= [[1, 2, 3], [1, 3, 5]]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "CategoryPlot().add(CategoryBars(value= [[1, 2, 3], [1, 3, 5]],\n", + " seriesNames= [\"Gas\", \"Liquid\"]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bars = CategoryBars(value= [[1, 2], [3, 4], [5, 6], [7, 8]],\n", + " seriesNames= [\"Gas\", None, \"\", \"Liquid\"])\n", + "CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = CategoryPlot(showLegend= True) # force legend display\n", + "bars = CategoryBars(value= [[1, 2, 3], [1, 3, 5]])\n", + "# since no display names were provided, default names \"series0\" etc will be used.\n", + "plot.add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = CategoryPlot(orientation= PlotOrientationType.HORIZONTAL)\n", + "plot.add(CategoryBars(value=[[1, 2, 3], [1, 3, 5]]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "plot = CategoryPlot(categoryNames= [\"Acid\", \"Neutral\", \"Base\"], \n", + " categoryNamesLabelAngle= -1/4 * math.pi)\n", + "plot.add(CategoryBars(value= [[1, 2, 3], [4, 5, 6]]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "CategoryPlot(categoryMargin= 2).add(CategoryBars(value= [[1, 2, 3], [4, 5, 6]]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#bars = CategoryBars(value= (1..4) + 2)\n", + "#CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bars = CategoryBars(value= [[1, 2], [3, 4], [5, 6]], color= Color.pink)\n", + "CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "colors = [Color.red, Color.gray, Color.blue]\n", + "bars = CategoryBars(value= [[1, 2], [3, 4], [5, 6]], color= colors)\n", + "CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "colors = [[Color.red, Color.gray],\n", + " [Color.gray, Color.gray],\n", + " [Color.blue, Color.pink]]\n", + "bars = CategoryBars(value= [[1, 2], [3, 4], [5, 6]], color= colors)\n", + "CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "colors = [Color.pink, [Color.red, Color.gray, Color.blue]]\n", + "bars = CategoryBars(value= [[1, 2, 3], [4, 5, 6]], color= colors)\n", + "CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bars = CategoryBars(value= [[1, 2, 3], [4, 5, 6]], base= -2)\n", + "CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bars = CategoryBars(value= [[1, 2, 3], [4, 5, 4]], base= [-1, -3])\n", + "CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bars = CategoryBars(value= [[1, 2, 3], [4, 5, 6]],\n", + " base= [[0, 1, 1], [-4, -5, -6]])\n", + "CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bars = CategoryBars(value= [[1, 2, 3], [4, 5, 6]],\n", + " width= [[0.3, 0.6, 1.7], 1.0])\n", + "CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bars = CategoryBars(value= [[1, 2, 3], [4, 5, 6]],\n", + " fill= [[True, True, False], [True, False, True]],\n", + " drawOutline= [[True, False, True], [True, True, False]],\n", + " outlineColor= [Color.black, Color.red])\n", + "CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bars = CategoryBars(value= [[1, 2, 3], [4, 5, 8], [10, 9, 10]],\n", + " base= [0, [1, 2, 3], [4, 5, 8]],\n", + " centerSeries= True,\n", + " itemLabel= [[1, 2, 3], [4, 5, 8], [10, 9, 10]]\n", + " )\n", + "CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ss = [StrokeType.DASH, StrokeType.LONGDASH]\n", + "cs = [Color.black, Color.red]\n", + "CategoryPlot().add(CategoryStems(value= [[1, 2, 4], [4, 5, 8]], color= cs, style= ss))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "CategoryPlot().add(CategoryPoints(value= [[1, 2, 4], [4, 5, 8]]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ss = [StrokeType.DASH, StrokeType.DOT]\n", + "CategoryPlot().add(CategoryLines(value= [[1, 2, 4], [4, 5, 8]], style= ss,\n", + " seriesNames=[\"Lanthanide\", \"Actinide\"]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s1 = [1, 2, 4]\n", + "s2 = [4, 5, 8]\n", + "lines = CategoryLines(value= [s1, s2], centerSeries= True)\n", + "points = CategoryPoints(value= [s1, s2], centerSeries= True)\n", + "stems = CategoryStems(value=[s1], base= [s2], style= StrokeType.DOT, color= Color.gray)\n", + "#plot = CategoryPlot().add(lines).add(points).add(stems)\n", + "plot = CategoryPlot()\n", + "plot.add(lines)\n", + "plot.add(points)\n", + "plot.add(stems)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = CategoryPlot(initWidth= 500, initHeight= 400,\n", + " title= \"Bar Chart Demo\",\n", + " xLabel= \"Alkali\", yLabel= \"Temperature ° Celcius\",\n", + " categoryNames=[\"Lithium\", \"Sodium\", \"Potassium\", \"Rubidium\"])\n", + "s1 = [[10, 15, 13, 7], [22, 18, 28, 17]]\n", + "high = [[12.4, 19.5, 15.1, 8.2], [24.3, 23.3, 30.1, 18.2]]\n", + "low = [[7.6, 10.5, 10.9, 5.8], [19.7, 12.7, 25.9, 15.8]]\n", + "color = [Color(247, 150, 70), Color.orange, Color(155, 187, 89)]\n", + "plot.add(CategoryBars(value= s1, color= color, seriesNames= [\"Solid\", \"Liquid\"]))\n", + "plot.add(CategoryStems(value= high, base= low, color= color[2]))\n", + "plot.add(CategoryPoints(value= high, outlineColor= color[2], size=12))\n", + "plot.add(CategoryPoints(value= low, outlineColor= color[2], size=12))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "p = CategoryPlot(title= \"Multiple Y Axes Demo\",\n", + " yLabel= \"Price\",\n", + " categoryNames= [\"Q1\", \"Q2\", \"Q3\", \"Q4\"])\n", + "p.add(YAxis(label= \"Volume\", upperMargin= 1))\n", + "p.add(CategoryBars(value= [[1500, 2200, 2500, 4000]], width= 0.6,\n", + " color= Color.PINK, yAxis= \"Volume\", showItemLabel= True,\n", + " labelPosition= LabelPositionType.VALUE_INSIDE))\n", + "p.add(CategoryLines(value= [[5, 2, 3.5, 4]], color= Color.GRAY,\n", + " showItemLabel=True))\n", + "p.add(CategoryPoints(value=[[5, 2, 3.5, 4]], color=Color.GRAY))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "CategoryPlot().add(CategoryStems(value=[[-3, 2, 4], [4, 5, 8]],\n", + " width= 10, showItemLabel= True))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bars = CategoryBars(value= [[-5, 2, 3], [1, 3, 5]],\n", + " showItemLabel= True)\n", + "CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bars = CategoryBars(value= [[-5, 2, 3], [1, 3, 5]],\n", + " labelPosition= LabelPositionType.BASE_OUTSIDE,\n", + " showItemLabel= True)\n", + "CategoryPlot().add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = CategoryPlot(title= \"Move mouse cursor over bars\")\n", + "bars = CategoryBars(value= [[-5, 2, 3], [1, 3, 5]], useToolTip= False)\n", + "plot.add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#table = [[close=11.59, high=13.15, low=11.92, open=11.92],\n", + "# [close=12.76, high=15.44, low=11.88, open=12.42],\n", + "# [close=18.19, high=20.96, low=17.93, open=18.56]]\n", + "#v = table.collect { it.values().toList() }\n", + "#p = CategoryPlot(categoryNames= table[0].keySet().toList())\n", + "#p.add(new CategoryBars(value= v, seriesNames= [\"A\", \"B\", \"C\"]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cs = [Color.orange]\n", + "\n", + "cp = CategoryPlot() \n", + "\n", + "cp.add(CategoryArea(value= [[1, 3, 2]], base= [[0.5, 1, 0]]))\n", + "cp.add(CategoryArea(value= [[2, 1, 0.5]], color= cs))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": false, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/doc/ChartingAPI.ipynb b/doc/ChartingAPI.ipynb new file mode 100644 index 0000000..b1c8675 --- /dev/null +++ b/doc/ChartingAPI.ipynb @@ -0,0 +1,758 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Python API to BeakerX Interactive Plotting\n", + "\n", + "You can access Beaker's native interactive plotting library from Python.\n", + "\n", + "## Plot with simple properties\n", + "\n", + "Python plots has syntax very similar to Groovy plots. Property names are the same." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from beakerx_widgets.plots import *\n", + "import pandas as pd\n", + "\n", + "tableRows = pd.read_csv('./resources/data/interest-rates.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Plot(title=\"Title\",\n", + " xLabel=\"Horizontal\",\n", + " yLabel=\"Vertical\",\n", + " initWidth=500,\n", + " initHeight=200)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plot items\n", + "\n", + "### Lines, Bars, Points and Right yAxis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = [1, 4, 6, 8, 10]\n", + "y = [3, 6, 4, 5, 9]\n", + "\n", + "pp = Plot(title='Bars, Lines, Points and 2nd yAxis', \n", + " xLabel=\"xLabel\", \n", + " yLabel=\"yLabel\", \n", + " legendLayout=LegendLayout.HORIZONTAL,\n", + " legendPosition=LegendPosition.RIGHT,\n", + " omitCheckboxes=True)\n", + "\n", + "pp.add(YAxis(label=\"Right yAxis\"))\n", + "pp.add(Bars(displayName=\"Bar\", \n", + " x=[1,3,5,7,10], \n", + " y=[100, 120,90,100,80], \n", + " width=1))\n", + "pp.add(Line(displayName=\"Line\", \n", + " x=x, \n", + " y=y, \n", + " width=6, \n", + " yAxis=\"Right yAxis\"))\n", + "pp.add(Points(x=x, \n", + " y=y, \n", + " size=10, \n", + " shape=ShapeType.DIAMOND,\n", + " yAxis=\"Right yAxis\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot(title= \"Setting line properties\")\n", + "ys = [0, 1, 6, 5, 2, 8]\n", + "ys2 = [0, 2, 7, 6, 3, 8]\n", + "plot.add(Line(y= ys, width= 10, color= Color.red))\n", + "plot.add(Line(y= ys, width= 3, color= Color.yellow))\n", + "plot.add(Line(y= ys, width= 4, color= Color(33, 87, 141), style= StrokeType.DASH, interpolation= 0))\n", + "plot.add(Line(y= ys2, width= 2, color= Color(212, 57, 59), style= StrokeType.DOT))\n", + "plot.add(Line(y= [5, 0], x= [0, 5], style= StrokeType.LONGDASH))\n", + "plot.add(Line(y= [4, 0], x= [0, 5], style= StrokeType.DASHDOT))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot(title= \"Changing Point Size, Color, Shape\")\n", + "y1 = [6, 7, 12, 11, 8, 14]\n", + "y2 = [4, 5, 10, 9, 6, 12]\n", + "y3 = [2, 3, 8, 7, 4, 10]\n", + "y4 = [0, 1, 6, 5, 2, 8]\n", + "plot.add(Points(y= y1))\n", + "plot.add(Points(y= y2, shape= ShapeType.CIRCLE))\n", + "plot.add(Points(y= y3, size= 8.0, shape= ShapeType.DIAMOND))\n", + "plot.add(Points(y= y4, size= 12.0, color= Color.orange, outlineColor= Color.red))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot(title= \"Changing point properties with list\")\n", + "cs = [Color.black, Color.red, Color.orange, Color.green, Color.blue, Color.pink]\n", + "ss = [6.0, 9.0, 12.0, 15.0, 18.0, 21.0]\n", + "fs = [False, False, False, True, False, False]\n", + "plot.add(Points(y= [5] * 6, size= 12.0, color= cs))\n", + "plot.add(Points(y= [4] * 6, size= 12.0, color= Color.gray, outlineColor= cs))\n", + "plot.add(Points(y= [3] * 6, size= ss, color= Color.red))\n", + "plot.add(Points(y= [2] * 6, size= 12.0, color= Color.black, fill= fs, outlineColor= Color.black))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot()\n", + "y1 = [1.5, 1, 6, 5, 2, 8]\n", + "cs = [Color.black, Color.red, Color.gray, Color.green, Color.blue, Color.pink]\n", + "ss = [StrokeType.SOLID, StrokeType.SOLID, StrokeType.DASH, StrokeType.DOT, StrokeType.DASHDOT, StrokeType.LONGDASH]\n", + "plot.add(Stems(y= y1, color= cs, style= ss, width= 5))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot(title= \"Setting the base of Stems\")\n", + "ys = [3, 5, 2, 3, 7]\n", + "y2s = [2.5, -1.0, 3.5, 2.0, 3.0]\n", + "plot.add(Stems(y= ys, width= 2, base= y2s))\n", + "plot.add(Points(y= ys))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot(title= \"Bars\")\n", + "cs = [Color(255, 0, 0, 128)] * 5 # transparent bars\n", + "cs[3] = Color.red # set color of a single bar, solid colored bar\n", + "plot.add(Bars(x= [1, 2, 3, 4, 5], y= [3, 5, 2, 3, 7], color= cs, outlineColor= Color.black, width= 0.3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Lines, Points with Pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot(title= \"Pandas line\")\n", + "plot.add(Line(y= tableRows.y1, width= 2, color= Color(216, 154, 54)))\n", + "plot.add(Line(y= tableRows.y10, width= 2, color= Color.lightGray))\n", + "\n", + "plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot(title= \"Pandas Series\")\n", + "plot.add(Line(y= pd.Series([0, 6, 1, 5, 2, 4, 3]), width=2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot(title= \"Bars\")\n", + "cs = [Color(255, 0, 0, 128)] * 7 # transparent bars\n", + "cs[3] = Color.red # set color of a single bar, solid colored bar\n", + "plot.add(Bars(pd.Series([0, 6, 1, 5, 2, 4, 3]), color= cs, outlineColor= Color.black, width= 0.3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Areas, Stems and Crosshair" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ch = Crosshair(color=Color.black, width=2, style=StrokeType.DOT)\n", + "plot = Plot(crosshair=ch)\n", + "y1 = [4, 8, 16, 20, 32]\n", + "base = [2, 4, 8, 10, 16]\n", + "cs = [Color.black, Color.orange, Color.gray, Color.yellow, Color.pink]\n", + "ss = [StrokeType.SOLID, \n", + " StrokeType.SOLID, \n", + " StrokeType.DASH, \n", + " StrokeType.DOT, \n", + " StrokeType.DASHDOT, \n", + " StrokeType.LONGDASH]\n", + "plot.add(Area(y=y1, base=base, color=Color(255, 0, 0, 50)))\n", + "plot.add(Stems(y=y1, base=base, color=cs, style=ss, width=5))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot()\n", + "y = [3, 5, 2, 3]\n", + "x0 = [0, 1, 2, 3]\n", + "x1 = [3, 4, 5, 8]\n", + "plot.add(Area(x= x0, y= y))\n", + "plot.add(Area(x= x1, y= y, color= Color(128, 128, 128, 50), interpolation= 0))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "p = Plot()\n", + "p.add(Line(y= [3, 6, 12, 24], displayName= \"Median\"))\n", + "p.add(Area(y= [4, 8, 16, 32], base= [2, 4, 8, 16],\n", + " color= Color(255, 0, 0, 50), displayName= \"Q1 to Q3\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ch = Crosshair(color= Color(255, 128, 5), width= 2, style= StrokeType.DOT)\n", + "pp = Plot(crosshair= ch, omitCheckboxes= True,\n", + " legendLayout= LegendLayout.HORIZONTAL, legendPosition= LegendPosition.TOP)\n", + "x = [1, 4, 6, 8, 10]\n", + "y = [3, 6, 4, 5, 9]\n", + "pp.add(Line(displayName= \"Line\", x= x, y= y, width= 3))\n", + "pp.add(Bars(displayName= \"Bar\", x= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], y= [2, 2, 4, 4, 2, 2, 0, 2, 2, 4], width= 0.5))\n", + "pp.add(Points(x= x, y= y, size= 10))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Constant Lines, Constant Bands" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "p = Plot ()\n", + "p.add(Line(y=[-1, 1]))\n", + "p.add(ConstantLine(x=0.65, style=StrokeType.DOT, color=Color.blue))\n", + "p.add(ConstantLine(y=0.1, style=StrokeType.DASHDOT, color=Color.blue))\n", + "p.add(ConstantLine(x=0.3, y=0.4, color=Color.gray, width=5, showLabel=True))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Plot().add(Line(y=[-3, 1, 3, 4, 5])).add(ConstantBand(x=[1, 2], y=[1, 3]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "p = Plot() \n", + "p.add(Line(x= [-3, 1, 2, 4, 5], y= [4, 2, 6, 1, 5]))\n", + "p.add(ConstantBand(x= ['-Infinity', 1], color= Color(128, 128, 128, 50)))\n", + "p.add(ConstantBand(x= [1, 2]))\n", + "p.add(ConstantBand(x= [4, 'Infinity']))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from decimal import Decimal\n", + "pos_inf = Decimal('Infinity')\n", + "neg_inf = Decimal('-Infinity')\n", + "print (pos_inf)\n", + "print (neg_inf)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from beakerx_widgets import Text as BeakerxText\n", + "plot = Plot()\n", + "xs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n", + "ys = [8.6, 6.1, 7.4, 2.5, 0.4, 0.0, 0.5, 1.7, 8.4, 1]\n", + "def label(i):\n", + " if ys[i] > ys[i+1] and ys[i] > ys[i-1]:\n", + " return \"max\"\n", + " if ys[i] < ys[i+1] and ys[i] < ys[i-1]:\n", + " return \"min\"\n", + " if ys[i] > ys[i-1]:\n", + " return \"rising\"\n", + " if ys[i] < ys[i-1]:\n", + " return \"falling\"\n", + " return \"\"\n", + "\n", + "for i in xs:\n", + " i = i - 1\n", + " if i > 0 and i < len(xs)-1:\n", + " plot.add(BeakerxText(x= xs[i], y= ys[i], text= label(i), pointerAngle= -i/3.0))\n", + "\n", + "plot.add(Line(x= xs, y= ys))\n", + "plot.add(Points(x= xs, y= ys))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot(title= \"Setting 2nd Axis bounds\")\n", + "ys = [0, 2, 4, 6, 15, 10]\n", + "ys2 = [-40, 50, 6, 4, 2, 0]\n", + "ys3 = [3, 6, 3, 6, 70, 6]\n", + "plot.add(YAxis(label=\"Spread\"))\n", + "plot.add(Line(y= ys))\n", + "plot.add(Line(y= ys2, yAxis=\"Spread\"))\n", + "plot.setXBound([-2, 10])\n", + "#plot.setYBound(1, 5)\n", + "plot.getYAxes()[0].setBound(1,5)\n", + "plot.getYAxes()[1].setBound(3,6)\n", + "\n", + "\n", + "plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot(title= \"Setting 2nd Axis bounds\")\n", + "ys = [0, 2, 4, 6, 15, 10]\n", + "ys2 = [-40, 50, 6, 4, 2, 0]\n", + "ys3 = [3, 6, 3, 6, 70, 6]\n", + "plot.add(YAxis(label=\"Spread\"))\n", + "plot.add(Line(y= ys))\n", + "plot.add(Line(y= ys2, yAxis=\"Spread\"))\n", + "plot.setXBound([-2, 10])\n", + "plot.setYBound(1, 5)\n", + "\n", + "plot" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## TimePlot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "from beakerx_base.utils import current_milli_time \n", + "\n", + "millis = current_milli_time()\n", + "\n", + "hour = round(1000 * 60 * 60)\n", + "xs = []\n", + "ys = []\n", + "for i in range(11):\n", + " xs.append(millis + hour * i)\n", + " ys.append(i)\n", + "\n", + "plot = TimePlot(timeZone=\"America/New_York\")\n", + "# list of milliseconds\n", + "plot.add(Points(x=xs, y=ys, size=10, displayName=\"milliseconds\"))\n", + "plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = TimePlot()\n", + "plot.add(Line(x=tableRows['time'], y=tableRows['m3']))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### numpy datatime64" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "y = pd.Series([7.5, 7.9, 7, 8.7, 8, 8.5])\n", + "dates = [np.datetime64('2015-02-01'), \n", + " np.datetime64('2015-02-02'), \n", + " np.datetime64('2015-02-03'),\n", + " np.datetime64('2015-02-04'),\n", + " np.datetime64('2015-02-05'),\n", + " np.datetime64('2015-02-06')]\n", + "plot = TimePlot()\n", + "\n", + "plot.add(Line(x=dates, y=y))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Timestamp" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "y = pd.Series([7.5, 7.9, 7, 8.7, 8, 8.5])\n", + "dates = pd.Series(['2015-02-01',\n", + " '2015-02-02',\n", + " '2015-02-03',\n", + " '2015-02-04',\n", + " '2015-02-05',\n", + " '2015-02-06']\n", + " , dtype='datetime64[ns]')\n", + "plot = TimePlot()\n", + "plot.add(Line(x=dates, y=y))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Datetime and date" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import datetime\n", + "\n", + "y = pd.Series([7.5, 7.9, 7, 8.7, 8, 8.5])\n", + "dates = [datetime.date(2015, 2, 1),\n", + " datetime.date(2015, 2, 2),\n", + " datetime.date(2015, 2, 3),\n", + " datetime.date(2015, 2, 4),\n", + " datetime.date(2015, 2, 5),\n", + " datetime.date(2015, 2, 6)]\n", + "plot = TimePlot()\n", + "plot.add(Line(x=dates, y=y))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "import datetime\n", + "\n", + "y = pd.Series([7.5, 7.9, 7, 8.7, 8, 8.5])\n", + "dates = [datetime.datetime(2015, 2, 1),\n", + " datetime.datetime(2015, 2, 2),\n", + " datetime.datetime(2015, 2, 3),\n", + " datetime.datetime(2015, 2, 4),\n", + " datetime.datetime(2015, 2, 5),\n", + " datetime.datetime(2015, 2, 6)]\n", + "plot = TimePlot()\n", + "plot.add(Line(x=dates, y=y))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## NanoPlot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "millis = current_milli_time()\n", + "nanos = millis * 1000 * 1000\n", + "xs = []\n", + "ys = []\n", + "for i in range(11):\n", + " xs.append(nanos + 7 * i)\n", + " ys.append(i)\n", + "\n", + "nanoplot = NanoPlot()\n", + "nanoplot.add(Points(x=xs, y=ys))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Stacking" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "y1 = [1,5,3,2,3]\n", + "y2 = [7,2,4,1,3]\n", + "p = Plot(title='Plot with XYStacker', initHeight=200)\n", + "a1 = Area(y=y1, displayName='y1')\n", + "a2 = Area(y=y2, displayName='y2')\n", + "stacker = XYStacker()\n", + "p.add(stacker.stack([a1, a2]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## SimpleTime Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "SimpleTimePlot(tableRows, [\"y1\", \"y10\"], # column names\n", + " timeColumn=\"time\", # time is default value for a timeColumn\n", + " yLabel=\"Price\", \n", + " displayNames=[\"1 Year\", \"10 Year\"],\n", + " colors = [[216, 154, 54], Color.lightGray],\n", + " displayLines=True, # no lines (true by default)\n", + " displayPoints=False) # show points (false by default))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#time column base on DataFrame index \n", + "tableRows.index = tableRows['time']\n", + "\n", + "SimpleTimePlot(tableRows, ['m3'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rng = pd.date_range('1/1/2011', periods=72, freq='H')\n", + "ts = pd.Series(np.random.randn(len(rng)), index=rng)\n", + "df = pd.DataFrame(ts, columns=['y'])\n", + "SimpleTimePlot(df, ['y'])\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Second Y Axis" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The plot can have two y-axes. Just add a `YAxis` to the plot object, and specify its label.\n", + "Then for data that should be scaled according to this second axis,\n", + "specify the property `yAxis` with a value that coincides with the label given.\n", + "You can use `upperMargin` and `lowerMargin` to restrict the range of the data leaving more white, perhaps for the data on the other axis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "p = TimePlot(xLabel= \"Time\", yLabel= \"Interest Rates\")\n", + "p.add(YAxis(label= \"Spread\", upperMargin= 4))\n", + "p.add(Area(x= tableRows.time, y= tableRows.spread, displayName= \"Spread\",\n", + " yAxis= \"Spread\", color= Color(180, 50, 50, 128)))\n", + "p.add(Line(x= tableRows.time, y= tableRows.m3, displayName= \"3 Month\"))\n", + "p.add(Line(x= tableRows.time, y= tableRows.y10, displayName= \"10 Year\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Combined Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "points = 100\n", + "logBase = 10\n", + "expys = []\n", + "xs = []\n", + "for i in range(0, points):\n", + " xs.append(i / 15.0)\n", + " expys.append(math.exp(xs[i]))\n", + "\n", + "\n", + "cplot = CombinedPlot(xLabel= \"Linear\")\n", + "logYPlot = Plot(title= \"Linear x, Log y\", yLabel= \"Log\", logY= True, yLogBase= logBase)\n", + "logYPlot.add(Line(x= xs, y= expys, displayName= \"f(x) = exp(x)\"))\n", + "logYPlot.add(Line(x= xs, y= xs, displayName= \"g(x) = x\"))\n", + "cplot.add(logYPlot, 4)\n", + "\n", + "linearYPlot = Plot(title= \"Linear x, Linear y\", yLabel= \"Linear\")\n", + "linearYPlot.add(Line(x= xs, y= expys, displayName= \"f(x) = exp(x)\"))\n", + "linearYPlot.add(Line(x= xs, y= xs, displayName= \"g(x) = x\"))\n", + "cplot.add(linearYPlot,4)\n", + "\n", + "cplot\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot(title= \"Log x, Log y\", xLabel= \"Log\", yLabel= \"Log\",\n", + " logX= True, xLogBase= logBase, logY= True, yLogBase= logBase)\n", + "\n", + "plot.add(Line(x= xs, y= expys, displayName= \"f(x) = exp(x)\"))\n", + "plot.add(Line(x= xs, y= xs, displayName= \"f(x) = x\"))\n", + "\n", + "plot" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": false, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/doc/EasyForm.ipynb b/doc/EasyForm.ipynb new file mode 100644 index 0000000..db8c48c --- /dev/null +++ b/doc/EasyForm.ipynb @@ -0,0 +1,354 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Python API to EasyForm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from beakerx_widgets.forms import *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "f = EasyForm(\"Form and Run\")\n", + "f.addTextField(\"first\")\n", + "f['first'] = \"First\"\n", + "f.addTextField(\"last\")\n", + "f['last'] = \"Last\"\n", + "f.addButton(\"Go!\", tag=\"run\")\n", + "f" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can access the values from the form by treating it as an array indexed on the field names:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "run" + ] + }, + "outputs": [], + "source": [ + "\"Good morning \" + f[\"first\"] + \" \" + f[\"last\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "f['last'][::-1] + '...' + f['first']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The array works both ways, so you set default values on the fields by writing the array:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "f['first'] = 'Beaker'\n", + "f['last'] = 'Berzelius'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Event Handlers for Smarter Forms\n", + "\n", + "You can use `onInit` and `onChange` to handle component events. For button events use `actionPerformed` or `addAction`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import operator\n", + "\n", + "f1 = EasyForm(\"OnInit and OnChange\")\n", + "f1.addTextField(\"first\", width=15)\n", + "f1.addTextField(\"last\", width=15)\\\n", + " .onInit(lambda: operator.setitem(f1, 'last', \"setinit1\"))\\\n", + " .onChange(lambda text: operator.setitem(f1, 'first', text + ' extra'))\n", + "\n", + "button = f1.addButton(\"action\", tag=\"action_button\")\n", + "button.actionPerformed = lambda: operator.setitem(f1, 'last', 'action done')\n", + "f1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "action_button" + ] + }, + "outputs": [], + "source": [ + "f1['last'] + \", \" + f1['first']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "f1['last'] = 'new Value'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "f1['first'] = 'new Value2'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## All Kinds of Fields" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "g = EasyForm(\"Field Types\")\n", + "g.addTextField(\"Short Text Field\", width=10)\n", + "g.addTextField(\"Text Field\")\n", + "g.addPasswordField(\"Password Field\", width=10)\n", + "g.addTextArea(\"Text Area\")\n", + "g.addTextArea(\"Tall Text Area\", 10, 5)\n", + "g.addCheckBox(\"Check Box\")\n", + "options = [\"a\", \"b\", \"c\", \"d\"]\n", + "g.addComboBox(\"Combo Box\", options)\n", + "g.addComboBox(\"Combo Box editable\", options, editable=True)\n", + "\n", + "g.addList(\"List\", options)\n", + "g.addList(\"List Single\", options, multi=False)\n", + "g.addList(\"List Two Row\", options, rows=2)\n", + "\n", + "g.addCheckBoxes(\"Check Boxes\", options)\n", + "g.addCheckBoxes(\"Check Boxes H\", options, orientation=EasyForm.HORIZONTAL)\n", + "\n", + "g.addRadioButtons(\"Radio Buttons\", options)\n", + "g.addRadioButtons(\"Radio Buttons H\", options, orientation=EasyForm.HORIZONTAL)\n", + "\n", + "g.addDatePicker(\"Date\")\n", + "\n", + "g.addButton(\"Go!\", tag=\"run2\")\n", + "g" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "run2" + ] + }, + "outputs": [], + "source": [ + "result = dict()\n", + "for child in g:\n", + " result[child] = g[child]\n", + "\n", + "result\n", + "# TableDisplay(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Dates" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gdp = EasyForm(\"Field Types\")\n", + "gdp.addDatePicker(\"Date\")\n", + "gdp" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gdp['Date']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### SetData" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "easyForm = EasyForm(\"Field Types\")\n", + "easyForm.addDatePicker(\"Date\", value=datetime.today().strftime('%Y%m%d'))\n", + "easyForm" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Default Values and placeholder" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "h = EasyForm(\"Default Values\")\n", + "h.addTextArea(\"Default Value\", value = \"Initial value\")\n", + "h.addTextArea(\"Place Holder\", placeholder = \"Put here some text\")\n", + "h.addCheckBox(\"Default Checked\", value = True)\n", + "h.addButton(\"Press\", tag=\"check\")\n", + "h" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "check" + ] + }, + "outputs": [], + "source": [ + "result = dict()\n", + "for child in h:\n", + " result[child] = h[child]\n", + "\n", + "TableDisplay(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## JupyterJSWidgets work with EasyForm\n", + "\n", + "The widgets from JupyterJSWidgets are compatible and can appear in forms." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ipywidgets import * \n", + "\n", + "w = IntSlider()\n", + "\n", + "widgetForm = EasyForm(\"python widgets\")\n", + "widgetForm.addWidget(\"IntSlider\", w)\n", + "widgetForm.addButton(\"Press\", tag=\"widget_test\")\n", + "widgetForm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "widget_test" + ] + }, + "outputs": [], + "source": [ + "widgetForm['IntSlider']" + ] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": false, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/doc/Heatmap.ipynb b/doc/Heatmap.ipynb new file mode 100644 index 0000000..daa8787 --- /dev/null +++ b/doc/Heatmap.ipynb @@ -0,0 +1,121 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Heap Maps\n", + "\n", + "A heat map is a two-dimensional representation of data in which values are represented by colors. A simple heat map provides an immediate visual summary of information." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from beakerx_widgets.plots import *\n", + "\n", + "data = [[533.08714795974, 484.92105712087596, 451.63070008303896, 894.4451947886148, 335.44965728686225, 640.9424094527392, 776.2709495045433, 621.8819257981404, 793.2905673902735, 328.97078791524234, 139.26962328268513, 800.9314566259062, 629.0795214099808, 418.90954534196544, 513.8036215424278, 742.9834968485734, 542.9393528649774, 671.4256827205828, 507.1129322933082, 258.8238039352692, 581.0354187924672, 190.1830169180297, 480.461111816312, 621.621218137835, 650.6023460248642, 635.7577683708486, 605.5201537254429, 364.55368485516846, 554.807212844458, 526.1823154945637], [224.1432052432479, 343.26660237811336, 228.29828973027486, 550.3809606942758, 340.16890889700994, 214.05332637480836, 461.3159325548031, 471.2546571575069, 503.071081294441, 757.4281483575993, 493.82140462579406, 579.4302306011925, 459.76905409338497, 580.1282535427403, 378.8722877921564, 442.8806517248869, 573.9346962907078, 449.0587543606964, 383.50503527041144, 378.90761994599256, 755.1883447435789, 581.6815170672886, 426.56807864689773, 602.6727518023347, 555.6481983927658, 571.1201152862207, 372.24744704437876, 424.73180136220844, 739.9173564499195, 462.3257604373609], [561.8684320610753, 604.2859791599086, 518.3421287392559, 524.6887104615442, 364.41920277904774, 433.37737233751386, 565.0508404421712, 533.6030951907703, 306.68809206630397, 738.7229466356732, 766.9678519097575, 699.8457506281374, 437.0340850742263, 802.4400914789037, 417.38754410115075, 907.5825538527938, 521.4281410545287, 318.6109350534576, 435.8275858900637, 463.82924688853524, 533.4069709666686, 404.50516534982546, 332.6966202103611, 560.0346672408426, 436.9691072984075, 631.3453929454839, 585.1581992195356, 522.3209865675237, 497.57041075817443, 525.8867246757814], [363.4020792898871, 457.31257834906256, 333.21325206873564, 508.0466632081777, 457.1905718373847, 611.2168422907173, 515.2088862309242, 674.5569500790505, 748.0512665828364, 889.7281605626981, 363.6454276219251, 647.0396659692233, 574.150119779024, 721.1853645071792, 309.5388283799724, 450.51745569875845, 339.1271937333267, 630.6976744426033, 630.1571298446103, 615.0700456998867, 780.7843408745639, 205.13803869051543, 784.5916902014255, 498.10545868387925, 553.936345186856, 207.59216580556847, 488.12270849418735, 422.6667046886397, 292.1061953879919, 565.1595338825396], [528.5186504364794, 642.5542319036714, 563.8776991112292, 537.0271437681837, 430.4056097950834, 384.50193545472877, 693.3404035076994, 573.0278734604005, 261.2443087970927, 563.412635691231, 258.13860041989085, 550.150017102056, 477.70582135030617, 509.4311099345934, 661.3308013433317, 523.1175760654914, 370.29659041946326, 557.8704186019502, 353.66591951113645, 510.5389425077261, 469.11212447314324, 626.2863927887214, 318.5642686423241, 141.13900677851177, 486.00711121264453, 542.0075639686526, 448.7161764573215, 376.65492084577164, 166.56246586635706, 718.6147921685923], [435.403218786657, 470.74259129379413, 615.3542648093958, 483.61792559031693, 607.9455289424717, 454.9949861614464, 869.45041758392, 750.3595195751914, 754.7958625343501, 508.38715645396553, 368.2779213892305, 662.23752125613, 350.46366230046397, 619.8010888063362, 497.9560438683688, 420.64163974607766, 487.16698403905633, 273.3352931767504, 354.02637708217384, 457.9408818614016, 496.2986534025747, 364.84710143814976, 458.29907844925157, 634.073520178434, 558.7161089429649, 603.6634230782621, 514.1019407724017, 539.6741842214251, 585.0639516732675, 488.3003071211236], [334.0264519516021, 459.5702037859653, 543.8547654459309, 471.6623772418301, 500.98627686914386, 740.3857774449933, 487.4853744264201, 664.5373560191691, 573.764159193263, 471.32565842016527, 448.8845519093864, 729.3173859836543, 453.34766656988694, 428.4975196541853, 575.1404740691066, 190.18782164376034, 243.90403003048107, 430.03959300145215, 429.08666492876233, 508.89662188951297, 669.6400651031191, 516.2894766192492, 441.39320293407405, 653.1948574772491, 529.6831617222962, 176.0833629734244, 568.7136007686755, 461.66494617366294, 443.39303344518356, 840.642834252332], [347.676690455591, 475.0701395711058, 383.94468812449156, 456.7512619303556, 547.1719187673109, 224.69458657065758, 458.98685335259506, 599.8561007491281, 231.02565460233575, 610.5318803183029, 763.3423474509603, 548.8104762105211, 445.95788564834953, 844.6566709331175, 591.2236009653337, 586.0438760821825, 399.6820689195621, 395.17360423878256, 535.9853351258233, 332.27242110850426, 801.7584039310705, 190.6337233666032, 805.700536966829, 799.6824375238089, 346.29917202656327, 611.7423892505719, 705.8824305058062, 535.9691379719488, 488.1708623023391, 604.3772264289142], [687.7108994865216, 483.44749361779685, 661.8182197739575, 591.5452701990528, 151.60961549943875, 524.1475889465452, 745.1142999852398, 665.6103992924466, 701.3015233859578, 648.9854638583182, 403.08097902196505, 384.97216329583586, 442.52161997463816, 590.5026536093199, 219.04366558018955, 899.2103705796073, 562.4908789323547, 666.088957218587, 496.97593850278065, 777.9572405840922, 531.7316118485633, 500.7782009017233, 646.4095967934252, 633.5713368259554, 608.1857007168994, 585.4020395597571, 490.06193749044934, 463.884131549627, 632.7981360348942, 634.8055942938928], [482.5550451528366, 691.7011356960619, 496.2851035642388, 529.4040886765091, 444.3593296445004, 198.06208336708823, 365.6472909266031, 391.3885069938369, 859.494451604626, 275.19483951927816, 568.4478784631463, 203.74971298680123, 676.2053582803082, 527.9859302404323, 714.4565600799949, 288.9012675397431, 629.6056652113498, 326.2525932990075, 519.5740740263301, 696.8119752318905, 347.1796230415255, 388.6576994098651, 357.54758351840974, 873.5528483422207, 507.0189947052724, 508.1981784529926, 536.9527958233257, 871.2838601964829, 361.93416709279154, 496.5981745168124]]\n", + "data2 = [[103,104,104,105,105,106,106,106,107,107,106,106,105,105,104,104,104,104,105,107,107,106,105,105,107,108,109,110,110,110,110,110,110,109,109,109,109,109,109,108,107,107,107,107,106,106,105,104,104,104,104,104,104,104,103,103,103,103,102,102,101,101,100,100,100,100,100,99,98,97,97,96,96,96,96,96,96,96,95,95,95,94,94,94,94,94,94], [104,104,105,105,106,106,107,107,107,107,107,107,107,106,106,106,106,106,106,108,108,108,106,106,108,109,110,110,112,112,113,112,111,110,110,110,110,109,109,109,108,107,107,107,107,106,106,105,104,104,104,104,104,104,104,103,103,103,103,102,102,101,101,100,100,100,100,99,99,98,97,97,96,96,96,96,96,96,96,95,95,95,94,94,94,94,94], [104,105,105,106,106,107,107,108,108,108,108,108,108,108,108,108,108,108,108,108,110,110,110,110,110,110,110,111,113,115,116,115,113,112,110,110,110,110,110,110,109,108,108,108,108,107,106,105,105,105,105,105,105,104,104,104,104,103,103,103,102,102,102,101,100,100,100,99,99,98,97,97,96,96,96,96,96,96,96,96,95,95,94,94,94,94,94], [105,105,106,106,107,107,108,108,109,109,109,109,109,110,110,110,110,110,110,110,111,112,115,115,115,115,115,116,116,117,119,118,117,116,114,113,112,110,110,110,110,110,110,109,109,108,107,106,106,106,106,106,105,105,105,104,104,104,103,103,103,102,102,102,101,100,100,99,99,98,97,97,96,96,96,96,96,96,96,96,95,95,94,94,94,94,94], [105,106,106,107,107,108,108,109,109,110,110,110,110,111,110,110,110,110,111,114,115,116,121,121,121,121,121,122,123,124,124,123,121,119,118,117,115,114,112,111,110,110,110,110,110,110,109,109,108,109,107,107,106,106,105,105,104,104,104,104,103,103,102,102,102,101,100,100,99,99,98,97,96,96,96,96,96,96,96,96,95,95,94,94,94,94,94], [106,106,107,107,107,108,109,109,110,110,111,111,112,113,112,111,111,112,115,118,118,119,126,128,128,127,128,128,129,130,129,128,127,125,122,120,118,117,115,114,112,110,110,110,110,110,111,110,110,110,109,109,108,107,106,105,105,105,104,104,104,103,103,102,102,102,101,100,99,99,98,97,96,96,96,96,96,96,96,96,95,95,94,94,94,94,94], [106,107,107,108,108,108,109,110,110,111,112,113,114,115,114,115,116,116,119,123,125,130,133,134,134,134,134,135,135,136,135,134,132,130,128,124,121,119,118,116,114,112,111,111,111,112,112,111,110,110,110,109,108,108,107,108,107,106,105,104,104,104,103,103,103,102,101,100,99,99,98,97,96,96,96,96,96,96,96,96,95,95,95,94,94,94,94], [107,107,108,108,109,109,110,110,112,113,114,115,116,117,117,120,120,121,123,129,134,136,138,139,139,139,140,142,142,141,141,140,137,134,131,127,124,122,120,118,117,115,113,114,113,114,114,113,112,111,110,110,109,108,107,106,105,105,105,104,104,104,103,103,103,101,100,100,99,99,98,97,96,96,96,96,96,96,96,96,96,95,95,94,94,94,94], [107,108,108,109,109,110,111,112,114,115,116,117,118,119,121,125,125,127,131,136,140,141,142,144,144,145,148,149,148,147,146,144,140,138,136,130,127,125,123,121,119,118,117,117,116,116,116,115,114,113,113,111,110,109,108,107,106,105,105,103,103,102,102,102,103,101,100,100,100,99,98,98,97,96,96,96,96,96,96,96,96,95,95,95,94,94,94], [107,108,109,109,110,110,110,113,115,117,118,119,120,123,126,129,131,134,139,142,144,145,147,148,150,152,154,154,153,154,151,149,146,143,140,136,130,128,126,124,122,121,120,119,118,117,117,117,116,116,115,113,112,110,109,108,107,106,106,105,104,103,102,101,101,100,100,100,100,99,99,98,97,96,96,96,96,96,96,96,96,95,95,95,94,94,94], [107,108,109,109,110,110,110,112,115,117,119,122,125,127,130,133,137,141,143,145,148,149,152,155,157,159,160,160,161,162,159,156,153,149,146,142,139,134,130,128,126,125,122,120,120,120,119,119,119,118,117,115,113,111,110,110,109,108,107,106,106,105,104,104,103,102,100,100,100,99,99,98,97,96,96,96,96,96,96,96,96,95,95,95,95,94,94], [108,108,109,109,110,110,110,112,115,118,121,125,128,131,134,138,141,145,147,149,152,157,160,161,163,166,169,170,170,171,168,162,158,155,152,148,144,140,136,132,129,127,124,122,121,120,120,120,120,120,119,117,115,113,110,110,110,110,109,108,108,107,107,106,105,104,102,100,100,100,99,98,97,96,96,96,96,96,96,96,96,96,95,95,95,94,94], [108,109,109,110,110,111,112,114,117,120,124,128,131,135,138,142,145,149,152,155,158,163,166,167,170,173,175,175,175,173,171,169,164,160,156,153,149,144,140,136,131,129,126,124,123,123,122,121,120,120,120,119,117,115,111,110,110,110,110,110,109,109,110,109,108,106,103,101,100,100,100,98,97,96,96,96,96,96,96,96,96,96,95,95,95,95,94], [108,109,110,110,110,113,114,116,119,122,126,131,134,138,141,145,149,152,156,160,164,169,171,174,177,175,178,179,177,175,174,172,168,163,160,157,151,147,143,138,133,130,128,125,125,124,123,122,121,121,120,120,118,116,115,111,110,110,110,110,113,114,113,112,110,107,105,102,100,100,100,98,97,96,96,96,96,96,96,96,96,96,96,95,95,95,94], [108,109,110,110,112,115,116,118,122,125,129,133,137,140,144,149,152,157,161,165,169,173,176,179,179,180,180,180,178,178,176,175,171,165,163,160,153,148,143,139,135,132,129,128,127,125,124,124,123,123,122,122,120,118,117,118,115,117,118,118,119,117,116,115,112,109,107,105,100,100,100,100,97,96,96,96,96,96,96,96,96,96,96,95,95,95,95], [108,109,110,111,114,116,118,122,127,130,133,136,140,144,148,153,157,161,165,169,173,177,180,180,180,180,181,180,180,180,179,178,173,168,165,161,156,149,143,139,136,133,130,129,128,126,126,125,125,125,125,124,122,121,120,120,120,120,121,122,123,122,120,117,114,111,108,106,105,100,100,100,100,96,96,96,96,96,96,96,96,96,96,96,95,95,95], [107,108,110,113,115,118,121,126,131,134,137,140,143,148,152,157,162,165,169,173,177,181,181,181,180,181,181,181,180,180,180,178,176,170,167,163,158,152,145,140,137,134,132,130,129,127,127,126,127,128,128,126,125,125,125,123,126,128,129,130,130,125,124,119,116,114,112,110,107,106,105,100,100,100,96,96,96,96,96,96,96,96,96,96,96,95,95], [107,109,111,116,119,122,125,130,135,137,140,144,148,152,156,161,165,168,172,177,181,184,181,181,181,180,180,180,180,180,180,178,178,173,168,163,158,152,146,141,138,136,134,132,130,129,128,128,130,130,130,129,128,129,129,130,132,133,133,134,134,132,128,122,119,116,114,112,108,106,105,105,100,100,100,97,97,97,97,97,97,97,96,96,96,96,95], [108,110,112,117,122,126,129,135,139,141,144,149,153,156,160,165,168,171,177,181,184,185,182,180,180,179,178,178,180,179,179,178,176,173,168,163,157,152,148,143,139,137,135,133,131,130,130,131,132,132,132,131,132,132,133,134,136,137,137,137,136,134,131,124,121,118,116,114,111,109,107,106,105,100,100,100,97,97,97,97,97,97,97,96,96,96,96], [108,110,114,120,126,129,134,139,142,144,146,152,158,161,164,168,171,175,181,184,186,186,183,179,178,178,177,175,178,177,177,176,175,173,168,162,156,153,149,145,142,140,138,136,133,132,132,132,134,134,134,134,135,136,137,138,140,140,140,140,139,137,133,127,123,120,118,115,112,108,108,106,106,105,100,100,100,98,98,98,98,98,98,97,96,96,96], [108,110,116,122,128,133,137,141,143,146,149,154,161,165,168,172,175,180,184,188,189,187,182,178,176,176,175,173,174,173,175,174,173,171,168,161,157,154,150,148,145,143,141,138,135,135,134,135,135,136,136,137,138,139,140,140,140,140,140,140,140,139,135,130,126,123,120,117,114,111,109,108,107,106,105,100,100,100,99,99,98,98,98,98,97,97,96], [110,112,118,124,130,135,139,142,145,148,151,157,163,169,172,176,179,183,187,190,190,186,180,177,175,173,170,169,169,170,171,172,170,170,167,163,160,157,154,152,149,147,144,140,137,137,136,137,138,138,139,140,141,140,140,140,140,140,140,140,140,138,134,131,128,124,121,118,115,112,110,109,108,107,106,105,100,100,100,99,99,99,98,98,98,97,97], [110,114,120,126,131,136,140,143,146,149,154,159,166,171,177,180,182,186,190,190,190,185,179,174,171,168,166,163,164,163,166,169,170,170,168,164,162,161,158,155,153,150,147,143,139,139,139,139,140,141,141,142,142,141,140,140,140,140,140,140,140,137,134,131,128,125,122,119,116,114,112,110,109,109,108,107,105,100,100,100,99,99,99,98,98,97,97], [110,115,121,127,132,136,140,144,148,151,157,162,169,174,178,181,186,188,190,191,190,184,177,172,168,165,162,159,158,158,159,161,166,167,169,166,164,163,161,159,156,153,149,146,142,142,141,142,143,143,143,143,144,142,141,140,140,140,140,140,140,138,134,131,128,125,123,120,117,116,114,112,110,109,108,107,106,105,102,101,100,99,99,99,98,98,97], [110,116,121,127,132,136,140,144,148,154,160,166,171,176,180,184,189,190,191,191,191,183,176,170,166,163,159,156,154,155,155,158,161,165,170,167,166,165,163,161,158,155,152,150,146,145,145,145,146,146,144,145,145,144,142,141,140,140,140,140,138,136,134,131,128,125,123,121,119,117,115,113,112,111,111,110,108,106,105,102,100,100,99,99,99,98,98], [110,114,119,126,131,135,140,144,149,158,164,168,172,176,183,184,189,190,191,191,190,183,174,169,165,161,158,154,150,151,152,155,159,164,168,168,168,167,165,163,160,158,155,153,150,148,148,148,148,148,147,146,146,145,143,142,141,140,139,138,136,134,132,131,128,126,124,122,120,118,116,114,113,113,112,111,108,107,106,105,104,102,100,99,99,99,99], [110,113,119,125,131,136,141,145,150,158,164,168,172,177,183,187,189,191,192,191,190,183,174,168,164,160,157,153,150,149,150,154,158,162,166,170,170,168,166,164,162,160,158,155,152,151,151,151,151,151,149,148,147,146,145,143,142,140,139,137,135,134,132,131,129,127,125,123,121,119,117,116,114,114,113,112,110,108,107,105,103,100,100,100,100,99,99], [110,112,118,124,130,136,142,146,151,157,163,168,174,178,183,187,189,190,191,192,189,182,174,168,164,160,157,153,149,148,149,153,157,161,167,170,170,170,168,166,165,163,159,156,154,153,155,155,155,155,152,150,149,147,145,143,141,140,139,138,136,134,133,131,130,128,126,124,122,120,119,117,116,115,114,113,111,110,107,106,105,105,102,101,100,100,100], [110,111,116,122,129,137,142,146,151,158,164,168,172,179,183,186,189,190,192,193,188,182,174,168,164,161,157,154,151,149,151,154,158,161,167,170,170,170,170,169,168,166,160,157,156,156,157,158,159,159,156,153,150,148,146,144,141,140,140,138,136,135,134,133,131,129,127,125,123,122,120,118,117,116,115,114,112,111,110,108,107,106,105,104,102,100,100], [108,110,115,121,131,137,142,147,152,159,163,167,170,177,182,184,187,189,192,194,189,183,174,169,165,161,158,156,154,153,154,157,160,164,167,171,172,174,174,173,171,168,161,159,158,158,159,161,161,160,158,155,151,149,147,144,142,141,140,138,137,136,135,134,132,130,128,126,125,123,121,119,118,117,116,115,113,112,112,111,110,109,108,107,105,101,100], [108,110,114,120,128,134,140,146,152,158,162,166,169,175,180,183,186,189,193,195,190,184,176,171,167,163,160,158,157,156,157,159,163,166,170,174,176,178,178,176,172,167,164,161,161,160,161,163,163,163,160,157,153,150,148,146,144,142,141,140,139,138,136,135,134,133,129,127,126,124,122,121,119,118,117,116,114,113,112,111,110,110,109,109,107,104,100], [107,110,115,119,123,129,135,141,146,156,161,165,168,173,179,182,186,189,193,194,191,184,179,175,170,166,162,161,160,160,161,162,165,169,172,176,178,179,179,176,172,168,165,163,163,163,163,165,166,164,161,158,155,152,150,147,146,144,143,142,141,139,139,138,137,135,131,128,127,125,124,122,121,119,118,116,115,113,112,111,111,110,110,109,109,105,100], [107,110,114,117,121,126,130,135,142,151,159,163,167,171,177,182,185,189,192,193,191,187,183,179,174,169,167,166,164,164,165,166,169,171,174,178,179,180,180,178,173,169,166,165,165,166,165,168,169,166,163,159,157,154,152,149,148,147,146,145,143,142,141,140,139,138,133,130,128,127,125,124,122,120,118,117,115,112,111,111,111,111,110,109,108,106,100], [107,109,113,118,122,126,129,134,139,150,156,160,165,170,175,181,184,188,191,192,192,189,185,181,177,173,171,169,168,167,169,170,172,174,176,178,179,180,180,179,175,170,168,166,166,168,168,170,170,168,164,160,158,155,152,151,150,149,149,148,147,145,144,143,142,141,136,133,130,129,127,125,123,120,119,118,115,112,111,111,111,110,109,109,109,105,100], [105,107,111,117,121,124,127,131,137,148,154,159,164,168,174,181,184,187,190,191,191,190,187,184,180,178,175,174,172,171,173,173,173,176,178,179,180,180,180,179,175,170,168,166,168,169,170,170,170,170,166,161,158,156,154,153,151,150,150,150,150,148,147,146,145,143,139,135,133,131,129,126,124,121,120,118,114,111,111,111,110,110,109,107,106,104,100], [104,106,110,114,118,121,125,129,135,142,150,157,162,167,173,180,183,186,188,190,190,190,189,184,183,181,180,179,179,176,177,176,176,177,178,179,180,180,179,177,173,169,167,166,167,169,170,170,170,170,167,161,159,157,155,153,151,150,150,150,150,150,150,149,147,145,141,138,135,133,130,127,125,123,121,118,113,111,110,110,109,109,107,106,105,103,100], [104,106,108,111,115,119,123,128,134,141,148,154,161,166,172,179,182,184,186,189,190,190,190,187,185,183,180,180,180,179,179,177,176,177,178,178,178,177,176,174,171,168,166,164,166,168,170,170,170,170,168,162,159,157,155,153,151,150,150,150,150,150,150,150,150,148,144,140,137,134,132,129,127,125,122,117,111,110,107,107,106,105,104,103,102,101,100], [103,105,107,110,114,118,122,127,132,140,146,153,159,165,171,176,180,183,185,186,189,190,188,187,184,182,180,180,180,179,178,176,176,176,176,174,174,173,172,170,168,167,165,163,164,165,169,170,170,170,166,162,159,157,155,153,151,150,150,150,150,150,150,150,150,150,146,142,139,136,133,131,128,125,122,117,110,108,106,105,104,103,103,101,101,101,101], [102,103,106,108,112,116,121,125,130,138,145,151,157,163,170,174,178,181,181,184,186,186,187,186,184,181,180,180,180,179,178,174,173,173,171,170,170,169,168,167,166,164,163,162,161,164,167,169,170,168,164,160,158,157,155,153,151,150,150,150,150,150,150,150,150,150,147,144,141,138,135,133,128,125,122,116,109,107,104,104,103,102,101,101,101,101,101], [101,102,105,107,110,115,120,124,129,136,143,149,155,162,168,170,174,176,178,179,181,182,184,184,183,181,180,180,179,177,174,172,170,168,166,165,164,164,164,164,162,160,159,159,158,160,162,164,166,166,163,159,157,156,155,153,151,150,150,150,150,150,150,150,150,150,149,146,143,140,137,133,129,124,119,112,108,105,103,103,102,101,101,101,101,100,100], [101,102,104,106,109,113,118,122,127,133,141,149,155,161,165,168,170,172,175,176,177,179,181,181,181,180,180,179,177,174,171,167,165,163,161,160,160,160,160,160,157,155,155,154,154,155,157,159,161,161,161,159,156,154,154,153,151,150,150,150,150,150,150,150,150,150,149,147,144,141,137,133,129,123,116,110,107,104,102,102,101,101,101,100,100,100,100], [102,103,104,106,108,112,116,120,125,129,137,146,154,161,163,165,166,169,172,173,174,175,177,178,178,178,178,177,174,171,168,164,160,158,157,157,156,156,156,155,152,151,150,150,151,151,152,154,156,157,157,156,155,153,152,152,151,150,150,150,150,150,150,150,150,150,150,147,144,141,138,133,127,120,113,109,106,103,101,101,101,100,100,100,100,100,100], [103,104,105,106,108,110,114,118,123,127,133,143,150,156,160,160,161,162,167,170,171,172,173,175,175,174,174,173,171,168,164,160,156,155,154,153,153,152,152,150,149,148,148,148,148,148,149,149,150,152,152,152,152,151,150,150,150,150,150,150,150,150,150,150,150,150,149,147,144,141,138,132,125,118,111,108,105,103,102,101,101,101,100,100,100,100,100], [104,105,106,107,108,110,113,117,120,125,129,138,145,151,156,156,157,158,160,164,166,168,170,171,172,171,171,169,166,163,160,156,153,151,150,150,149,149,149,148,146,146,146,146,146,146,146,147,148,148,149,149,149,148,148,148,148,149,149,150,150,150,150,150,150,150,148,146,143,141,136,129,123,117,110,108,105,104,103,102,102,101,101,100,100,100,100], [103,104,105,106,107,109,111,115,118,122,127,133,140,143,150,152,153,155,157,159,162,164,167,168,168,168,167,166,163,160,157,153,150,148,148,147,147,147,145,145,144,143,143,143,144,144,144,144,145,145,145,145,146,146,146,146,146,147,147,148,149,150,150,150,150,149,147,145,143,141,134,127,123,117,111,108,105,105,104,104,103,103,102,101,100,100,100], [102,103,104,105,106,107,109,113,116,120,125,129,133,137,143,147,149,151,152,154,158,161,164,165,164,164,163,163,160,157,154,151,149,147,145,145,144,143,141,140,141,141,141,141,141,142,142,142,142,142,142,142,143,143,143,144,144,145,146,146,146,147,148,148,148,148,145,143,142,140,134,128,123,117,112,108,106,105,105,104,104,103,102,101,100,100,99], [102,103,104,105,105,106,108,110,113,118,123,127,129,132,137,141,142,142,145,150,154,157,161,161,160,160,160,159,157,154,151,148,146,145,143,142,142,139,137,136,137,137,138,138,139,139,139,139,139,139,139,139,140,140,141,142,142,143,144,144,144,145,145,145,145,145,144,142,140,139,136,129,124,119,113,109,106,106,105,104,103,102,101,101,100,99,99], [102,103,104,104,105,106,107,108,111,116,121,124,126,128,131,134,135,137,139,143,147,152,156,157,157,157,156,155,153,151,148,146,143,142,141,140,138,135,133,132,132,133,133,133,134,135,135,135,135,136,136,137,137,138,138,139,140,141,141,142,142,143,142,142,141,141,140,139,137,134,133,129,125,121,114,110,107,106,106,104,103,102,101,100,99,99,99], [102,103,104,104,105,105,106,108,110,113,118,121,124,126,128,130,132,134,136,139,143,147,150,154,154,154,153,151,149,148,146,143,141,139,137,136,132,130,128,128,128,129,129,130,130,131,132,132,132,133,134,134,135,135,136,137,138,139,139,140,140,140,139,139,138,137,137,135,132,130,129,127,124,120,116,112,109,106,105,103,102,101,101,100,99,99,99], [101,102,103,104,104,105,106,107,108,110,114,119,121,124,126,128,129,132,134,137,140,143,147,149,151,151,151,149,147,145,143,141,138,136,134,131,128,126,124,125,125,126,126,127,128,128,129,129,130,130,131,131,132,132,133,134,135,135,136,136,137,137,136,136,135,134,133,131,129,128,127,126,123,119,115,111,109,107,105,104,103,102,101,100,100,100,99], [101,102,103,103,104,104,105,106,108,110,112,116,119,121,124,125,127,130,132,135,137,140,143,147,149,149,149,147,145,143,141,139,136,133,131,128,125,122,121,122,122,122,123,125,125,126,127,127,127,128,128,128,129,129,130,131,131,132,132,133,133,133,132,132,131,131,130,129,128,126,125,124,121,117,111,109,108,106,105,104,103,102,101,101,100,100,100], [100,101,102,103,103,104,105,106,107,108,110,114,117,119,121,123,126,128,130,133,136,139,141,144,146,147,146,145,143,141,138,136,133,130,127,124,121,120,120,120,120,120,121,122,123,124,124,125,125,126,126,125,126,126,126,125,126,127,128,128,129,129,128,128,128,128,128,128,126,125,123,122,119,114,109,108,107,106,105,104,103,103,102,102,101,100,100], [100,101,102,103,104,105,106,107,108,109,110,112,115,117,120,122,125,127,130,132,135,137,139,142,144,144,144,142,140,138,136,132,129,126,123,120,120,119,119,118,119,119,120,120,120,121,122,122,123,123,123,123,122,123,122,122,121,122,122,122,123,123,123,124,125,125,126,126,125,124,122,120,116,113,109,107,106,105,104,104,103,102,102,101,101,100,100], [100,101,102,103,104,105,106,107,108,109,110,112,114,117,119,122,124,127,129,131,134,136,138,140,142,142,142,140,138,136,133,129,125,122,120,119,118,118,117,116,117,117,118,119,119,120,120,120,121,121,121,122,121,120,120,120,119,119,120,120,120,120,120,120,123,123,124,124,124,123,121,119,114,112,108,106,106,104,104,103,102,102,101,101,100,100,99], [101,102,103,104,105,106,107,108,109,110,111,113,114,116,119,121,124,126,128,130,133,135,137,138,140,140,139,137,135,133,131,127,122,120,118,118,117,117,116,115,116,116,117,118,118,118,119,119,120,120,121,121,120,119,119,118,117,117,118,119,118,118,118,119,120,122,123,123,123,122,120,117,113,110,108,106,105,104,103,103,102,101,101,100,100,99,99], [101,102,103,104,105,106,107,108,109,110,111,111,113,115,118,121,123,125,127,129,131,133,135,137,138,138,137,134,132,130,127,122,120,118,116,116,116,116,115,113,114,115,116,117,117,118,118,119,119,119,120,120,119,118,117,117,116,116,117,117,117,118,119,119,119,120,121,121,121,121,119,116,113,110,107,105,105,103,103,103,102,101,100,100,99,99,99], [101,102,103,104,105,106,107,108,109,110,111,112,114,116,117,120,122,124,126,129,130,132,133,135,136,136,134,132,129,126,122,120,118,116,114,114,114,114,114,113,113,114,115,116,116,117,117,117,118,118,119,119,118,117,116,116,115,115,116,116,116,117,117,118,118,119,120,120,120,120,119,116,113,109,106,104,104,103,102,102,101,101,100,99,99,99,98], [101,102,103,104,105,106,107,108,109,110,111,113,115,117,117,118,121,123,126,128,130,130,131,132,133,134,131,129,125,122,120,118,116,114,113,112,112,113,112,112,111,112,113,113,114,115,116,116,117,117,118,118,116,116,115,115,115,114,114,115,116,116,117,117,118,118,119,119,120,120,117,115,112,108,106,104,103,102,102,102,101,100,99,99,99,98,98], [101,102,103,104,105,105,106,107,108,109,110,111,113,115,117,118,120,122,125,126,127,128,129,130,131,131,128,125,121,120,118,116,114,113,113,111,111,111,111,110,109,110,111,112,113,113,114,115,115,116,117,117,116,115,114,114,113,113,114,114,115,115,116,116,117,118,118,119,119,118,116,114,112,108,105,103,103,102,101,101,100,100,99,99,98,98,97], [100,101,102,103,104,105,106,107,108,109,110,110,111,113,115,118,120,121,122,124,125,125,126,127,128,127,124,121,120,118,116,114,113,112,112,110,109,109,108,108,108,109,110,111,112,112,113,114,114,115,116,116,115,114,113,112,112,113,113,114,114,115,115,116,116,117,117,118,118,117,115,113,111,107,105,103,102,101,101,100,100,100,99,99,98,98,97], [100,101,102,103,104,105,105,106,107,108,109,110,110,111,114,116,118,120,120,121,122,122,123,124,123,123,120,118,117,115,114,115,113,111,110,109,108,108,107,107,107,108,109,110,111,111,112,113,113,114,115,115,114,113,112,111,111,112,112,112,113,114,114,115,115,116,116,117,117,116,114,112,109,106,104,102,101,100,100,99,99,99,99,98,98,97,97]]\n", + "data3 = [[16,29, 12, 14, 16, 5, 9, 43, 25, 49, 57, 61, 37, 66, 79, 55, 51, 55, 17, 29, 9, 4, 9, 12, 9], [22,6, 2, 12, 23, 9, 2, 4, 11, 28, 49, 51, 47, 38, 65, 69, 59, 65, 59, 22, 11, 12, 9, 9, 13], [2, 5, 8, 44, 9, 22, 2, 5, 12, 34, 43, 54, 44, 49, 48, 54, 59, 69, 51, 21, 16, 9, 5, 4, 7], [3, 9, 9, 34, 9, 9, 2, 4, 13, 26, 58, 61, 59, 53, 54, 64, 55, 52, 53, 18, 3, 9, 12, 2, 8], [4, 2, 9, 8, 2, 23, 2, 4, 14, 31, 48, 46, 59, 66, 54, 56, 67, 54, 23, 14, 6, 8, 7, 9, 8], [5, 2, 23, 2, 9, 9, 9, 4, 8, 8, 6, 14, 12, 9, 14, 9, 21, 22, 34, 12, 9, 23, 9, 11, 13], [6, 7, 23, 23, 9, 4, 7, 4, 23, 11, 32, 2, 2, 5, 34, 9, 4, 12, 15, 19, 45, 9, 19, 9, 4]]\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "HeatMap(data = data)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "HeatMap(title= \"Heatmap Second Example\",\n", + " xLabel= \"X Label\",\n", + " yLabel= \"Y Label\",\n", + " data = data,\n", + " legendPosition = LegendPosition.TOP)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "HeatMap(title = \"Green Yellow White\",\n", + " data = data2,\n", + " showLegend = False,\n", + " color = GradientColor.GREEN_YELLOW_WHITE)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "colors = [Color.black, Color.yellow, Color.red]\n", + "HeatMap(title= \"Custom Gradient Example\",\n", + " data= data3,\n", + " color= GradientColor(colors))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "HeatMap(initWidth= 900,\n", + " initHeight= 300,\n", + " title= \"Custom size, no tooltips\",\n", + " data= data3,\n", + " useToolTip= False,\n", + " showLegend= False,\n", + " color= GradientColor.WHITE_BLUE)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": false, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/doc/Histogram.ipynb b/doc/Histogram.ipynb new file mode 100644 index 0000000..efff2be --- /dev/null +++ b/doc/Histogram.ipynb @@ -0,0 +1,170 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Histograms" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In a histogram, the height of each bar (bin) represents the number of data points that fall into its interval. They are generally used to show the distribution of a variable.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate some random data\n", + "import random\n", + "from beakerx_widgets import *\n", + "data1 = []\n", + "data2 = []\n", + "\n", + "for x in range(1, 10000):\n", + " data1.append(random.gauss(0, 1)) \n", + " data2.append(2*random.gauss(0, 1) + 1.0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Histogram(data= data1, binCount= 25)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Histogram(\n", + " initWidth=800,\n", + " initHeight=200,\n", + " title=\"Wide Histogram with Manual Parameters\",\n", + " xLabel=\"Size\",\n", + " yLabel=\"Count\",\n", + " rangeMin= -8, \n", + " rangeMax= 8, \n", + " data= data1,\n", + " binCount= 99, \n", + " color= Color(0, 154, 166))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Histogram(title= \"Default is Overlap\",\n", + " data= [data1, data2],\n", + " binCount= 99,\n", + " names= [\"old and tired\", \"new and improved\"],\n", + " color= [Color(0, 154, 166),\n", + " Color(230, 50, 50, 128) # transparent!\n", + " ])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Histogram(title= \"Stack\",\n", + " showLegend= False,\n", + " displayMode= Histogram.DisplayMode.STACK,\n", + " data= [data1, data2],\n", + " binCount= 99)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Histogram(title= \"Side by Side\",\n", + " displayMode= Histogram.DisplayMode.SIDE_BY_SIDE,\n", + " data = [data1,data2], \n", + " binCount= 55)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Histogram(title= \"Cumulative\",\n", + " cumulative= True,\n", + " data= data1,\n", + " binCount= 55)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Histogram(title= \"Normed, Area = 1.0\",\n", + " normed= True,\n", + " data= data1,\n", + " binCount= 55)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Histogram(log= True, data= data1, binCount= 99)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": false, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/doc/InitCells.ipynb b/doc/InitCells.ipynb new file mode 100644 index 0000000..4b71c50 --- /dev/null +++ b/doc/InitCells.ipynb @@ -0,0 +1,63 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Initialization Cells\n", + "\n", + "BeakerX supports them by including the [already existing contributed extension](https://github.com/ipython-contrib/jupyter_contrib_nbextensions/blob/master/src/jupyter_contrib_nbextensions/nbextensions/init_cell/main.js). The View -> Cell Toolbar -> Initialization Cell menu item makes the UI visible. It also adds a calculator button icon to the toolbar to run the init cells.\n", + "\n", + "Initialization cells are not run unless a notebook is trusted. Click on the trust button to enable this feature on a given notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "init_cell": true + }, + "outputs": [], + "source": [ + "import time\n", + "print(time.strftime(\"%Y-%m-%d %H:%M\"))" + ] + } + ], + "metadata": { + "beakerx_kernel_parameters": {}, + "celltoolbar": "Initialization Cell", + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": false, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/doc/KernelMagics.ipynb b/doc/KernelMagics.ipynb new file mode 100644 index 0000000..511b70a --- /dev/null +++ b/doc/KernelMagics.ipynb @@ -0,0 +1,269 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Magics to Access the JVM Kernels from Python\n", + "\n", + "BeakerX has magics for Python so you can run cells in the other languages.\n", + "The first few cells below show how complete the implementation is with Groovy, then we have just one cell in each other language.\n", + "\n", + "There are also [Polyglot Magics](../groovy/PolyglotMagic.ipynb) magics for accessing Python from the JVM.\n", + "\n", + "You can communicate between languages with [Autotranslation](GeneralAutotranslation.ipynb).\n", + "\n", + "## Groovy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%groovy\n", + "println(\"stdout works\")\n", + "f = {it + \" work\"}\n", + "f(\"results\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%groovy\n", + "new Plot(title:\"plots work\", initHeight: 200)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%groovy\n", + "[a:\"tables\", b:\"work\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%groovy\n", + "\"errors work\"/1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%groovy\n", + "HTML(\"

HTML works

\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%groovy\n", + "def p = new Plot(title : 'Plots Work', xLabel: 'Horizontal', yLabel: 'Vertical');\n", + "p << new Line(x: [0, 1, 2, 3, 4, 5], y: [0, 1, 6, 5, 2, 8])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Java" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%java\n", + "import java.util.List;\n", + "import com.twosigma.beakerx.chart.xychart.Plot;\n", + "import java.util.Arrays;\n", + "\n", + "Plot p = new Plot();\n", + "\n", + "p.setTitle(\"Java Works\");\n", + "p.setXLabel(\"Horizontal\");\n", + "p.setYLabel(\"Vertical\");\n", + "\n", + "Bars b = new Bars();\n", + "\n", + "List x = Arrays.asList(0, 1, 2, 3, 4, 5);\n", + "List y = Arrays.asList(0, 1, 6, 5, 2, 8);\n", + "Line line = new Line();\n", + "line.setX(x);\n", + "line.setY(y);\n", + "p.add(line);\n", + " \n", + "return p;" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Scala" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%scala\n", + "val plot = new Plot { title = \"Scala Works\"; xLabel=\"Horizontal\"; yLabel=\"Vertical\" }\n", + "val line = new Line {x = Seq(0, 1, 2, 3, 4, 5); y = Seq(0, 1, 6, 5, 2, 8)}\n", + "plot.add(line)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kotlin" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%kotlin\n", + "val x: MutableList = mutableListOf(0, 1, 2, 3, 4, 5)\n", + "val y: MutableList = mutableListOf(0, 1, 6, 5, 2, 8)\n", + "val line = Line()\n", + "line.setX(x)\n", + "line.setY(y)\n", + "\n", + "val plot = Plot()\n", + "plot.setTitle(\"Kotlin Works\")\n", + "plot.setXLabel(\"Horizontal\")\n", + "plot.setYLabel(\"Vertical\")\n", + "plot.add(line)\n", + "plot" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Clojure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%clojure\n", + "(import '[com.twosigma.beakerx.chart.xychart Plot]\n", + " '[com.twosigma.beakerx.chart.xychart.plotitem Line])\n", + "(doto (Plot.)\n", + " (.setTitle \"Clojure Works\")\n", + " (.setXLabel \"Horizontal\")\n", + " (.setYLabel \"Vertical\")\n", + " (.add (doto (Line.)\n", + " (.setX [0, 1, 2, 3, 4, 5])\n", + " (.setY [0, 1, 6, 5, 2, 8]))))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## SQL" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%sql\n", + "%defaultDatasource jdbc:h2:mem:db\n", + "DROP TABLE IF EXISTS cities;\n", + "CREATE TABLE cities(\n", + " zip_code varchar(5),\n", + " latitude float,\n", + " longitude float,\n", + " city varchar(100),\n", + " state varchar(2),\n", + " county varchar(100),\n", + " PRIMARY KEY (zip_code),\n", + ") AS SELECT\n", + " zip_code,\n", + " latitude,\n", + " longitude,\n", + " city,\n", + " state,\n", + " county\n", + "FROM CSVREAD('../resources/data/UScity.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%sql\n", + "SELECT * FROM cities WHERE state = 'NY'" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": false, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/doc/OutputContainers.ipynb b/doc/OutputContainers.ipynb new file mode 100644 index 0000000..6320f35 --- /dev/null +++ b/doc/OutputContainers.ipynb @@ -0,0 +1,171 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Output Containers and Layout Managers\n", + "\n", + "Output containers are objects that hold a collection of other objects, and displays all its contents, even when they are complex interactive objects and MIME type.\n", + "By default the contents are just stacked up on the page, but you can configure them to get tabs, a grid, or cycling." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Stacked Output Containers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from beakerx_widgets.outputs import *\n", + "from beakerx_widgets.plots import *\n", + "from beakerx_base import *\n", + "import pandas as pd\n", + "\n", + "o = OutputContainer()\n", + "o.addItem(\"simplest example\")\n", + "o.addItem([2, 3, 5, 7])\n", + "o.addItem(BeakerxHTML(\"

title

\"))\n", + "o.addItem(None)\n", + "o" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rates = pd.read_csv('./resources/data/interest-rates.csv')\n", + "c = Color(120, 120, 120, 100)\n", + "plot1 = Plot(initWidth= 300, initHeight= 400) \n", + "plot1.add(Points(x= rates.y1, y=rates.y30, size= 3, displayName=\"y1 vs y30\"))\n", + "plot1.add(Line(x= rates.y1, y=rates.y30, color= c))\n", + "plot1.add(Points(x= rates.m3, y=rates.y5, size= 3, displayName=\"m3 vs y5\"))\n", + "plot1.add(Line(x= rates.m3, y=rates.y5, color= c))\n", + "plot1.setShowLegend(False)\n", + "plot2 = SimpleTimePlot(rates, [\"m3\", \"y1\"], showLegend=False, initWidth= 300, initHeight= 400)\n", + "plot3 = SimpleTimePlot(rates, [\"y5\", \"y10\"], showLegend=False, initWidth= 300, initHeight= 400)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tabbed Output Containers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "table = pd.DataFrame({'a' : [1, 2, 1, 5], 'b' : [\"a\", \"ab\", \"b\", \"ababa\"]})\n", + "\n", + "l = TabbedOutputContainerLayoutManager()\n", + "l.setBorderDisplayed(False)\n", + "o = OutputContainer()\n", + "o.setLayoutManager(l)\n", + "o.addItem(plot1, \"Scatter with History\")\n", + "o.addItem(plot2, \"Short Term\")\n", + "o.addItem(plot3, \"Long Term\")\n", + "o.addItem(table, \"Pandas Table\")\n", + "o" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Grid Output Containers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bars = CategoryPlot(initWidth= 300, initHeight= 400)\n", + "bars.add(CategoryBars(value= [[1.1, 2.4, 3.8], [1, 3, 4]]))\n", + "\n", + "lg = GridOutputContainerLayoutManager(3)\n", + "\n", + "og = OutputContainer()\n", + "og.setLayoutManager(lg)\n", + "og.addItem(plot1, \"Scatter with History\")\n", + "og.addItem(plot2, \"Short Term\")\n", + "og.addItem(plot3, \"Long Term1\")\n", + "og.addItem(bars, \"Bar Chart\")\n", + "og.addItem(BeakerxHTML(\"
something
\"))\n", + "og.addItem(table, \"Pandas Table\")\n", + "og" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cycling Output Container" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "l = CyclingOutputContainerLayoutManager()\n", + "l.setPeriod(2345); # milliseconds\n", + "l.setBorderDisplayed(False);\n", + "o = OutputContainer()\n", + "o.setLayoutManager(l)\n", + "o.addItem(plot1, \"Scatter with History\")\n", + "o.addItem(plot2, \"Short Term\")\n", + "o.addItem(plot3, \"Long Term\")\n", + "o" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": false, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/doc/PlotActions.ipynb b/doc/PlotActions.ipynb new file mode 100644 index 0000000..de18aa4 --- /dev/null +++ b/doc/PlotActions.ipynb @@ -0,0 +1,227 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot Actions\n", + "\n", + "Plots can be configured to run code or other cells when the user clicks on or types into them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from beakerx_widgets import *\n", + "from beakerx_base import *\n", + "from random import randint" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "abc = 0 # test variable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "p = Plot(showLegend = True, useToolTip= False)\n", + "\n", + "def on_click1(info):\n", + " info.graphics.display_name = \"new name\"\n", + " \n", + "def on_click2(info):\n", + " info.graphics.y[0] = randint(0, 9)\n", + " \n", + "p.add(Line(x = [1, 2, 3], y = [2, 3, 4], width = 10, displayName = \"line 1\").onClick(on_click1))\n", + "p.add(Line(x = [1, 2, 3], y = [5, 6, 7], width = 10, displayName = \"line 2\").onClick(on_click2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "p = Plot(showLegend = True, useToolTip = False)\n", + "\n", + "def on_click(info):\n", + " global abc\n", + " abc += 1\n", + " beakerx.runByTag(\"on_click_any_action\")\n", + " \n", + "\n", + "p.add(Line(x = [1, 2, 3], y = [2, 3, 4], width = 10, displayName = \"line 1\").onClick(on_click))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "on_click_any_action" + ] + }, + "outputs": [], + "source": [ + "print(abc)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plot = Plot(useToolTip = False)\n", + "plot.add(Points(x = list(range(1, 6)), y = list(range(1, 6)), size = 12, color = Color.orange, outlineColor = Color.black, displayName = \"orange\").onClick(\"run_tag\"))\n", + "plot.add(Points(x = list(range(1, 6)), y = list(range(3, 9)), size = 12, color = Color.green, outlineColor = Color.black, displayName = \"green\").onClick(\"run_tag\"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "run_tag" + ] + }, + "outputs": [], + "source": [ + "details = plot.details\n", + "item = details.graphics\n", + "index = details.index\n", + "key = details.key\n", + "tag = details.tag\n", + "action = details.actionType\n", + "print(\"You clicked on {} {} (element with coordinates [{},{}])\".format(item.display_name, type(item).__name__, item.x[index], item.y[index]))\n", + "print(\"Key pressed = {} Tag = {} Action = {}\".format(key, tag, action))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "barsPlot = Plot(useToolTip = False);\n", + "bars = Bars(x = list(range(1,6)), y = [5, 2, 4, 3, 7], color = Color.green, outlineColor = Color.black, width = 0.3)\n", + "\n", + "\n", + "def on_space(info):\n", + " info.graphics.y[info.index] += 1\n", + " \n", + "def on_caps_lock(info):\n", + " info.graphics.y[info.index] -= 1\n", + "\n", + "#Also buttons like KeyboardCodes.UP_ARROW is handled by jupyter notebook\n", + "bars.onKey(KeyboardCodes.SPACE, on_space)\n", + "\n", + "#Also buttons like KeyboardCodes.DOWN_ARROW is handled by jupyter notebook\n", + "bars.onKey(KeyboardCodes.CAPS_LOCK, on_caps_lock)\n", + " \n", + "#Tag events working\n", + "bars.onKey(\"T\", \"run_tag2\")\n", + "\n", + "barsPlot.add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "run_tag2" + ] + }, + "outputs": [], + "source": [ + "details = barsPlot.details\n", + "item = details.graphics\n", + "index = details.index\n", + "key = details.key\n", + "tag = details.tag\n", + "action = details.actionType\n", + "print(\"Key action on {} (element with coordinates [{}, {}])\".format(type(item).__name__, item.x[index], item.y[index]))\n", + "print(\"Key pressed = {} Tag = {} Action = {}\".format(key, tag, action))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "barsPlot = Plot(useToolTip = False);\n", + "bars = Bars(x = list(range(1,5)), y = [5, 2, 4, 3, 7], color = Color.green, outlineColor = Color.black, width = 0.3)\n", + " \n", + "#Buttons like KeyboardCodes.UP_ARROW is handled by jupyter notebook\n", + "def on_space(info):\n", + " global abc\n", + " abc+=1\n", + " beakerx.runByTag('run_tag3')\n", + " \n", + "bars.onKey(KeyboardCodes.SPACE, on_space)\n", + "barsPlot.add(bars)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "run_tag3" + ] + }, + "outputs": [], + "source": [ + "print(abc)" + ] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": false, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/doc/PlotJSAPI.ipynb b/doc/PlotJSAPI.ipynb new file mode 100644 index 0000000..fcba60d --- /dev/null +++ b/doc/PlotJSAPI.ipynb @@ -0,0 +1,336 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# JavaScript API to BeakerX Plot Widgets\n", + "\n", + "In addition to being available in Jupyter with BeakerX as demonstrated below, you can also access with widgets as a [JS library on npm](https://www.npmjs.com/package/beakerx) which you can [embed on any page](http://ipywidgets.readthedocs.io/en/latest/embedding.html), like [nbviewer does](http://nbviewer.jupyter.org/github/twosigma/beakerx/blob/bc12afb1a19e9c237131d17c22e92edfa44028b1/doc/contents/nbviewer/NbviewerCategoryPlot.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%javascript\n", + "var plot = new beakerx.Plot({\n", + " title: 'Title',\n", + " xLabel: \"Horizontal\", \n", + " yLabel: \"Vertical\",\n", + " initWidth: 800,\n", + " initHeight: 500\n", + "})\n", + "plot.display(this);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%javascript\n", + "\n", + "// Run to define example data for demos\n", + "beakerx.rates = [{\"time\":\"1990-01-31T00:00:00.000Z\",\"m3\":7.8981,\"m6\":7.9562,\"y1\":7.921,\"y2\":8.0852,\"y3\":8.1324,\"y5\":8.1195,\"y7\":8.1962,\"y10\":8.2067,\"y30\":8.2586},{\"time\":\"1990-02-28T00:00:00.000Z\",\"m3\":8.0021,\"m6\":8.1211,\"y1\":8.1111,\"y2\":8.3705,\"y3\":8.3868,\"y5\":8.4247,\"y7\":8.4758,\"y10\":8.4732,\"y30\":8.5037},{\"time\":\"1990-03-31T00:00:00.000Z\",\"m3\":8.17,\"m6\":8.28,\"y1\":8.35,\"y2\":8.6268,\"y3\":8.6273,\"y5\":8.6005,\"y7\":8.6482,\"y10\":8.5886,\"y30\":8.5632},{\"time\":\"1990-04-30T00:00:00.000Z\",\"m3\":8.0405,\"m6\":8.27,\"y1\":8.4045,\"y2\":8.724,\"y3\":8.7825,\"y5\":8.768,\"y7\":8.813,\"y10\":8.7855,\"y30\":8.756},{\"time\":\"1990-05-31T00:00:00.000Z\",\"m3\":8.0068,\"m6\":8.1909,\"y1\":8.3164,\"y2\":8.6423,\"y3\":8.6923,\"y5\":8.7359,\"y7\":8.7836,\"y10\":8.7582,\"y30\":8.7314},{\"time\":\"1990-06-30T00:00:00.000Z\",\"m3\":7.9867,\"m6\":8.0452,\"y1\":8.0962,\"y2\":8.351,\"y3\":8.3981,\"y5\":8.4305,\"y7\":8.5176,\"y10\":8.48,\"y30\":8.4576},{\"time\":\"1990-07-31T00:00:00.000Z\",\"m3\":7.8748,\"m6\":7.9233,\"y1\":7.941,\"y2\":8.1571,\"y3\":8.2648,\"y5\":8.331,\"y7\":8.4552,\"y10\":8.4714,\"y30\":8.4981},{\"time\":\"1990-08-31T00:00:00.000Z\",\"m3\":7.6943,\"m6\":7.7661,\"y1\":7.7791,\"y2\":8.0613,\"y3\":8.2187,\"y5\":8.4365,\"y7\":8.6439,\"y10\":8.7526,\"y30\":8.8635},{\"time\":\"1990-09-30T00:00:00.000Z\",\"m3\":7.5979,\"m6\":7.6984,\"y1\":7.7632,\"y2\":8.0795,\"y3\":8.2674,\"y5\":8.5137,\"y7\":8.7868,\"y10\":8.8932,\"y30\":9.0289},{\"time\":\"1990-10-31T00:00:00.000Z\",\"m3\":7.4,\"m6\":7.5295,\"y1\":7.5514,\"y2\":7.8777,\"y3\":8.0686,\"y5\":8.3277,\"y7\":8.5936,\"y10\":8.7195,\"y30\":8.8577},{\"time\":\"1990-11-30T00:00:00.000Z\",\"m3\":7.2905,\"m6\":7.385,\"y1\":7.3135,\"y2\":7.5995,\"y3\":7.737,\"y5\":8.0225,\"y7\":8.2765,\"y10\":8.392,\"y30\":8.5405},{\"time\":\"1990-12-31T00:00:00.000Z\",\"m3\":6.9495,\"m6\":7.0265,\"y1\":7.0505,\"y2\":7.314,\"y3\":7.466,\"y5\":7.7265,\"y7\":8.0005,\"y10\":8.075,\"y30\":8.237},{\"time\":\"1991-01-31T00:00:00.000Z\",\"m3\":6.4105,\"m6\":6.5757,\"y1\":6.6443,\"y2\":7.1252,\"y3\":7.3776,\"y5\":7.7,\"y7\":7.9705,\"y10\":8.0919,\"y30\":8.2695},{\"time\":\"1991-02-28T00:00:00.000Z\",\"m3\":6.1163,\"m6\":6.1947,\"y1\":6.2668,\"y2\":6.8668,\"y3\":7.0774,\"y5\":7.4726,\"y7\":7.7326,\"y10\":7.8547,\"y30\":8.0342},{\"time\":\"1991-03-31T00:00:00.000Z\",\"m3\":6.0935,\"m6\":6.1985,\"y1\":6.396,\"y2\":7.103,\"y3\":7.3535,\"y5\":7.772,\"y7\":8.0025,\"y10\":8.11,\"y30\":8.288},{\"time\":\"1991-04-30T00:00:00.000Z\",\"m3\":5.8255,\"m6\":5.9768,\"y1\":6.2359,\"y2\":6.9482,\"y3\":7.2318,\"y5\":7.7009,\"y7\":7.9227,\"y10\":8.0391,\"y30\":8.2095},{\"time\":\"1991-05-31T00:00:00.000Z\",\"m3\":5.6336,\"m6\":5.8732,\"y1\":6.1305,\"y2\":6.7836,\"y3\":7.1168,\"y5\":7.7014,\"y7\":7.9423,\"y10\":8.0677,\"y30\":8.2673},{\"time\":\"1991-06-30T00:00:00.000Z\",\"m3\":5.751,\"m6\":6.024,\"y1\":6.3585,\"y2\":6.956,\"y3\":7.3915,\"y5\":7.937,\"y7\":8.171,\"y10\":8.284,\"y30\":8.472},{\"time\":\"1991-07-31T00:00:00.000Z\",\"m3\":5.7509,\"m6\":5.9745,\"y1\":6.3055,\"y2\":6.9177,\"y3\":7.3759,\"y5\":7.9114,\"y7\":8.1468,\"y10\":8.2727,\"y30\":8.4523},{\"time\":\"1991-08-31T00:00:00.000Z\",\"m3\":5.4977,\"m6\":5.6336,\"y1\":5.7782,\"y2\":6.4332,\"y3\":6.7973,\"y5\":7.425,\"y7\":7.74,\"y10\":7.9,\"y30\":8.1436},{\"time\":\"1991-09-30T00:00:00.000Z\",\"m3\":5.3735,\"m6\":5.479,\"y1\":5.5725,\"y2\":6.1815,\"y3\":6.5,\"y5\":7.136,\"y7\":7.4765,\"y10\":7.65,\"y30\":7.948},{\"time\":\"1991-10-31T00:00:00.000Z\",\"m3\":5.1441,\"m6\":5.2591,\"y1\":5.3336,\"y2\":5.9123,\"y3\":6.2291,\"y5\":6.8714,\"y7\":7.2464,\"y10\":7.5273,\"y30\":7.9305},{\"time\":\"1991-11-30T00:00:00.000Z\",\"m3\":4.6895,\"m6\":4.8011,\"y1\":4.8895,\"y2\":5.5589,\"y3\":5.9037,\"y5\":6.6179,\"y7\":7.0589,\"y10\":7.4174,\"y30\":7.9216},{\"time\":\"1991-12-31T00:00:00.000Z\",\"m3\":4.1843,\"m6\":4.2614,\"y1\":4.3781,\"y2\":5.0257,\"y3\":5.3929,\"y5\":6.1862,\"y7\":6.6852,\"y10\":7.0886,\"y30\":7.7019},{\"time\":\"1992-01-31T00:00:00.000Z\",\"m3\":3.9062,\"m6\":4.01,\"y1\":4.151,\"y2\":4.9581,\"y3\":5.3957,\"y5\":6.2429,\"y7\":6.7048,\"y10\":7.0324,\"y30\":7.5819},{\"time\":\"1992-02-29T00:00:00.000Z\",\"m3\":3.95,\"m6\":4.0763,\"y1\":4.2874,\"y2\":5.2121,\"y3\":5.72,\"y5\":6.5779,\"y7\":6.9621,\"y10\":7.3379,\"y30\":7.8547},{\"time\":\"1992-03-31T00:00:00.000Z\",\"m3\":4.1386,\"m6\":4.3309,\"y1\":4.6345,\"y2\":5.685,\"y3\":6.1832,\"y5\":6.9455,\"y7\":7.2573,\"y10\":7.5423,\"y30\":7.9695},{\"time\":\"1992-04-30T00:00:00.000Z\",\"m3\":3.839,\"m6\":4.0029,\"y1\":4.299,\"y2\":5.3443,\"y3\":5.9252,\"y5\":6.7843,\"y7\":7.1529,\"y10\":7.4805,\"y30\":7.9624},{\"time\":\"1992-05-31T00:00:00.000Z\",\"m3\":3.719,\"m6\":3.8815,\"y1\":4.1895,\"y2\":5.2255,\"y3\":5.8135,\"y5\":6.6925,\"y7\":7.0615,\"y10\":7.392,\"y30\":7.891},{\"time\":\"1992-06-30T00:00:00.000Z\",\"m3\":3.745,\"m6\":3.895,\"y1\":4.1659,\"y2\":5.0482,\"y3\":5.5973,\"y5\":6.4827,\"y7\":6.9045,\"y10\":7.2618,\"y30\":7.8418},{\"time\":\"1992-07-31T00:00:00.000Z\",\"m3\":3.2795,\"m6\":3.3832,\"y1\":3.5959,\"y2\":4.3555,\"y3\":4.9064,\"y5\":5.8368,\"y7\":6.3573,\"y10\":6.8445,\"y30\":7.5982},{\"time\":\"1992-08-31T00:00:00.000Z\",\"m3\":3.199,\"m6\":3.311,\"y1\":3.4719,\"y2\":4.1929,\"y3\":4.7248,\"y5\":5.5962,\"y7\":6.1219,\"y10\":6.5857,\"y30\":7.3905},{\"time\":\"1992-09-30T00:00:00.000Z\",\"m3\":2.9686,\"m6\":3.0419,\"y1\":3.1843,\"y2\":3.8943,\"y3\":4.4152,\"y5\":5.38,\"y7\":5.961,\"y10\":6.4152,\"y30\":7.341},{\"time\":\"1992-10-31T00:00:00.000Z\",\"m3\":2.9329,\"m6\":3.1276,\"y1\":3.3024,\"y2\":4.0829,\"y3\":4.6381,\"y5\":5.6005,\"y7\":6.1548,\"y10\":6.589,\"y30\":7.5319},{\"time\":\"1992-11-30T00:00:00.000Z\",\"m3\":3.2058,\"m6\":3.4395,\"y1\":3.6821,\"y2\":4.5795,\"y3\":5.1405,\"y5\":6.0379,\"y7\":6.49,\"y10\":6.8732,\"y30\":7.6068},{\"time\":\"1992-12-31T00:00:00.000Z\",\"m3\":3.2914,\"m6\":3.4686,\"y1\":3.7114,\"y2\":4.6736,\"y3\":5.2136,\"y5\":6.0759,\"y7\":6.4568,\"y10\":6.77,\"y30\":7.4364},{\"time\":\"1993-01-31T00:00:00.000Z\",\"m3\":3.0711,\"m6\":3.2379,\"y1\":3.4963,\"y2\":4.39,\"y3\":4.9326,\"y5\":5.8337,\"y7\":6.26,\"y10\":6.6,\"y30\":7.3416},{\"time\":\"1993-02-28T00:00:00.000Z\",\"m3\":2.9926,\"m6\":3.1605,\"y1\":3.3858,\"y2\":4.0984,\"y3\":4.5789,\"y5\":5.4289,\"y7\":5.8689,\"y10\":6.2589,\"y30\":7.0895},{\"time\":\"1993-03-31T00:00:00.000Z\",\"m3\":3.0143,\"m6\":3.1457,\"y1\":3.333,\"y2\":3.9496,\"y3\":4.4013,\"y5\":5.1948,\"y7\":5.6557,\"y10\":5.9752,\"y30\":6.8217},{\"time\":\"1993-04-30T00:00:00.000Z\",\"m3\":2.9305,\"m6\":3.0629,\"y1\":3.2443,\"y2\":3.8376,\"y3\":4.3024,\"y5\":5.1329,\"y7\":5.5938,\"y10\":5.9695,\"y30\":6.8543},{\"time\":\"1993-05-31T00:00:00.000Z\",\"m3\":3.025,\"m6\":3.1655,\"y1\":3.3635,\"y2\":3.977,\"y3\":4.4045,\"y5\":5.198,\"y7\":5.659,\"y10\":6.0355,\"y30\":6.919},{\"time\":\"1993-06-30T00:00:00.000Z\",\"m3\":3.1445,\"m6\":3.2945,\"y1\":3.5364,\"y2\":4.1636,\"y3\":4.5309,\"y5\":5.2168,\"y7\":5.6118,\"y10\":5.9627,\"y30\":6.8073},{\"time\":\"1993-07-31T00:00:00.000Z\",\"m3\":3.111,\"m6\":3.2576,\"y1\":3.4748,\"y2\":4.0719,\"y3\":4.4295,\"y5\":5.091,\"y7\":5.4762,\"y10\":5.8052,\"y30\":6.6257},{\"time\":\"1993-08-31T00:00:00.000Z\",\"m3\":3.0905,\"m6\":3.2355,\"y1\":3.4436,\"y2\":4.0014,\"y3\":4.3636,\"y5\":5.025,\"y7\":5.3532,\"y10\":5.6777,\"y30\":6.3232},{\"time\":\"1993-09-30T00:00:00.000Z\",\"m3\":3.0086,\"m6\":3.151,\"y1\":3.3562,\"y2\":3.8471,\"y3\":4.1652,\"y5\":4.7348,\"y7\":5.0762,\"y10\":5.36,\"y30\":5.9971},{\"time\":\"1993-10-31T00:00:00.000Z\",\"m3\":3.092,\"m6\":3.219,\"y1\":3.392,\"y2\":3.8745,\"y3\":4.175,\"y5\":4.707,\"y7\":5.054,\"y10\":5.334,\"y30\":5.939},{\"time\":\"1993-11-30T00:00:00.000Z\",\"m3\":3.1795,\"m6\":3.3615,\"y1\":3.579,\"y2\":4.1555,\"y3\":4.498,\"y5\":5.063,\"y7\":5.447,\"y10\":5.724,\"y30\":6.21},{\"time\":\"1993-12-31T00:00:00.000Z\",\"m3\":3.1277,\"m6\":3.3355,\"y1\":3.6073,\"y2\":4.2123,\"y3\":4.54,\"y5\":5.1468,\"y7\":5.4841,\"y10\":5.7741,\"y30\":6.2518},{\"time\":\"1994-01-31T00:00:00.000Z\",\"m3\":3.0445,\"m6\":3.253,\"y1\":3.5425,\"y2\":4.14,\"y3\":4.479,\"y5\":5.087,\"y7\":5.431,\"y10\":5.7505,\"y30\":6.291},{\"time\":\"1994-02-28T00:00:00.000Z\",\"m3\":3.3305,\"m6\":3.5347,\"y1\":3.8658,\"y2\":4.4742,\"y3\":4.8321,\"y5\":5.3953,\"y7\":5.7195,\"y10\":5.9732,\"y30\":6.4905},{\"time\":\"1994-03-31T00:00:00.000Z\",\"m3\":3.5909,\"m6\":3.9148,\"y1\":4.3191,\"y2\":4.9952,\"y3\":5.3957,\"y5\":5.9413,\"y7\":6.28,\"y10\":6.4826,\"y30\":6.9065},{\"time\":\"1994-04-30T00:00:00.000Z\",\"m3\":3.7805,\"m6\":4.2474,\"y1\":4.8158,\"y2\":5.5495,\"y3\":5.9942,\"y5\":6.5242,\"y7\":6.8042,\"y10\":6.9721,\"y30\":7.2689},{\"time\":\"1994-05-31T00:00:00.000Z\",\"m3\":4.2676,\"m6\":4.7857,\"y1\":5.3143,\"y2\":5.9681,\"y3\":6.3362,\"y5\":6.78,\"y7\":7.0062,\"y10\":7.1833,\"y30\":7.4124},{\"time\":\"1994-06-30T00:00:00.000Z\",\"m3\":4.2491,\"m6\":4.7195,\"y1\":5.2673,\"y2\":5.9314,\"y3\":6.2682,\"y5\":6.6955,\"y7\":6.9068,\"y10\":7.1014,\"y30\":7.395},{\"time\":\"1994-07-31T00:00:00.000Z\",\"m3\":4.4565,\"m6\":4.9535,\"y1\":5.4765,\"y2\":6.13,\"y3\":6.476,\"y5\":6.9095,\"y7\":7.1245,\"y10\":7.298,\"y30\":7.579},{\"time\":\"1994-08-31T00:00:00.000Z\",\"m3\":4.6117,\"m6\":5.0817,\"y1\":5.5622,\"y2\":6.1774,\"y3\":6.4957,\"y5\":6.8778,\"y7\":7.0622,\"y10\":7.2361,\"y30\":7.4861},{\"time\":\"1994-09-30T00:00:00.000Z\",\"m3\":4.7514,\"m6\":5.24,\"y1\":5.7629,\"y2\":6.3924,\"y3\":6.6929,\"y5\":7.0833,\"y7\":7.2833,\"y10\":7.4571,\"y30\":7.7124},{\"time\":\"1994-10-31T00:00:00.000Z\",\"m3\":5.102,\"m6\":5.621,\"y1\":6.1145,\"y2\":6.7285,\"y3\":7.0415,\"y5\":7.403,\"y7\":7.5815,\"y10\":7.744,\"y30\":7.935},{\"time\":\"1994-11-30T00:00:00.000Z\",\"m3\":5.449,\"m6\":5.975,\"y1\":6.54,\"y2\":7.1485,\"y3\":7.44,\"y5\":7.7175,\"y7\":7.83,\"y10\":7.955,\"y30\":8.0815},{\"time\":\"1994-12-31T00:00:00.000Z\",\"m3\":5.7648,\"m6\":6.5048,\"y1\":7.1362,\"y2\":7.5895,\"y3\":7.7095,\"y5\":7.7762,\"y7\":7.8014,\"y10\":7.8138,\"y30\":7.8714},{\"time\":\"1995-01-31T00:00:00.000Z\",\"m3\":5.9015,\"m6\":6.506,\"y1\":7.0505,\"y2\":7.507,\"y3\":7.6625,\"y5\":7.756,\"y7\":7.7915,\"y10\":7.7795,\"y30\":7.8465},{\"time\":\"1995-02-28T00:00:00.000Z\",\"m3\":5.9395,\"m6\":6.3084,\"y1\":6.6968,\"y2\":7.1116,\"y3\":7.2474,\"y5\":7.3658,\"y7\":7.4405,\"y10\":7.4695,\"y30\":7.6116},{\"time\":\"1995-03-31T00:00:00.000Z\",\"m3\":5.9104,\"m6\":6.1726,\"y1\":6.4317,\"y2\":6.7757,\"y3\":6.8922,\"y5\":7.0474,\"y7\":7.1426,\"y10\":7.2048,\"y30\":7.4478},{\"time\":\"1995-04-30T00:00:00.000Z\",\"m3\":5.8379,\"m6\":6.0463,\"y1\":6.2658,\"y2\":6.5679,\"y3\":6.6842,\"y5\":6.86,\"y7\":6.9511,\"y10\":7.0626,\"y30\":7.3605},{\"time\":\"1995-05-31T00:00:00.000Z\",\"m3\":5.8477,\"m6\":5.9305,\"y1\":5.9977,\"y2\":6.1709,\"y3\":6.2686,\"y5\":6.4145,\"y7\":6.5032,\"y10\":6.6327,\"y30\":6.95},{\"time\":\"1995-06-30T00:00:00.000Z\",\"m3\":5.6391,\"m6\":5.6618,\"y1\":5.6423,\"y2\":5.7159,\"y3\":5.7959,\"y5\":5.9286,\"y7\":6.0473,\"y10\":6.1682,\"y30\":6.5732},{\"time\":\"1995-07-31T00:00:00.000Z\",\"m3\":5.593,\"m6\":5.6185,\"y1\":5.594,\"y2\":5.78,\"y3\":5.8855,\"y5\":6.0085,\"y7\":6.199,\"y10\":6.278,\"y30\":6.721},{\"time\":\"1995-08-31T00:00:00.000Z\",\"m3\":5.5657,\"m6\":5.6504,\"y1\":5.7526,\"y2\":5.9809,\"y3\":6.1026,\"y5\":6.2439,\"y7\":6.4117,\"y10\":6.4883,\"y30\":6.8604},{\"time\":\"1995-09-30T00:00:00.000Z\",\"m3\":5.434,\"m6\":5.537,\"y1\":5.62,\"y2\":5.806,\"y3\":5.891,\"y5\":6.0015,\"y7\":6.1315,\"y10\":6.1975,\"y30\":6.549},{\"time\":\"1995-10-31T00:00:00.000Z\",\"m3\":5.4443,\"m6\":5.5648,\"y1\":5.5905,\"y2\":5.6971,\"y3\":5.7719,\"y5\":5.8629,\"y7\":5.9671,\"y10\":6.0448,\"y30\":6.3748},{\"time\":\"1995-11-30T00:00:00.000Z\",\"m3\":5.5181,\"m6\":5.5057,\"y1\":5.4329,\"y2\":5.4786,\"y3\":5.5695,\"y5\":5.6943,\"y7\":5.83,\"y10\":5.9305,\"y30\":6.2629},{\"time\":\"1995-12-31T00:00:00.000Z\",\"m3\":5.294,\"m6\":5.349,\"y1\":5.307,\"y2\":5.3245,\"y3\":5.3865,\"y5\":5.5135,\"y7\":5.6315,\"y10\":5.7115,\"y30\":6.0625},{\"time\":\"1996-01-31T00:00:00.000Z\",\"m3\":5.1505,\"m6\":5.1329,\"y1\":5.0876,\"y2\":5.1124,\"y3\":5.2048,\"y5\":5.3624,\"y7\":5.539,\"y10\":5.6524,\"y30\":6.0519},{\"time\":\"1996-02-29T00:00:00.000Z\",\"m3\":4.9645,\"m6\":4.9665,\"y1\":4.942,\"y2\":5.0255,\"y3\":5.143,\"y5\":5.3795,\"y7\":5.638,\"y10\":5.8055,\"y30\":6.242},{\"time\":\"1996-03-31T00:00:00.000Z\",\"m3\":5.0976,\"m6\":5.1586,\"y1\":5.3424,\"y2\":5.661,\"y3\":5.7857,\"y5\":5.9662,\"y7\":6.1895,\"y10\":6.2686,\"y30\":6.6043},{\"time\":\"1996-04-30T00:00:00.000Z\",\"m3\":5.0877,\"m6\":5.2677,\"y1\":5.5377,\"y2\":5.9573,\"y3\":6.1077,\"y5\":6.3009,\"y7\":6.48,\"y10\":6.5114,\"y30\":6.7941},{\"time\":\"1996-05-31T00:00:00.000Z\",\"m3\":5.1536,\"m6\":5.3309,\"y1\":5.6368,\"y2\":6.095,\"y3\":6.2714,\"y5\":6.4832,\"y7\":6.6568,\"y10\":6.7368,\"y30\":6.9277},{\"time\":\"1996-06-30T00:00:00.000Z\",\"m3\":5.2315,\"m6\":5.462,\"y1\":5.81,\"y2\":6.297,\"y3\":6.485,\"y5\":6.6895,\"y7\":6.834,\"y10\":6.912,\"y30\":7.059},{\"time\":\"1996-07-31T00:00:00.000Z\",\"m3\":5.2982,\"m6\":5.5241,\"y1\":5.8509,\"y2\":6.27,\"y3\":6.4536,\"y5\":6.6359,\"y7\":6.7582,\"y10\":6.8655,\"y30\":7.0332},{\"time\":\"1996-08-31T00:00:00.000Z\",\"m3\":5.1891,\"m6\":5.3414,\"y1\":5.6668,\"y2\":6.0291,\"y3\":6.2064,\"y5\":6.3891,\"y7\":6.515,\"y10\":6.6355,\"y30\":6.8418},{\"time\":\"1996-09-30T00:00:00.000Z\",\"m3\":5.2375,\"m6\":5.4535,\"y1\":5.833,\"y2\":6.234,\"y3\":6.4075,\"y5\":6.597,\"y7\":6.73,\"y10\":6.832,\"y30\":7.026},{\"time\":\"1996-10-31T00:00:00.000Z\",\"m3\":5.1232,\"m6\":5.3164,\"y1\":5.55,\"y2\":5.9127,\"y3\":6.0786,\"y5\":6.2705,\"y7\":6.42,\"y10\":6.5336,\"y30\":6.8086},{\"time\":\"1996-11-30T00:00:00.000Z\",\"m3\":5.1695,\"m6\":5.2737,\"y1\":5.4232,\"y2\":5.7016,\"y3\":5.8184,\"y5\":5.9732,\"y7\":6.0974,\"y10\":6.2037,\"y30\":6.4816},{\"time\":\"1996-12-31T00:00:00.000Z\",\"m3\":5.0405,\"m6\":5.2433,\"y1\":5.4719,\"y2\":5.7819,\"y3\":5.9067,\"y5\":6.0681,\"y7\":6.199,\"y10\":6.3024,\"y30\":6.5524},{\"time\":\"1997-01-31T00:00:00.000Z\",\"m3\":5.1657,\"m6\":5.3076,\"y1\":5.6124,\"y2\":6.0081,\"y3\":6.1557,\"y5\":6.3333,\"y7\":6.4714,\"y10\":6.579,\"y30\":6.8267},{\"time\":\"1997-02-28T00:00:00.000Z\",\"m3\":5.1421,\"m6\":5.2653,\"y1\":5.5253,\"y2\":5.8984,\"y3\":6.0326,\"y5\":6.1989,\"y7\":6.3189,\"y10\":6.4195,\"y30\":6.6879},{\"time\":\"1997-03-31T00:00:00.000Z\",\"m3\":5.2845,\"m6\":5.479,\"y1\":5.7955,\"y2\":6.222,\"y3\":6.3785,\"y5\":6.537,\"y7\":6.6535,\"y10\":6.6945,\"y30\":6.9325},{\"time\":\"1997-04-30T00:00:00.000Z\",\"m3\":5.3045,\"m6\":5.595,\"y1\":5.9882,\"y2\":6.4482,\"y3\":6.605,\"y5\":6.7591,\"y7\":6.86,\"y10\":6.8855,\"y30\":7.0927},{\"time\":\"1997-05-31T00:00:00.000Z\",\"m3\":5.1967,\"m6\":5.5276,\"y1\":5.8695,\"y2\":6.2767,\"y3\":6.4214,\"y5\":6.5676,\"y7\":6.6586,\"y10\":6.711,\"y30\":6.9357},{\"time\":\"1997-06-30T00:00:00.000Z\",\"m3\":5.071,\"m6\":5.3386,\"y1\":5.6919,\"y2\":6.0948,\"y3\":6.2371,\"y5\":6.3752,\"y7\":6.4581,\"y10\":6.4938,\"y30\":6.7724},{\"time\":\"1997-07-31T00:00:00.000Z\",\"m3\":5.1945,\"m6\":5.3295,\"y1\":5.5418,\"y2\":5.8909,\"y3\":6.0014,\"y5\":6.1209,\"y7\":6.2018,\"y10\":6.2205,\"y30\":6.51},{\"time\":\"1997-08-31T00:00:00.000Z\",\"m3\":5.2795,\"m6\":5.4014,\"y1\":5.5648,\"y2\":5.939,\"y3\":6.0595,\"y5\":6.16,\"y7\":6.2905,\"y10\":6.2986,\"y30\":6.5786},{\"time\":\"1997-09-30T00:00:00.000Z\",\"m3\":5.0848,\"m6\":5.2957,\"y1\":5.5238,\"y2\":5.8819,\"y3\":5.9757,\"y5\":6.1062,\"y7\":6.201,\"y10\":6.2086,\"y30\":6.4952},{\"time\":\"1997-10-31T00:00:00.000Z\",\"m3\":5.1127,\"m6\":5.2991,\"y1\":5.4564,\"y2\":5.7718,\"y3\":5.8391,\"y5\":5.9282,\"y7\":6.0541,\"y10\":6.0295,\"y30\":6.3264},{\"time\":\"1997-11-30T00:00:00.000Z\",\"m3\":5.2811,\"m6\":5.3767,\"y1\":5.4572,\"y2\":5.7128,\"y3\":5.755,\"y5\":5.8039,\"y7\":5.9017,\"y10\":5.875,\"y30\":6.1094},{\"time\":\"1997-12-31T00:00:00.000Z\",\"m3\":5.3045,\"m6\":5.4514,\"y1\":5.525,\"y2\":5.715,\"y3\":5.7418,\"y5\":5.7732,\"y7\":5.825,\"y10\":5.8086,\"y30\":5.99},{\"time\":\"1998-01-31T00:00:00.000Z\",\"m3\":5.179,\"m6\":5.2305,\"y1\":5.2445,\"y2\":5.357,\"y3\":5.375,\"y5\":5.416,\"y7\":5.5305,\"y10\":5.5445,\"y30\":5.811},{\"time\":\"1998-02-28T00:00:00.000Z\",\"m3\":5.23,\"m6\":5.2742,\"y1\":5.3084,\"y2\":5.4179,\"y3\":5.4342,\"y5\":5.4926,\"y7\":5.6032,\"y10\":5.5747,\"y30\":5.8911},{\"time\":\"1998-03-31T00:00:00.000Z\",\"m3\":5.1618,\"m6\":5.2482,\"y1\":5.3895,\"y2\":5.5614,\"y3\":5.5668,\"y5\":5.6118,\"y7\":5.7091,\"y10\":5.6473,\"y30\":5.9482},{\"time\":\"1998-04-30T00:00:00.000Z\",\"m3\":5.0829,\"m6\":5.2629,\"y1\":5.379,\"y2\":5.5643,\"y3\":5.581,\"y5\":5.6119,\"y7\":5.6957,\"y10\":5.6376,\"y30\":5.9238},{\"time\":\"1998-05-31T00:00:00.000Z\",\"m3\":5.14,\"m6\":5.3565,\"y1\":5.44,\"y2\":5.5935,\"y3\":5.6075,\"y5\":5.628,\"y7\":5.7185,\"y10\":5.6525,\"y30\":5.9265},{\"time\":\"1998-06-30T00:00:00.000Z\",\"m3\":5.1241,\"m6\":5.3236,\"y1\":5.4086,\"y2\":5.52,\"y3\":5.5186,\"y5\":5.5241,\"y7\":5.5632,\"y10\":5.4964,\"y30\":5.7036},{\"time\":\"1998-07-31T00:00:00.000Z\",\"m3\":5.0945,\"m6\":5.2277,\"y1\":5.3582,\"y2\":5.4582,\"y3\":5.4664,\"y5\":5.4614,\"y7\":5.5209,\"y10\":5.4614,\"y30\":5.6768},{\"time\":\"1998-08-31T00:00:00.000Z\",\"m3\":5.041,\"m6\":5.1519,\"y1\":5.2052,\"y2\":5.2686,\"y3\":5.239,\"y5\":5.2748,\"y7\":5.3586,\"y10\":5.3419,\"y30\":5.5405},{\"time\":\"1998-09-30T00:00:00.000Z\",\"m3\":4.739,\"m6\":4.8105,\"y1\":4.711,\"y2\":4.6662,\"y3\":4.6157,\"y5\":4.6238,\"y7\":4.7619,\"y10\":4.8067,\"y30\":5.2048},{\"time\":\"1998-10-31T00:00:00.000Z\",\"m3\":4.0705,\"m6\":4.1962,\"y1\":4.1214,\"y2\":4.0919,\"y3\":4.179,\"y5\":4.1843,\"y7\":4.4576,\"y10\":4.53,\"y30\":5.0105},{\"time\":\"1998-11-30T00:00:00.000Z\",\"m3\":4.5311,\"m6\":4.5868,\"y1\":4.5253,\"y2\":4.54,\"y3\":4.5721,\"y5\":4.5374,\"y7\":4.7758,\"y10\":4.8274,\"y30\":5.2484},{\"time\":\"1998-12-31T00:00:00.000Z\",\"m3\":4.4968,\"m6\":4.5682,\"y1\":4.5186,\"y2\":4.5064,\"y3\":4.4845,\"y5\":4.45,\"y7\":4.6491,\"y10\":4.645,\"y30\":5.0591},{\"time\":\"1999-01-31T00:00:00.000Z\",\"m3\":4.4463,\"m6\":4.4884,\"y1\":4.5142,\"y2\":4.6153,\"y3\":4.6116,\"y5\":4.6005,\"y7\":4.7953,\"y10\":4.7221,\"y30\":5.1579},{\"time\":\"1999-02-28T00:00:00.000Z\",\"m3\":4.5553,\"m6\":4.6074,\"y1\":4.7026,\"y2\":4.8763,\"y3\":4.9005,\"y5\":4.9147,\"y7\":5.0984,\"y10\":4.9989,\"y30\":5.3653},{\"time\":\"1999-03-31T00:00:00.000Z\",\"m3\":4.5665,\"m6\":4.6465,\"y1\":4.7813,\"y2\":5.053,\"y3\":5.1057,\"y5\":5.1404,\"y7\":5.36,\"y10\":5.2326,\"y30\":5.5804},{\"time\":\"1999-04-30T00:00:00.000Z\",\"m3\":4.4091,\"m6\":4.5445,\"y1\":4.69,\"y2\":4.9768,\"y3\":5.0323,\"y5\":5.0795,\"y7\":5.28,\"y10\":5.1845,\"y30\":5.5477},{\"time\":\"1999-05-31T00:00:00.000Z\",\"m3\":4.631,\"m6\":4.746,\"y1\":4.849,\"y2\":5.2545,\"y3\":5.333,\"y5\":5.437,\"y7\":5.6445,\"y10\":5.5395,\"y30\":5.8055},{\"time\":\"1999-06-30T00:00:00.000Z\",\"m3\":4.7155,\"m6\":5.0295,\"y1\":5.0968,\"y2\":5.6186,\"y3\":5.6973,\"y5\":5.8091,\"y7\":6.0477,\"y10\":5.8995,\"y30\":6.0418},{\"time\":\"1999-07-31T00:00:00.000Z\",\"m3\":4.6857,\"m6\":4.7538,\"y1\":5.0319,\"y2\":5.5548,\"y3\":5.6181,\"y5\":5.6786,\"y7\":5.9443,\"y10\":5.7919,\"y30\":5.9833},{\"time\":\"1999-08-31T00:00:00.000Z\",\"m3\":4.8736,\"m6\":5.0882,\"y1\":5.1977,\"y2\":5.6782,\"y3\":5.7664,\"y5\":5.8414,\"y7\":6.1509,\"y10\":5.9391,\"y30\":6.0686},{\"time\":\"1999-09-30T00:00:00.000Z\",\"m3\":4.8224,\"m6\":5.0843,\"y1\":5.2514,\"y2\":5.6619,\"y3\":5.7457,\"y5\":5.8019,\"y7\":6.119,\"y10\":5.9152,\"y30\":6.0714},{\"time\":\"1999-10-31T00:00:00.000Z\",\"m3\":5.0185,\"m6\":5.2005,\"y1\":5.429,\"y2\":5.8625,\"y3\":5.9425,\"y5\":6.0335,\"y7\":6.3295,\"y10\":6.112,\"y30\":6.2635},{\"time\":\"1999-11-30T00:00:00.000Z\",\"m3\":5.2275,\"m6\":5.428,\"y1\":5.5535,\"y2\":5.862,\"y3\":5.9185,\"y5\":5.969,\"y7\":6.173,\"y10\":6.034,\"y30\":6.145},{\"time\":\"1999-12-31T00:00:00.000Z\",\"m3\":5.3568,\"m6\":5.6841,\"y1\":5.8423,\"y2\":6.1045,\"y3\":6.1427,\"y5\":6.1864,\"y7\":6.3768,\"y10\":6.2755,\"y30\":6.3514},{\"time\":\"2000-01-31T00:00:00.000Z\",\"m3\":5.499,\"m6\":5.7595,\"y1\":6.1215,\"y2\":6.44,\"y3\":6.489,\"y5\":6.5795,\"y7\":6.7025,\"y10\":6.661,\"y30\":6.6255},{\"time\":\"2000-02-29T00:00:00.000Z\",\"m3\":5.727,\"m6\":5.9955,\"y1\":6.218,\"y2\":6.6105,\"y3\":6.6525,\"y5\":6.678,\"y7\":6.7165,\"y10\":6.5195,\"y30\":6.232},{\"time\":\"2000-03-31T00:00:00.000Z\",\"m3\":5.8639,\"m6\":6.1135,\"y1\":6.2222,\"y2\":6.5283,\"y3\":6.5283,\"y5\":6.5039,\"y7\":6.5065,\"y10\":6.2565,\"y30\":6.0535},{\"time\":\"2000-04-30T00:00:00.000Z\",\"m3\":5.8216,\"m6\":6.0695,\"y1\":6.1505,\"y2\":6.4037,\"y3\":6.3563,\"y5\":6.2626,\"y7\":6.2658,\"y10\":5.9905,\"y30\":5.8463},{\"time\":\"2000-05-31T00:00:00.000Z\",\"m3\":5.9945,\"m6\":6.3891,\"y1\":6.3264,\"y2\":6.8095,\"y3\":6.7677,\"y5\":6.6877,\"y7\":6.6895,\"y10\":6.4405,\"y30\":6.1486},{\"time\":\"2000-06-30T00:00:00.000Z\",\"m3\":5.8618,\"m6\":6.2386,\"y1\":6.1727,\"y2\":6.4818,\"y3\":6.4264,\"y5\":6.3009,\"y7\":6.3268,\"y10\":6.0973,\"y30\":5.9264},{\"time\":\"2000-07-31T00:00:00.000Z\",\"m3\":6.1425,\"m6\":6.2735,\"y1\":6.0825,\"y2\":6.339,\"y3\":6.277,\"y5\":6.179,\"y7\":6.2235,\"y10\":6.054,\"y30\":5.851},{\"time\":\"2000-08-31T00:00:00.000Z\",\"m3\":6.2774,\"m6\":6.3548,\"y1\":6.183,\"y2\":6.2287,\"y3\":6.1743,\"y5\":6.0609,\"y7\":6.0504,\"y10\":5.8261,\"y30\":5.7161},{\"time\":\"2000-09-30T00:00:00.000Z\",\"m3\":6.175,\"m6\":6.2495,\"y1\":6.1255,\"y2\":6.0815,\"y3\":6.0165,\"y5\":5.9345,\"y7\":5.9805,\"y10\":5.799,\"y30\":5.8265},{\"time\":\"2000-10-31T00:00:00.000Z\",\"m3\":6.2948,\"m6\":6.3157,\"y1\":6.0124,\"y2\":5.9124,\"y3\":5.8457,\"y5\":5.7829,\"y7\":5.8371,\"y10\":5.7386,\"y30\":5.8033},{\"time\":\"2000-11-30T00:00:00.000Z\",\"m3\":6.3562,\"m6\":6.339,\"y1\":6.0919,\"y2\":5.8752,\"y3\":5.7867,\"y5\":5.6976,\"y7\":5.7814,\"y10\":5.7171,\"y30\":5.7757},{\"time\":\"2000-12-31T00:00:00.000Z\",\"m3\":5.937,\"m6\":5.923,\"y1\":5.6025,\"y2\":5.352,\"y3\":5.2595,\"y5\":5.168,\"y7\":5.283,\"y10\":5.2405,\"y30\":5.4905},{\"time\":\"2001-01-31T00:00:00.000Z\",\"m3\":5.2852,\"m6\":5.1462,\"y1\":4.8148,\"y2\":4.76,\"y3\":4.7743,\"y5\":4.8586,\"y7\":5.1305,\"y10\":5.161,\"y30\":5.541},{\"time\":\"2001-02-28T00:00:00.000Z\",\"m3\":5.0053,\"m6\":4.8889,\"y1\":4.6842,\"y2\":4.6568,\"y3\":4.7079,\"y5\":4.8863,\"y7\":5.0974,\"y10\":5.0989,\"y30\":5.4547},{\"time\":\"2001-03-31T00:00:00.000Z\",\"m3\":4.5355,\"m6\":4.4355,\"y1\":4.2986,\"y2\":4.3423,\"y3\":4.4282,\"y5\":4.6427,\"y7\":4.8827,\"y10\":4.8855,\"y30\":5.3395},{\"time\":\"2001-04-30T00:00:00.000Z\",\"m3\":3.966,\"m6\":3.9855,\"y1\":3.9765,\"y2\":4.234,\"y3\":4.423,\"y5\":4.7635,\"y7\":5.0345,\"y10\":5.141,\"y30\":5.646},{\"time\":\"2001-05-31T00:00:00.000Z\",\"m3\":3.7032,\"m6\":3.7382,\"y1\":3.7814,\"y2\":4.26,\"y3\":4.5073,\"y5\":4.9277,\"y7\":5.24,\"y10\":5.3914,\"y30\":5.78},{\"time\":\"2001-06-30T00:00:00.000Z\",\"m3\":3.5652,\"m6\":3.559,\"y1\":3.5762,\"y2\":4.08,\"y3\":4.3481,\"y5\":4.8071,\"y7\":5.1381,\"y10\":5.2843,\"y30\":5.6695},{\"time\":\"2001-07-31T00:00:00.000Z\",\"m3\":3.5933,\"m6\":3.5567,\"y1\":3.6171,\"y2\":4.0386,\"y3\":4.3133,\"y5\":4.7619,\"y7\":5.061,\"y10\":5.2362,\"y30\":5.6133},{\"time\":\"2001-08-31T00:00:00.000Z\",\"m3\":3.4378,\"m6\":3.3865,\"y1\":3.4704,\"y2\":3.7574,\"y3\":4.0378,\"y5\":4.5739,\"y7\":4.8435,\"y10\":4.9713,\"y30\":5.4835},{\"time\":\"2001-09-30T00:00:00.000Z\",\"m3\":2.6924,\"m6\":2.7059,\"y1\":2.8247,\"y2\":3.1188,\"y3\":3.4506,\"y5\":4.1153,\"y7\":4.5124,\"y10\":4.7318,\"y30\":5.4829},{\"time\":\"2001-10-31T00:00:00.000Z\",\"m3\":2.1977,\"m6\":2.1709,\"y1\":2.3305,\"y2\":2.7259,\"y3\":3.1368,\"y5\":3.91,\"y7\":4.3073,\"y10\":4.5668,\"y30\":5.3155},{\"time\":\"2001-11-30T00:00:00.000Z\",\"m3\":1.9065,\"m6\":1.924,\"y1\":2.182,\"y2\":2.7825,\"y3\":3.2235,\"y5\":3.9725,\"y7\":4.422,\"y10\":4.6515,\"y30\":5.118},{\"time\":\"2001-12-31T00:00:00.000Z\",\"m3\":1.72,\"m6\":1.8155,\"y1\":2.2155,\"y2\":3.107,\"y3\":3.6245,\"y5\":4.3895,\"y7\":4.859,\"y10\":5.0875,\"y30\":5.48},{\"time\":\"2002-01-31T00:00:00.000Z\",\"m3\":1.6848,\"m6\":1.771,\"y1\":2.1586,\"y2\":3.0281,\"y3\":3.5567,\"y5\":4.34,\"y7\":4.7895,\"y10\":5.0357,\"y30\":5.4452},{\"time\":\"2002-02-28T00:00:00.000Z\",\"m3\":1.7568,\"m6\":1.8637,\"y1\":2.2326,\"y2\":3.0153,\"y3\":3.5463,\"y5\":4.2984,\"y7\":4.7147,\"y10\":4.9116,\"y30\":5.4009},{\"time\":\"2002-03-31T00:00:00.000Z\",\"m3\":1.825,\"m6\":2.0565,\"y1\":2.567,\"y2\":3.5575,\"y3\":4.142,\"y5\":4.738,\"y7\":5.1365,\"y10\":5.284,\"y30\":null},{\"time\":\"2002-04-30T00:00:00.000Z\",\"m3\":1.745,\"m6\":1.975,\"y1\":2.4759,\"y2\":3.4227,\"y3\":4.0073,\"y5\":4.6468,\"y7\":5.0164,\"y10\":5.2109,\"y30\":null},{\"time\":\"2002-05-31T00:00:00.000Z\",\"m3\":1.7605,\"m6\":1.9114,\"y1\":2.3541,\"y2\":3.2641,\"y3\":3.8018,\"y5\":4.4945,\"y7\":4.9005,\"y10\":5.1645,\"y30\":null},{\"time\":\"2002-06-30T00:00:00.000Z\",\"m3\":1.732,\"m6\":1.8325,\"y1\":2.1965,\"y2\":2.993,\"y3\":3.485,\"y5\":4.186,\"y7\":4.601,\"y10\":4.9265,\"y30\":null},{\"time\":\"2002-07-31T00:00:00.000Z\",\"m3\":1.7136,\"m6\":1.7364,\"y1\":1.9609,\"y2\":2.5568,\"y3\":3.0109,\"y5\":3.8073,\"y7\":4.3041,\"y10\":4.6532,\"y30\":null},{\"time\":\"2002-08-31T00:00:00.000Z\",\"m3\":1.6473,\"m6\":1.6373,\"y1\":1.7573,\"y2\":2.1336,\"y3\":2.5223,\"y5\":3.2945,\"y7\":3.875,\"y10\":4.2573,\"y30\":null},{\"time\":\"2002-09-30T00:00:00.000Z\",\"m3\":1.659,\"m6\":1.6385,\"y1\":1.715,\"y2\":2.0045,\"y3\":2.317,\"y5\":2.938,\"y7\":3.496,\"y10\":3.87,\"y30\":null},{\"time\":\"2002-10-31T00:00:00.000Z\",\"m3\":1.61,\"m6\":1.5895,\"y1\":1.65,\"y2\":1.9118,\"y3\":2.2536,\"y5\":2.945,\"y7\":3.5368,\"y10\":3.9409,\"y30\":null},{\"time\":\"2002-11-30T00:00:00.000Z\",\"m3\":1.2547,\"m6\":1.2979,\"y1\":1.4916,\"y2\":1.9232,\"y3\":2.3153,\"y5\":3.0547,\"y7\":3.6442,\"y10\":4.0484,\"y30\":null},{\"time\":\"2002-12-31T00:00:00.000Z\",\"m3\":1.211,\"m6\":1.2676,\"y1\":1.45,\"y2\":1.8362,\"y3\":2.2348,\"y5\":3.0333,\"y7\":3.6267,\"y10\":4.0324,\"y30\":null},{\"time\":\"2003-01-31T00:00:00.000Z\",\"m3\":1.189,\"m6\":1.2219,\"y1\":1.3643,\"y2\":1.7433,\"y3\":2.1824,\"y5\":3.0524,\"y7\":3.6019,\"y10\":4.0486,\"y30\":null},{\"time\":\"2003-02-28T00:00:00.000Z\",\"m3\":1.1853,\"m6\":1.1953,\"y1\":1.2963,\"y2\":1.6279,\"y3\":2.0505,\"y5\":2.8979,\"y7\":3.4479,\"y10\":3.9026,\"y30\":null},{\"time\":\"2003-03-31T00:00:00.000Z\",\"m3\":1.151,\"m6\":1.1567,\"y1\":1.24,\"y2\":1.5738,\"y3\":1.9752,\"y5\":2.7838,\"y7\":3.3448,\"y10\":3.8071,\"y30\":null},{\"time\":\"2003-04-30T00:00:00.000Z\",\"m3\":1.1524,\"m6\":1.169,\"y1\":1.2671,\"y2\":1.6224,\"y3\":2.059,\"y5\":2.9286,\"y7\":3.4743,\"y10\":3.9586,\"y30\":null},{\"time\":\"2003-05-31T00:00:00.000Z\",\"m3\":1.0881,\"m6\":1.1062,\"y1\":1.1814,\"y2\":1.4152,\"y3\":1.7476,\"y5\":2.5157,\"y7\":3.0714,\"y10\":3.569,\"y30\":null},{\"time\":\"2003-06-30T00:00:00.000Z\",\"m3\":0.9362,\"m6\":0.9433,\"y1\":1.0095,\"y2\":1.2271,\"y3\":1.5143,\"y5\":2.2657,\"y7\":2.8395,\"y10\":3.3343,\"y30\":null},{\"time\":\"2003-07-31T00:00:00.000Z\",\"m3\":0.9205,\"m6\":0.9727,\"y1\":1.1164,\"y2\":1.4741,\"y3\":1.9327,\"y5\":2.8723,\"y7\":3.4532,\"y10\":3.9755,\"y30\":null},{\"time\":\"2003-08-31T00:00:00.000Z\",\"m3\":0.969,\"m6\":1.0524,\"y1\":1.3086,\"y2\":1.8643,\"y3\":2.4352,\"y5\":3.37,\"y7\":3.9624,\"y10\":4.4452,\"y30\":null},{\"time\":\"2003-09-30T00:00:00.000Z\",\"m3\":0.9552,\"m6\":1.029,\"y1\":1.2376,\"y2\":1.7062,\"y3\":2.2333,\"y5\":3.1848,\"y7\":3.7443,\"y10\":4.2743,\"y30\":null},{\"time\":\"2003-10-31T00:00:00.000Z\",\"m3\":0.9418,\"m6\":1.0195,\"y1\":1.2541,\"y2\":1.7477,\"y3\":2.2636,\"y5\":3.1859,\"y7\":3.7514,\"y10\":4.2905,\"y30\":null},{\"time\":\"2003-11-30T00:00:00.000Z\",\"m3\":0.9522,\"m6\":1.0422,\"y1\":1.3367,\"y2\":1.9267,\"y3\":2.4506,\"y5\":3.2872,\"y7\":3.8083,\"y10\":4.3,\"y30\":null},{\"time\":\"2003-12-31T00:00:00.000Z\",\"m3\":0.9145,\"m6\":1.0109,\"y1\":1.3059,\"y2\":1.9073,\"y3\":2.44,\"y5\":3.2686,\"y7\":3.7905,\"y10\":4.2677,\"y30\":null},{\"time\":\"2004-01-31T00:00:00.000Z\",\"m3\":0.9005,\"m6\":0.99,\"y1\":1.2395,\"y2\":1.761,\"y3\":2.271,\"y5\":3.1215,\"y7\":3.6465,\"y10\":4.1505,\"y30\":null},{\"time\":\"2004-02-29T00:00:00.000Z\",\"m3\":0.9447,\"m6\":1.0111,\"y1\":1.2437,\"y2\":1.74,\"y3\":2.2468,\"y5\":3.0679,\"y7\":3.5853,\"y10\":4.0842,\"y30\":null},{\"time\":\"2004-03-31T00:00:00.000Z\",\"m3\":0.9535,\"m6\":1.0091,\"y1\":1.1874,\"y2\":1.5778,\"y3\":2,\"y5\":2.787,\"y7\":3.3061,\"y10\":3.8265,\"y30\":null},{\"time\":\"2004-04-30T00:00:00.000Z\",\"m3\":0.9581,\"m6\":1.109,\"y1\":1.4338,\"y2\":2.071,\"y3\":2.5681,\"y5\":3.3895,\"y7\":3.8924,\"y10\":4.3476,\"y30\":null},{\"time\":\"2004-05-31T00:00:00.000Z\",\"m3\":1.0395,\"m6\":1.3335,\"y1\":1.7775,\"y2\":2.5335,\"y3\":3.0975,\"y5\":3.8505,\"y7\":4.313,\"y10\":4.7155,\"y30\":null},{\"time\":\"2004-06-30T00:00:00.000Z\",\"m3\":1.2881,\"m6\":1.6352,\"y1\":2.1152,\"y2\":2.7633,\"y3\":3.2562,\"y5\":3.929,\"y7\":4.3524,\"y10\":4.7338,\"y30\":null},{\"time\":\"2004-07-31T00:00:00.000Z\",\"m3\":1.3576,\"m6\":1.6962,\"y1\":2.0952,\"y2\":2.6386,\"y3\":3.0462,\"y5\":3.6886,\"y7\":4.1119,\"y10\":4.4981,\"y30\":null},{\"time\":\"2004-08-31T00:00:00.000Z\",\"m3\":1.5032,\"m6\":1.76,\"y1\":2.0159,\"y2\":2.5082,\"y3\":2.8845,\"y5\":3.4745,\"y7\":3.9041,\"y10\":4.2814,\"y30\":null},{\"time\":\"2004-09-30T00:00:00.000Z\",\"m3\":1.6814,\"m6\":1.9105,\"y1\":2.1167,\"y2\":2.5252,\"y3\":2.8295,\"y5\":3.3552,\"y7\":3.7514,\"y10\":4.1257,\"y30\":null},{\"time\":\"2004-10-31T00:00:00.000Z\",\"m3\":1.7945,\"m6\":2.0515,\"y1\":2.228,\"y2\":2.5845,\"y3\":2.8535,\"y5\":3.3475,\"y7\":3.748,\"y10\":4.097,\"y30\":null},{\"time\":\"2004-11-30T00:00:00.000Z\",\"m3\":2.1075,\"m6\":2.3245,\"y1\":2.5,\"y2\":2.852,\"y3\":3.0935,\"y5\":3.525,\"y7\":3.882,\"y10\":4.194,\"y30\":null},{\"time\":\"2004-12-31T00:00:00.000Z\",\"m3\":2.2227,\"m6\":2.4968,\"y1\":2.6705,\"y2\":3.0118,\"y3\":3.2141,\"y5\":3.5982,\"y7\":3.9277,\"y10\":4.2309,\"y30\":null},{\"time\":\"2005-01-31T00:00:00.000Z\",\"m3\":2.371,\"m6\":2.6755,\"y1\":2.8605,\"y2\":3.2225,\"y3\":3.39,\"y5\":3.707,\"y7\":3.9735,\"y10\":4.2215,\"y30\":null},{\"time\":\"2005-02-28T00:00:00.000Z\",\"m3\":2.5795,\"m6\":2.8458,\"y1\":3.0295,\"y2\":3.3847,\"y3\":3.5353,\"y5\":3.7663,\"y7\":3.9658,\"y10\":4.1653,\"y30\":null},{\"time\":\"2005-03-31T00:00:00.000Z\",\"m3\":2.7959,\"m6\":3.0868,\"y1\":3.3023,\"y2\":3.7268,\"y3\":3.9132,\"y5\":4.1655,\"y7\":4.3277,\"y10\":4.4977,\"y30\":null},{\"time\":\"2005-04-30T00:00:00.000Z\",\"m3\":2.8367,\"m6\":3.1443,\"y1\":3.3167,\"y2\":3.6538,\"y3\":3.7895,\"y5\":3.9986,\"y7\":4.1581,\"y10\":4.341,\"y30\":null},{\"time\":\"2005-05-31T00:00:00.000Z\",\"m3\":2.9019,\"m6\":3.171,\"y1\":3.331,\"y2\":3.6438,\"y3\":3.7248,\"y5\":3.8529,\"y7\":3.9414,\"y10\":4.1443,\"y30\":null},{\"time\":\"2005-06-30T00:00:00.000Z\",\"m3\":3.0364,\"m6\":3.2218,\"y1\":3.3632,\"y2\":3.6405,\"y3\":3.685,\"y5\":3.7723,\"y7\":3.8605,\"y10\":3.9982,\"y30\":null},{\"time\":\"2005-07-31T00:00:00.000Z\",\"m3\":3.2875,\"m6\":3.5265,\"y1\":3.641,\"y2\":3.871,\"y3\":3.914,\"y5\":3.979,\"y7\":4.058,\"y10\":4.1775,\"y30\":null},{\"time\":\"2005-08-31T00:00:00.000Z\",\"m3\":3.5183,\"m6\":3.7848,\"y1\":3.8722,\"y2\":4.0443,\"y3\":4.0787,\"y5\":4.1222,\"y7\":4.1778,\"y10\":4.2626,\"y30\":null},{\"time\":\"2005-09-30T00:00:00.000Z\",\"m3\":3.4943,\"m6\":3.7938,\"y1\":3.8452,\"y2\":3.9462,\"y3\":3.9629,\"y5\":4.0062,\"y7\":4.0805,\"y10\":4.199,\"y30\":null},{\"time\":\"2005-10-31T00:00:00.000Z\",\"m3\":3.7925,\"m6\":4.125,\"y1\":4.1755,\"y2\":4.271,\"y3\":4.2905,\"y5\":4.3285,\"y7\":4.382,\"y10\":4.4635,\"y30\":null},{\"time\":\"2005-11-30T00:00:00.000Z\",\"m3\":3.9745,\"m6\":4.295,\"y1\":4.334,\"y2\":4.4165,\"y3\":4.4335,\"y5\":4.4525,\"y7\":4.482,\"y10\":4.535,\"y30\":null},{\"time\":\"2005-12-31T00:00:00.000Z\",\"m3\":3.971,\"m6\":4.3276,\"y1\":4.3529,\"y2\":4.4043,\"y3\":4.3919,\"y5\":4.3924,\"y7\":4.4138,\"y10\":4.4671,\"y30\":null},{\"time\":\"2006-01-31T00:00:00.000Z\",\"m3\":4.336,\"m6\":4.4695,\"y1\":4.445,\"y2\":4.3955,\"y3\":4.3515,\"y5\":4.3455,\"y7\":4.365,\"y10\":4.416,\"y30\":null},{\"time\":\"2006-02-28T00:00:00.000Z\",\"m3\":4.5395,\"m6\":4.6916,\"y1\":4.6847,\"y2\":4.6684,\"y3\":4.6374,\"y5\":4.5721,\"y7\":4.5642,\"y10\":4.5689,\"y30\":4.5369},{\"time\":\"2006-03-31T00:00:00.000Z\",\"m3\":4.6278,\"m6\":4.7922,\"y1\":4.7735,\"y2\":4.7339,\"y3\":4.7383,\"y5\":4.7161,\"y7\":4.7139,\"y10\":4.7239,\"y30\":4.7343},{\"time\":\"2006-04-30T00:00:00.000Z\",\"m3\":4.7216,\"m6\":4.9,\"y1\":4.8974,\"y2\":4.8889,\"y3\":4.8853,\"y5\":4.9021,\"y7\":4.9353,\"y10\":4.9905,\"y30\":5.0626},{\"time\":\"2006-05-31T00:00:00.000Z\",\"m3\":4.8364,\"m6\":5.01,\"y1\":4.995,\"y2\":4.9682,\"y3\":4.9745,\"y5\":4.9977,\"y7\":5.0323,\"y10\":5.11,\"y30\":5.2014},{\"time\":\"2006-06-30T00:00:00.000Z\",\"m3\":4.9177,\"m6\":5.1727,\"y1\":5.155,\"y2\":5.1218,\"y3\":5.0882,\"y5\":5.0673,\"y7\":5.0755,\"y10\":5.1064,\"y30\":5.1541},{\"time\":\"2006-07-31T00:00:00.000Z\",\"m3\":5.0765,\"m6\":5.2665,\"y1\":5.2175,\"y2\":5.1185,\"y3\":5.07,\"y5\":5.04,\"y7\":5.0485,\"y10\":5.0875,\"y30\":5.1345},{\"time\":\"2006-08-31T00:00:00.000Z\",\"m3\":5.0904,\"m6\":5.1726,\"y1\":5.0826,\"y2\":4.9035,\"y3\":4.8461,\"y5\":4.8222,\"y7\":4.8287,\"y10\":4.8765,\"y30\":4.9961},{\"time\":\"2006-09-30T00:00:00.000Z\",\"m3\":4.93,\"m6\":5.0785,\"y1\":4.9745,\"y2\":4.769,\"y3\":4.6925,\"y5\":4.6675,\"y7\":4.676,\"y10\":4.719,\"y30\":4.852},{\"time\":\"2006-10-31T00:00:00.000Z\",\"m3\":5.0462,\"m6\":5.119,\"y1\":5.01,\"y2\":4.7957,\"y3\":4.7219,\"y5\":4.6867,\"y7\":4.69,\"y10\":4.729,\"y30\":4.8548},{\"time\":\"2006-11-30T00:00:00.000Z\",\"m3\":5.0733,\"m6\":5.1471,\"y1\":5.011,\"y2\":4.7405,\"y3\":4.6429,\"y5\":4.5824,\"y7\":4.5819,\"y10\":4.5952,\"y30\":4.6857},{\"time\":\"2006-12-31T00:00:00.000Z\",\"m3\":4.973,\"m6\":5.072,\"y1\":4.9415,\"y2\":4.6735,\"y3\":4.578,\"y5\":4.533,\"y7\":4.5355,\"y10\":4.5645,\"y30\":4.6825},{\"time\":\"2007-01-31T00:00:00.000Z\",\"m3\":5.1052,\"m6\":5.1486,\"y1\":5.0571,\"y2\":4.8762,\"y3\":4.7943,\"y5\":4.7529,\"y7\":4.7524,\"y10\":4.7595,\"y30\":4.8519},{\"time\":\"2007-02-28T00:00:00.000Z\",\"m3\":5.1632,\"m6\":5.1579,\"y1\":5.0537,\"y2\":4.8489,\"y3\":4.7537,\"y5\":4.7105,\"y7\":4.7111,\"y10\":4.7226,\"y30\":4.8216},{\"time\":\"2007-03-31T00:00:00.000Z\",\"m3\":5.08,\"m6\":5.0968,\"y1\":4.9205,\"y2\":4.5736,\"y3\":4.5064,\"y5\":4.4809,\"y7\":4.4986,\"y10\":4.5645,\"y30\":4.7218},{\"time\":\"2007-04-30T00:00:00.000Z\",\"m3\":5.0067,\"m6\":5.0676,\"y1\":4.9324,\"y2\":4.6662,\"y3\":4.6014,\"y5\":4.5933,\"y7\":4.6167,\"y10\":4.6938,\"y30\":4.8662},{\"time\":\"2007-05-31T00:00:00.000Z\",\"m3\":4.8677,\"m6\":4.9786,\"y1\":4.9091,\"y2\":4.7664,\"y3\":4.6936,\"y5\":4.6673,\"y7\":4.6855,\"y10\":4.7464,\"y30\":4.9014},{\"time\":\"2007-06-30T00:00:00.000Z\",\"m3\":4.7419,\"m6\":4.9548,\"y1\":4.9624,\"y2\":4.9805,\"y3\":4.999,\"y5\":5.0262,\"y7\":5.0538,\"y10\":5.1029,\"y30\":5.2033},{\"time\":\"2007-07-31T00:00:00.000Z\",\"m3\":4.961,\"m6\":5.0352,\"y1\":4.9643,\"y2\":4.819,\"y3\":4.8238,\"y5\":4.8848,\"y7\":4.9329,\"y10\":5.0043,\"y30\":5.1081},{\"time\":\"2007-08-31T00:00:00.000Z\",\"m3\":4.317,\"m6\":4.553,\"y1\":4.4722,\"y2\":4.313,\"y3\":4.3387,\"y5\":4.4322,\"y7\":4.527,\"y10\":4.6748,\"y30\":4.9322},{\"time\":\"2007-09-30T00:00:00.000Z\",\"m3\":3.9942,\"m6\":4.2016,\"y1\":4.1368,\"y2\":4.0116,\"y3\":4.0605,\"y5\":4.1989,\"y7\":4.33,\"y10\":4.5216,\"y30\":4.7932},{\"time\":\"2007-10-31T00:00:00.000Z\",\"m3\":4.0027,\"m6\":4.1641,\"y1\":4.0968,\"y2\":3.9677,\"y3\":4.0109,\"y5\":4.1982,\"y7\":4.3318,\"y10\":4.5277,\"y30\":4.7736},{\"time\":\"2007-11-30T00:00:00.000Z\",\"m3\":3.3545,\"m6\":3.581,\"y1\":3.499,\"y2\":3.3365,\"y3\":3.347,\"y5\":3.667,\"y7\":3.866,\"y10\":4.1485,\"y30\":4.52},{\"time\":\"2007-12-31T00:00:00.000Z\",\"m3\":3.0665,\"m6\":3.337,\"y1\":3.263,\"y2\":3.117,\"y3\":3.1325,\"y5\":3.4875,\"y7\":3.743,\"y10\":4.0975,\"y30\":4.527},{\"time\":\"2008-01-31T00:00:00.000Z\",\"m3\":2.82,\"m6\":2.8362,\"y1\":2.7114,\"y2\":2.4757,\"y3\":2.5114,\"y5\":2.9805,\"y7\":3.3071,\"y10\":3.7443,\"y30\":4.3305},{\"time\":\"2008-02-29T00:00:00.000Z\",\"m3\":2.174,\"m6\":2.098,\"y1\":2.0535,\"y2\":1.973,\"y3\":2.188,\"y5\":2.779,\"y7\":3.2095,\"y10\":3.7375,\"y30\":4.517},{\"time\":\"2008-03-31T00:00:00.000Z\",\"m3\":1.2835,\"m6\":1.507,\"y1\":1.544,\"y2\":1.617,\"y3\":1.7965,\"y5\":2.4835,\"y7\":2.932,\"y10\":3.51,\"y30\":4.393},{\"time\":\"2008-04-30T00:00:00.000Z\",\"m3\":1.3118,\"m6\":1.5827,\"y1\":1.7382,\"y2\":2.0473,\"y3\":2.2327,\"y5\":2.8414,\"y7\":3.1936,\"y10\":3.675,\"y30\":4.4432},{\"time\":\"2008-05-31T00:00:00.000Z\",\"m3\":1.7643,\"m6\":1.8633,\"y1\":2.0557,\"y2\":2.4457,\"y3\":2.6895,\"y5\":3.1533,\"y7\":3.46,\"y10\":3.88,\"y30\":4.5962},{\"time\":\"2008-06-30T00:00:00.000Z\",\"m3\":1.8905,\"m6\":2.1852,\"y1\":2.4195,\"y2\":2.7738,\"y3\":3.0752,\"y5\":3.4852,\"y7\":3.7338,\"y10\":4.0995,\"y30\":4.689},{\"time\":\"2008-07-31T00:00:00.000Z\",\"m3\":1.655,\"m6\":1.9759,\"y1\":2.2818,\"y2\":2.5732,\"y3\":2.8727,\"y5\":3.3032,\"y7\":3.5959,\"y10\":4.0077,\"y30\":4.5709},{\"time\":\"2008-08-31T00:00:00.000Z\",\"m3\":1.7529,\"m6\":1.9671,\"y1\":2.1771,\"y2\":2.4205,\"y3\":2.699,\"y5\":3.1419,\"y7\":3.459,\"y10\":3.8857,\"y30\":4.5019},{\"time\":\"2008-09-30T00:00:00.000Z\",\"m3\":1.1467,\"m6\":1.641,\"y1\":1.9129,\"y2\":2.0762,\"y3\":2.3157,\"y5\":2.8843,\"y7\":3.2486,\"y10\":3.6862,\"y30\":4.269},{\"time\":\"2008-10-31T00:00:00.000Z\",\"m3\":0.6859,\"m6\":1.2259,\"y1\":1.4205,\"y2\":1.6123,\"y3\":1.8645,\"y5\":2.7264,\"y7\":3.1868,\"y10\":3.8141,\"y30\":4.1732},{\"time\":\"2008-11-30T00:00:00.000Z\",\"m3\":0.1939,\"m6\":0.7411,\"y1\":1.0667,\"y2\":1.2128,\"y3\":1.5144,\"y5\":2.2917,\"y7\":2.8189,\"y10\":3.5267,\"y30\":4.0044},{\"time\":\"2008-12-31T00:00:00.000Z\",\"m3\":0.0395,\"m6\":0.2559,\"y1\":0.4945,\"y2\":0.8209,\"y3\":1.07,\"y5\":1.5218,\"y7\":1.8927,\"y10\":2.4164,\"y30\":2.87},{\"time\":\"2009-01-31T00:00:00.000Z\",\"m3\":0.1295,\"m6\":0.3045,\"y1\":0.4445,\"y2\":0.807,\"y3\":1.134,\"y5\":1.5965,\"y7\":1.978,\"y10\":2.5175,\"y30\":3.128},{\"time\":\"2009-02-28T00:00:00.000Z\",\"m3\":0.2953,\"m6\":0.4589,\"y1\":0.6226,\"y2\":0.9753,\"y3\":1.3689,\"y5\":1.8716,\"y7\":2.3032,\"y10\":2.87,\"y30\":3.5868},{\"time\":\"2009-03-31T00:00:00.000Z\",\"m3\":0.2159,\"m6\":0.4259,\"y1\":0.6441,\"y2\":0.9314,\"y3\":1.3123,\"y5\":1.8159,\"y7\":2.4177,\"y10\":2.8195,\"y30\":3.6432},{\"time\":\"2009-04-30T00:00:00.000Z\",\"m3\":0.1581,\"m6\":0.3505,\"y1\":0.5476,\"y2\":0.9271,\"y3\":1.3167,\"y5\":1.8581,\"y7\":2.4652,\"y10\":2.9271,\"y30\":3.76},{\"time\":\"2009-05-31T00:00:00.000Z\",\"m3\":0.177,\"m6\":0.3035,\"y1\":0.5015,\"y2\":0.9295,\"y3\":1.3925,\"y5\":2.134,\"y7\":2.8105,\"y10\":3.293,\"y30\":4.227},{\"time\":\"2009-06-30T00:00:00.000Z\",\"m3\":0.1786,\"m6\":0.3145,\"y1\":0.5136,\"y2\":1.1841,\"y3\":1.7595,\"y5\":2.7059,\"y7\":3.365,\"y10\":3.7218,\"y30\":4.5164},{\"time\":\"2009-07-31T00:00:00.000Z\",\"m3\":0.1836,\"m6\":0.2795,\"y1\":0.4786,\"y2\":1.0191,\"y3\":1.5482,\"y5\":2.4627,\"y7\":3.1382,\"y10\":3.5623,\"y30\":4.4068},{\"time\":\"2009-08-31T00:00:00.000Z\",\"m3\":0.1719,\"m6\":0.269,\"y1\":0.459,\"y2\":1.1152,\"y3\":1.6486,\"y5\":2.571,\"y7\":3.2138,\"y10\":3.5871,\"y30\":4.371},{\"time\":\"2009-09-30T00:00:00.000Z\",\"m3\":0.1233,\"m6\":0.2071,\"y1\":0.4048,\"y2\":0.9562,\"y3\":1.4776,\"y5\":2.369,\"y7\":3.0167,\"y10\":3.4019,\"y30\":4.1857},{\"time\":\"2009-10-31T00:00:00.000Z\",\"m3\":0.0743,\"m6\":0.1605,\"y1\":0.3748,\"y2\":0.949,\"y3\":1.4638,\"y5\":2.3329,\"y7\":2.9633,\"y10\":3.3876,\"y30\":4.1886},{\"time\":\"2009-11-30T00:00:00.000Z\",\"m3\":0.0521,\"m6\":0.1547,\"y1\":0.3132,\"y2\":0.8032,\"y3\":1.3163,\"y5\":2.2305,\"y7\":2.9216,\"y10\":3.4026,\"y30\":4.3147},{\"time\":\"2009-12-31T00:00:00.000Z\",\"m3\":0.0545,\"m6\":0.1677,\"y1\":0.3714,\"y2\":0.8745,\"y3\":1.3832,\"y5\":2.3405,\"y7\":3.0745,\"y10\":3.59,\"y30\":4.4941},{\"time\":\"2010-01-31T00:00:00.000Z\",\"m3\":0.0616,\"m6\":0.1484,\"y1\":0.3458,\"y2\":0.9289,\"y3\":1.4921,\"y5\":2.4842,\"y7\":3.2089,\"y10\":3.7332,\"y30\":4.6047},{\"time\":\"2010-02-28T00:00:00.000Z\",\"m3\":0.1089,\"m6\":0.1811,\"y1\":0.3458,\"y2\":0.8568,\"y3\":1.4011,\"y5\":2.3637,\"y7\":3.1226,\"y10\":3.6911,\"y30\":4.6195},{\"time\":\"2010-03-31T00:00:00.000Z\",\"m3\":0.1504,\"m6\":0.2257,\"y1\":0.3957,\"y2\":0.9587,\"y3\":1.5057,\"y5\":2.433,\"y7\":3.1617,\"y10\":3.7274,\"y30\":4.6448},{\"time\":\"2010-04-30T00:00:00.000Z\",\"m3\":0.1623,\"m6\":0.2441,\"y1\":0.445,\"y2\":1.0605,\"y3\":1.6382,\"y5\":2.5814,\"y7\":3.2818,\"y10\":3.8468,\"y30\":4.6932},{\"time\":\"2010-05-31T00:00:00.000Z\",\"m3\":0.16,\"m6\":0.2235,\"y1\":0.371,\"y2\":0.8305,\"y3\":1.319,\"y5\":2.18,\"y7\":2.861,\"y10\":3.42,\"y30\":4.286},{\"time\":\"2010-06-30T00:00:00.000Z\",\"m3\":0.1236,\"m6\":0.1909,\"y1\":0.3182,\"y2\":0.7245,\"y3\":1.1745,\"y5\":1.9964,\"y7\":2.6559,\"y10\":3.2041,\"y30\":4.1277},{\"time\":\"2010-07-31T00:00:00.000Z\",\"m3\":0.1576,\"m6\":0.2005,\"y1\":0.2914,\"y2\":0.6167,\"y3\":0.9786,\"y5\":1.7629,\"y7\":2.4343,\"y10\":3.0114,\"y30\":3.9943},{\"time\":\"2010-08-31T00:00:00.000Z\",\"m3\":0.155,\"m6\":0.1914,\"y1\":0.2591,\"y2\":0.5205,\"y3\":0.7836,\"y5\":1.4655,\"y7\":2.1005,\"y10\":2.6986,\"y30\":3.8032},{\"time\":\"2010-09-30T00:00:00.000Z\",\"m3\":0.1519,\"m6\":0.1933,\"y1\":0.2567,\"y2\":0.48,\"y3\":0.7419,\"y5\":1.4105,\"y7\":2.0514,\"y10\":2.6476,\"y30\":3.7733},{\"time\":\"2010-10-31T00:00:00.000Z\",\"m3\":0.1345,\"m6\":0.1765,\"y1\":0.228,\"y2\":0.376,\"y3\":0.5685,\"y5\":1.1825,\"y7\":1.8515,\"y10\":2.54,\"y30\":3.8725},{\"time\":\"2010-11-30T00:00:00.000Z\",\"m3\":0.143,\"m6\":0.182,\"y1\":0.252,\"y2\":0.454,\"y3\":0.674,\"y5\":1.35,\"y7\":2.0235,\"y10\":2.763,\"y30\":4.186},{\"time\":\"2010-12-31T00:00:00.000Z\",\"m3\":0.1409,\"m6\":0.1932,\"y1\":0.2941,\"y2\":0.6182,\"y3\":0.9932,\"y5\":1.9345,\"y7\":2.6577,\"y10\":3.2909,\"y30\":4.4177},{\"time\":\"2011-01-31T00:00:00.000Z\",\"m3\":0.152,\"m6\":0.1815,\"y1\":0.2735,\"y2\":0.6145,\"y3\":1.0275,\"y5\":1.9945,\"y7\":2.7235,\"y10\":3.394,\"y30\":4.523},{\"time\":\"2011-02-28T00:00:00.000Z\",\"m3\":0.1321,\"m6\":0.1684,\"y1\":0.2863,\"y2\":0.7726,\"y3\":1.2832,\"y5\":2.2579,\"y7\":2.9616,\"y10\":3.5763,\"y30\":4.6521},{\"time\":\"2011-03-31T00:00:00.000Z\",\"m3\":0.1004,\"m6\":0.1565,\"y1\":0.2591,\"y2\":0.6974,\"y3\":1.1743,\"y5\":2.1135,\"y7\":2.7991,\"y10\":3.4143,\"y30\":4.5139},{\"time\":\"2011-04-30T00:00:00.000Z\",\"m3\":0.0585,\"m6\":0.1195,\"y1\":0.2465,\"y2\":0.7345,\"y3\":1.21,\"y5\":2.169,\"y7\":2.837,\"y10\":3.455,\"y30\":4.5015},{\"time\":\"2011-05-31T00:00:00.000Z\",\"m3\":0.041,\"m6\":0.0867,\"y1\":0.1881,\"y2\":0.5552,\"y3\":0.9352,\"y5\":1.8424,\"y7\":2.5138,\"y10\":3.1686,\"y30\":4.2933},{\"time\":\"2011-06-30T00:00:00.000Z\",\"m3\":0.0373,\"m6\":0.1014,\"y1\":0.1809,\"y2\":0.4105,\"y3\":0.7114,\"y5\":1.58,\"y7\":2.2868,\"y10\":3.0023,\"y30\":4.2327},{\"time\":\"2011-07-31T00:00:00.000Z\",\"m3\":0.0375,\"m6\":0.084,\"y1\":0.185,\"y2\":0.4065,\"y3\":0.68,\"y5\":1.541,\"y7\":2.277,\"y10\":3.003,\"y30\":4.2705},{\"time\":\"2011-08-31T00:00:00.000Z\",\"m3\":0.0243,\"m6\":0.0622,\"y1\":0.1148,\"y2\":0.2304,\"y3\":0.383,\"y5\":1.0213,\"y7\":1.6291,\"y10\":2.303,\"y30\":3.6513},{\"time\":\"2011-09-30T00:00:00.000Z\",\"m3\":0.0138,\"m6\":0.0433,\"y1\":0.1048,\"y2\":0.2114,\"y3\":0.3538,\"y5\":0.9005,\"y7\":1.4195,\"y10\":1.9752,\"y30\":3.1824},{\"time\":\"2011-10-31T00:00:00.000Z\",\"m3\":0.019,\"m6\":0.0545,\"y1\":0.1145,\"y2\":0.2805,\"y3\":0.471,\"y5\":1.0615,\"y7\":1.619,\"y10\":2.152,\"y30\":3.128},{\"time\":\"2011-11-30T00:00:00.000Z\",\"m3\":0.014,\"m6\":0.0465,\"y1\":0.112,\"y2\":0.2535,\"y3\":0.3935,\"y5\":0.908,\"y7\":1.452,\"y10\":2.0135,\"y30\":3.0155},{\"time\":\"2011-12-31T00:00:00.000Z\",\"m3\":0.0114,\"m6\":0.0486,\"y1\":0.1152,\"y2\":0.2571,\"y3\":0.3871,\"y5\":0.8914,\"y7\":1.429,\"y10\":1.9781,\"y30\":2.9824},{\"time\":\"2012-01-31T00:00:00.000Z\",\"m3\":0.0345,\"m6\":0.0655,\"y1\":0.115,\"y2\":0.2405,\"y3\":0.358,\"y5\":0.835,\"y7\":1.3805,\"y10\":1.9665,\"y30\":3.026},{\"time\":\"2012-02-29T00:00:00.000Z\",\"m3\":0.092,\"m6\":0.124,\"y1\":0.161,\"y2\":0.278,\"y3\":0.3845,\"y5\":0.831,\"y7\":1.3735,\"y10\":1.9675,\"y30\":3.109},{\"time\":\"2012-03-31T00:00:00.000Z\",\"m3\":0.0841,\"m6\":0.1423,\"y1\":0.19,\"y2\":0.3445,\"y3\":0.5077,\"y5\":1.0173,\"y7\":1.5645,\"y10\":2.1727,\"y30\":3.2814},{\"time\":\"2012-04-30T00:00:00.000Z\",\"m3\":0.0838,\"m6\":0.139,\"y1\":0.1833,\"y2\":0.2919,\"y3\":0.4319,\"y5\":0.8948,\"y7\":1.4252,\"y10\":2.0529,\"y30\":3.1843},{\"time\":\"2012-05-31T00:00:00.000Z\",\"m3\":0.0895,\"m6\":0.1455,\"y1\":0.1918,\"y2\":0.285,\"y3\":0.3882,\"y5\":0.7618,\"y7\":1.2132,\"y10\":1.8032,\"y30\":2.9309},{\"time\":\"2012-06-30T00:00:00.000Z\",\"m3\":0.0914,\"m6\":0.1481,\"y1\":0.189,\"y2\":0.2919,\"y3\":0.389,\"y5\":0.7114,\"y7\":1.08,\"y10\":1.6224,\"y30\":2.6981},{\"time\":\"2012-07-31T00:00:00.000Z\",\"m3\":0.0971,\"m6\":0.1457,\"y1\":0.1857,\"y2\":0.2481,\"y3\":0.331,\"y5\":0.6195,\"y7\":0.9843,\"y10\":1.5267,\"y30\":2.59},{\"time\":\"2012-08-31T00:00:00.000Z\",\"m3\":0.1026,\"m6\":0.1404,\"y1\":0.1835,\"y2\":0.2687,\"y3\":0.3678,\"y5\":0.7139,\"y7\":1.1352,\"y10\":1.6783,\"y30\":2.7709},{\"time\":\"2012-09-30T00:00:00.000Z\",\"m3\":0.1053,\"m6\":0.1374,\"y1\":0.1758,\"y2\":0.2553,\"y3\":0.3384,\"y5\":0.6689,\"y7\":1.1184,\"y10\":1.7232,\"y30\":2.8816},{\"time\":\"2012-10-31T00:00:00.000Z\",\"m3\":0.1048,\"m6\":0.149,\"y1\":0.1795,\"y2\":0.2771,\"y3\":0.369,\"y5\":0.7086,\"y7\":1.1457,\"y10\":1.7462,\"y30\":2.9005},{\"time\":\"2012-11-30T00:00:00.000Z\",\"m3\":0.0935,\"m6\":0.1445,\"y1\":0.1785,\"y2\":0.268,\"y3\":0.355,\"y5\":0.665,\"y7\":1.0765,\"y10\":1.654,\"y30\":2.8035},{\"time\":\"2012-12-31T00:00:00.000Z\",\"m3\":0.07,\"m6\":0.1195,\"y1\":0.1585,\"y2\":0.257,\"y3\":0.354,\"y5\":0.696,\"y7\":1.1345,\"y10\":1.719,\"y30\":2.8835},{\"time\":\"2013-01-31T00:00:00.000Z\",\"m3\":0.0743,\"m6\":0.1076,\"y1\":0.1452,\"y2\":0.2652,\"y3\":0.3905,\"y5\":0.8052,\"y7\":1.2986,\"y10\":1.9148,\"y30\":3.0805},{\"time\":\"2013-02-28T00:00:00.000Z\",\"m3\":0.0989,\"m6\":0.1242,\"y1\":0.1574,\"y2\":0.2674,\"y3\":0.3984,\"y5\":0.8463,\"y7\":1.3484,\"y10\":1.9842,\"y30\":3.1653},{\"time\":\"2013-03-31T00:00:00.000Z\",\"m3\":0.087,\"m6\":0.1145,\"y1\":0.1475,\"y2\":0.256,\"y3\":0.386,\"y5\":0.8185,\"y7\":1.3175,\"y10\":1.9575,\"y30\":3.1625},{\"time\":\"2013-04-30T00:00:00.000Z\",\"m3\":0.06,\"m6\":0.0941,\"y1\":0.1245,\"y2\":0.2323,\"y3\":0.3405,\"y5\":0.7105,\"y7\":1.1523,\"y10\":1.7591,\"y30\":2.9327},{\"time\":\"2013-05-31T00:00:00.000Z\",\"m3\":0.0441,\"m6\":0.0809,\"y1\":0.1186,\"y2\":0.2505,\"y3\":0.3959,\"y5\":0.8409,\"y7\":1.3109,\"y10\":1.9282,\"y30\":3.1127},{\"time\":\"2013-06-30T00:00:00.000Z\",\"m3\":0.0505,\"m6\":0.0875,\"y1\":0.142,\"y2\":0.3335,\"y3\":0.577,\"y5\":1.2035,\"y7\":1.714,\"y10\":2.3,\"y30\":3.4},{\"time\":\"2013-07-31T00:00:00.000Z\",\"m3\":0.0355,\"m6\":0.0736,\"y1\":0.1218,\"y2\":0.3409,\"y3\":0.6441,\"y5\":1.4032,\"y7\":1.9914,\"y10\":2.5823,\"y30\":3.605},{\"time\":\"2013-08-31T00:00:00.000Z\",\"m3\":0.0436,\"m6\":0.0718,\"y1\":0.1268,\"y2\":0.3564,\"y3\":0.7045,\"y5\":1.5205,\"y7\":2.1523,\"y10\":2.7373,\"y30\":3.7577},{\"time\":\"2013-09-30T00:00:00.000Z\",\"m3\":0.016,\"m6\":0.0415,\"y1\":0.1185,\"y2\":0.404,\"y3\":0.7795,\"y5\":1.596,\"y7\":2.2165,\"y10\":2.8095,\"y30\":3.787},{\"time\":\"2013-10-31T00:00:00.000Z\",\"m3\":0.0473,\"m6\":0.0759,\"y1\":0.1214,\"y2\":0.3355,\"y3\":0.6277,\"y5\":1.3668,\"y7\":1.9936,\"y10\":2.6159,\"y30\":3.6759},{\"time\":\"2013-11-30T00:00:00.000Z\",\"m3\":0.0679,\"m6\":0.0984,\"y1\":0.1216,\"y2\":0.3037,\"y3\":0.58,\"y5\":1.3711,\"y7\":2.0695,\"y10\":2.7184,\"y30\":3.8},{\"time\":\"2013-12-31T00:00:00.000Z\",\"m3\":0.0667,\"m6\":0.0952,\"y1\":0.1329,\"y2\":0.34,\"y3\":0.6852,\"y5\":1.5762,\"y7\":2.2857,\"y10\":2.9019,\"y30\":3.889},{\"time\":\"2014-01-31T00:00:00.000Z\",\"m3\":0.0433,\"m6\":0.069,\"y1\":0.1162,\"y2\":0.3938,\"y3\":0.7795,\"y5\":1.6467,\"y7\":2.2938,\"y10\":2.8581,\"y30\":3.769},{\"time\":\"2014-02-28T00:00:00.000Z\",\"m3\":0.0526,\"m6\":0.0811,\"y1\":0.1168,\"y2\":0.3268,\"y3\":0.6884,\"y5\":1.5158,\"y7\":2.1526,\"y10\":2.7095,\"y30\":3.6626},{\"time\":\"2014-03-31T00:00:00.000Z\",\"m3\":0.0524,\"m6\":0.0786,\"y1\":0.1281,\"y2\":0.399,\"y3\":0.8167,\"y5\":1.6395,\"y7\":2.2333,\"y10\":2.7233,\"y30\":3.621},{\"time\":\"2014-04-30T00:00:00.000Z\",\"m3\":0.031,\"m6\":0.0533,\"y1\":0.1076,\"y2\":0.4171,\"y3\":0.8848,\"y5\":1.701,\"y7\":2.2695,\"y10\":2.7052,\"y30\":3.5176},{\"time\":\"2014-05-31T00:00:00.000Z\",\"m3\":0.0324,\"m6\":0.0519,\"y1\":0.0967,\"y2\":0.389,\"y3\":0.8262,\"y5\":1.5929,\"y7\":2.121,\"y10\":2.559,\"y30\":3.39},{\"time\":\"2014-06-30T00:00:00.000Z\",\"m3\":0.0357,\"m6\":0.0605,\"y1\":0.1048,\"y2\":0.4524,\"y3\":0.9048,\"y5\":1.679,\"y7\":2.1933,\"y10\":2.5986,\"y30\":3.42},{\"time\":\"2014-07-31T00:00:00.000Z\",\"m3\":0.0264,\"m6\":0.0595,\"y1\":0.1114,\"y2\":0.5064,\"y3\":0.9736,\"y5\":1.6995,\"y7\":2.1727,\"y10\":2.5423,\"y30\":3.3318},{\"time\":\"2014-08-31T00:00:00.000Z\",\"m3\":0.0329,\"m6\":0.0524,\"y1\":0.1071,\"y2\":0.4724,\"y3\":0.9276,\"y5\":1.6314,\"y7\":2.0805,\"y10\":2.42,\"y30\":3.201},{\"time\":\"2014-09-30T00:00:00.000Z\",\"m3\":0.02,\"m6\":0.0438,\"y1\":0.1095,\"y2\":0.5667,\"y3\":1.051,\"y5\":1.7738,\"y7\":2.2205,\"y10\":2.5343,\"y30\":3.26},{\"time\":\"2014-10-31T00:00:00.000Z\",\"m3\":0.0168,\"m6\":0.0505,\"y1\":0.1045,\"y2\":0.4455,\"y3\":0.875,\"y5\":1.5459,\"y7\":1.9805,\"y10\":2.3041,\"y30\":3.04},{\"time\":\"2014-11-30T00:00:00.000Z\",\"m3\":0.0217,\"m6\":0.0694,\"y1\":0.1344,\"y2\":0.5272,\"y3\":0.9628,\"y5\":1.6206,\"y7\":2.0289,\"y10\":2.3256,\"y30\":3.0383},{\"time\":\"2014-12-31T00:00:00.000Z\",\"m3\":0.0291,\"m6\":0.1064,\"y1\":0.2145,\"y2\":0.6355,\"y3\":1.0641,\"y5\":1.64,\"y7\":1.9805,\"y10\":2.2073,\"y30\":2.8332},{\"time\":\"2015-01-31T00:00:00.000Z\",\"m3\":0.0275,\"m6\":0.083,\"y1\":0.1955,\"y2\":0.5515,\"y3\":0.897,\"y5\":1.3745,\"y7\":1.6715,\"y10\":1.8815,\"y30\":2.455},{\"time\":\"2015-02-28T00:00:00.000Z\",\"m3\":0.0179,\"m6\":0.0711,\"y1\":0.2242,\"y2\":0.6189,\"y3\":0.9916,\"y5\":1.4726,\"y7\":1.7879,\"y10\":1.9753,\"y30\":2.5663},{\"time\":\"2015-03-31T00:00:00.000Z\",\"m3\":0.0277,\"m6\":0.11,\"y1\":0.2536,\"y2\":0.6405,\"y3\":1.0168,\"y5\":1.5191,\"y7\":1.8423,\"y10\":2.0427,\"y30\":2.6264},{\"time\":\"2015-04-30T00:00:00.000Z\",\"m3\":0.0232,\"m6\":0.0936,\"y1\":0.2336,\"y2\":0.54,\"y3\":0.8655,\"y5\":1.3545,\"y7\":1.6909,\"y10\":1.935,\"y30\":2.5859},{\"time\":\"2015-05-31T00:00:00.000Z\",\"m3\":0.0165,\"m6\":0.0795,\"y1\":0.241,\"y2\":0.609,\"y3\":0.977,\"y5\":1.5385,\"y7\":1.933,\"y10\":2.1975,\"y30\":2.955},{\"time\":\"2015-06-30T00:00:00.000Z\",\"m3\":0.015,\"m6\":0.0868,\"y1\":0.2755,\"y2\":0.6886,\"y3\":1.065,\"y5\":1.6836,\"y7\":2.0995,\"y10\":2.3636,\"y30\":3.1118},{\"time\":\"2015-07-31T00:00:00.000Z\",\"m3\":0.0323,\"m6\":0.1164,\"y1\":0.2968,\"y2\":0.6677,\"y3\":1.0264,\"y5\":1.6323,\"y7\":2.0418,\"y10\":2.3245,\"y30\":3.0664},{\"time\":\"2015-08-31T00:00:00.000Z\",\"m3\":0.0719,\"m6\":0.22,\"y1\":0.3757,\"y2\":0.6976,\"y3\":1.03,\"y5\":1.5414,\"y7\":1.9081,\"y10\":2.1671,\"y30\":2.8557},{\"time\":\"2015-09-30T00:00:00.000Z\",\"m3\":0.0243,\"m6\":0.1795,\"y1\":0.3733,\"y2\":0.7133,\"y3\":1.0133,\"y5\":1.49,\"y7\":1.8762,\"y10\":2.1729,\"y30\":2.9529},{\"time\":\"2015-10-31T00:00:00.000Z\",\"m3\":0.0167,\"m6\":0.1138,\"y1\":0.2633,\"y2\":0.6448,\"y3\":0.9267,\"y5\":1.3857,\"y7\":1.7624,\"y10\":2.07,\"y30\":2.8881},{\"time\":\"2015-11-30T00:00:00.000Z\",\"m3\":0.1263,\"m6\":0.3279,\"y1\":0.4768,\"y2\":0.8847,\"y3\":1.2042,\"y5\":1.6711,\"y7\":2.0247,\"y10\":2.2632,\"y30\":3.03},{\"time\":\"2015-12-31T00:00:00.000Z\",\"m3\":0.2286,\"m6\":0.4991,\"y1\":0.6536,\"y2\":0.9827,\"y3\":1.2805,\"y5\":1.6986,\"y7\":2.0382,\"y10\":2.2427,\"y30\":2.97},{\"time\":\"2016-01-31T00:00:00.000Z\",\"m3\":0.22,\"m6\":0.49,\"y1\":0.61,\"y2\":1.02,\"y3\":1.31,\"y5\":1.73,\"y7\":2.06,\"y10\":2.24,\"y30\":2.98}]\n", + "beakerx.time = [633744000000,636163200000,638841600000,641433600000,644112000000,646704000000,649382400000,652060800000,654652800000,657331200000,659923200000,662601600000,665280000000,667699200000,670377600000,672969600000,675648000000,678240000000,680918400000,683596800000,686188800000,688867200000,691459200000,694137600000,696816000000,699321600000,702000000000,704592000000,707270400000,709862400000,712540800000,715219200000,717811200000,720489600000,723081600000,725760000000,728438400000,730857600000,733536000000,736128000000,738806400000,741398400000,744076800000,746755200000,749347200000,752025600000,754617600000,757296000000,759974400000,762393600000,765072000000,767664000000,770342400000,772934400000,775612800000,778291200000,780883200000,783561600000,786153600000,788832000000,791510400000,793929600000,796608000000,799200000000,801878400000,804470400000,807148800000,809827200000,812419200000,815097600000,817689600000,820368000000,823046400000,825552000000,828230400000,830822400000,833500800000,836092800000,838771200000,841449600000,844041600000,846720000000,849312000000,851990400000,854668800000,857088000000,859766400000,862358400000,865036800000,867628800000,870307200000,872985600000,875577600000,878256000000,880848000000,883526400000,886204800000,888624000000,891302400000,893894400000,896572800000,899164800000,901843200000,904521600000,907113600000,909792000000,912384000000,915062400000,917740800000,920160000000,922838400000,925430400000,928108800000,930700800000,933379200000,936057600000,938649600000,941328000000,943920000000,946598400000,949276800000,951782400000,954460800000,957052800000,959731200000,962323200000,965001600000,967680000000,970272000000,972950400000,975542400000,978220800000,980899200000,983318400000,985996800000,988588800000,991267200000,993859200000,996537600000,999216000000,1001808000000,1004486400000,1007078400000,1009756800000,1012435200000,1014854400000,1017532800000,1020124800000,1022803200000,1025395200000,1028073600000,1030752000000,1033344000000,1036022400000,1038614400000,1041292800000,1043971200000,1046390400000,1049068800000,1051660800000,1054339200000,1056931200000,1059609600000,1062288000000,1064880000000,1067558400000,1070150400000,1072828800000,1075507200000,1078012800000,1080691200000,1083283200000,1085961600000,1088553600000,1091232000000,1093910400000,1096502400000,1099180800000,1101772800000,1104451200000,1107129600000,1109548800000,1112227200000,1114819200000,1117497600000,1120089600000,1122768000000,1125446400000,1128038400000,1130716800000,1133308800000,1135987200000,1138665600000,1141084800000,1143763200000,1146355200000,1149033600000,1151625600000,1154304000000,1156982400000,1159574400000,1162252800000,1164844800000,1167523200000,1170201600000,1172620800000,1175299200000,1177891200000,1180569600000,1183161600000,1185840000000,1188518400000,1191110400000,1193788800000,1196380800000,1199059200000,1201737600000,1204243200000,1206921600000,1209513600000,1212192000000,1214784000000,1217462400000,1220140800000,1222732800000,1225411200000,1228003200000,1230681600000,1233360000000,1235779200000,1238457600000,1241049600000,1243728000000,1246320000000,1248998400000,1251676800000,1254268800000,1256947200000,1259539200000,1262217600000,1264896000000,1267315200000,1269993600000,1272585600000,1275264000000,1277856000000,1280534400000,1283212800000,1285804800000,1288483200000,1291075200000,1293753600000,1296432000000,1298851200000,1301529600000,1304121600000,1306800000000,1309392000000,1312070400000,1314748800000,1317340800000,1320019200000,1322611200000,1325289600000,1327968000000,1330473600000,1333152000000,1335744000000,1338422400000,1341014400000,1343692800000,1346371200000,1348963200000,1351641600000,1354233600000,1356912000000,1359590400000,1362009600000,1364688000000,1367280000000,1369958400000,1372550400000,1375228800000,1377907200000,1380499200000,1383177600000,1385769600000,1388448000000,1391126400000,1393545600000,1396224000000,1398816000000,1401494400000,1404086400000,1406764800000,1409443200000,1412035200000,1414713600000,1417305600000,1419984000000,1422662400000,1425081600000,1427760000000,1430352000000,1433030400000,1435622400000,1438300800000,1440979200000,1443571200000,1446249600000,1448841600000,1451520000000,1454198400000]\n", + "beakerx.m3 = [7.8981,8.0021,8.1700,8.0405,8.0068,7.9867,7.8748,7.6943,7.5979,7.4000,7.2905,6.9495,6.4105,6.1163,6.0935,5.8255,5.6336,5.7510,5.7509,5.4977,5.3735,5.1441,4.6895,4.1843,3.9062,3.9500,4.1386,3.8390,3.7190,3.7450,3.2795,3.1990,2.9686,2.9329,3.2058,3.2914,3.0711,2.9926,3.0143,2.9305,3.0250,3.1445,3.1110,3.0905,3.0086,3.0920,3.1795,3.1277,3.0445,3.3305,3.5909,3.7805,4.2676,4.2491,4.4565,4.6117,4.7514,5.1020,5.4490,5.7648,5.9015,5.9395,5.9104,5.8379,5.8477,5.6391,5.5930,5.5657,5.4340,5.4443,5.5181,5.2940,5.1505,4.9645,5.0976,5.0877,5.1536,5.2315,5.2982,5.1891,5.2375,5.1232,5.1695,5.0405,5.1657,5.1421,5.2845,5.3045,5.1967,5.0710,5.1945,5.2795,5.0848,5.1127,5.2811,5.3045,5.1790,5.2300,5.1618,5.0829,5.1400,5.1241,5.0945,5.0410,4.7390,4.0705,4.5311,4.4968,4.4463,4.5553,4.5665,4.4091,4.6310,4.7155,4.6857,4.8736,4.8224,5.0185,5.2275,5.3568,5.4990,5.7270,5.8639,5.8216,5.9945,5.8618,6.1425,6.2774,6.1750,6.2948,6.3562,5.9370,5.2852,5.0053,4.5355,3.9660,3.7032,3.5652,3.5933,3.4378,2.6924,2.1977,1.9065,1.7200,1.6848,1.7568,1.8250,1.7450,1.7605,1.7320,1.7136,1.6473,1.6590,1.6100,1.2547,1.2110,1.1890,1.1853,1.1510,1.1524,1.0881,0.9362,0.9205,0.9690,0.9552,0.9418,0.9522,0.9145,0.9005,0.9447,0.9535,0.9581,1.0395,1.2881,1.3576,1.5032,1.6814,1.7945,2.1075,2.2227,2.3710,2.5795,2.7959,2.8367,2.9019,3.0364,3.2875,3.5183,3.4943,3.7925,3.9745,3.9710,4.3360,4.5395,4.6278,4.7216,4.8364,4.9177,5.0765,5.0904,4.9300,5.0462,5.0733,4.9730,5.1052,5.1632,5.0800,5.0067,4.8677,4.7419,4.9610,4.3170,3.9942,4.0027,3.3545,3.0665,2.8200,2.1740,1.2835,1.3118,1.7643,1.8905,1.6550,1.7529,1.1467,0.6859,0.1939,0.0395,0.1295,0.2953,0.2159,0.1581,0.1770,0.1786,0.1836,0.1719,0.1233,0.0743,0.0521,0.0545,0.0616,0.1089,0.1504,0.1623,0.1600,0.1236,0.1576,0.1550,0.1519,0.1345,0.1430,0.1409,0.1520,0.1321,0.1004,0.0585,0.0410,0.0373,0.0375,0.0243,0.0138,0.0190,0.0140,0.0114,0.0345,0.0920,0.0841,0.0838,0.0895,0.0914,0.0971,0.1026,0.1053,0.1048,0.0935,0.0700,0.0743,0.0989,0.0870,0.0600,0.0441,0.0505,0.0355,0.0436,0.0160,0.0473,0.0679,0.0667,0.0433,0.0526,0.0524,0.0310,0.0324,0.0357,0.0264,0.0329,0.0200,0.0168,0.0217,0.0291,0.0275,0.0179,0.0277,0.0232,0.0165,0.0150,0.0323,0.0719,0.0243,0.0167,0.1263,0.2286,0.2200]\n", + "beakerx.y10 = [8.2067,8.4732,8.5886,8.7855,8.7582,8.4800,8.4714,8.7526,8.8932,8.7195,8.3920,8.0750,8.0919,7.8547,8.1100,8.0391,8.0677,8.2840,8.2727,7.9000,7.6500,7.5273,7.4174,7.0886,7.0324,7.3379,7.5423,7.4805,7.3920,7.2618,6.8445,6.5857,6.4152,6.5890,6.8732,6.7700,6.6000,6.2589,5.9752,5.9695,6.0355,5.9627,5.8052,5.6777,5.3600,5.3340,5.7240,5.7741,5.7505,5.9732,6.4826,6.9721,7.1833,7.1014,7.2980,7.2361,7.4571,7.7440,7.9550,7.8138,7.7795,7.4695,7.2048,7.0626,6.6327,6.1682,6.2780,6.4883,6.1975,6.0448,5.9305,5.7115,5.6524,5.8055,6.2686,6.5114,6.7368,6.9120,6.8655,6.6355,6.8320,6.5336,6.2037,6.3024,6.5790,6.4195,6.6945,6.8855,6.7110,6.4938,6.2205,6.2986,6.2086,6.0295,5.8750,5.8086,5.5445,5.5747,5.6473,5.6376,5.6525,5.4964,5.4614,5.3419,4.8067,4.5300,4.8274,4.6450,4.7221,4.9989,5.2326,5.1845,5.5395,5.8995,5.7919,5.9391,5.9152,6.1120,6.0340,6.2755,6.6610,6.5195,6.2565,5.9905,6.4405,6.0973,6.0540,5.8261,5.7990,5.7386,5.7171,5.2405,5.1610,5.0989,4.8855,5.1410,5.3914,5.2843,5.2362,4.9713,4.7318,4.5668,4.6515,5.0875,5.0357,4.9116,5.2840,5.2109,5.1645,4.9265,4.6532,4.2573,3.8700,3.9409,4.0484,4.0324,4.0486,3.9026,3.8071,3.9586,3.5690,3.3343,3.9755,4.4452,4.2743,4.2905,4.3000,4.2677,4.1505,4.0842,3.8265,4.3476,4.7155,4.7338,4.4981,4.2814,4.1257,4.0970,4.1940,4.2309,4.2215,4.1653,4.4977,4.3410,4.1443,3.9982,4.1775,4.2626,4.1990,4.4635,4.5350,4.4671,4.4160,4.5689,4.7239,4.9905,5.1100,5.1064,5.0875,4.8765,4.7190,4.7290,4.5952,4.5645,4.7595,4.7226,4.5645,4.6938,4.7464,5.1029,5.0043,4.6748,4.5216,4.5277,4.1485,4.0975,3.7443,3.7375,3.5100,3.6750,3.8800,4.0995,4.0077,3.8857,3.6862,3.8141,3.5267,2.4164,2.5175,2.8700,2.8195,2.9271,3.2930,3.7218,3.5623,3.5871,3.4019,3.3876,3.4026,3.5900,3.7332,3.6911,3.7274,3.8468,3.4200,3.2041,3.0114,2.6986,2.6476,2.5400,2.7630,3.2909,3.3940,3.5763,3.4143,3.4550,3.1686,3.0023,3.0030,2.3030,1.9752,2.1520,2.0135,1.9781,1.9665,1.9675,2.1727,2.0529,1.8032,1.6224,1.5267,1.6783,1.7232,1.7462,1.6540,1.7190,1.9148,1.9842,1.9575,1.7591,1.9282,2.3000,2.5823,2.7373,2.8095,2.6159,2.7184,2.9019,2.8581,2.7095,2.7233,2.7052,2.5590,2.5986,2.5423,2.4200,2.5343,2.3041,2.3256,2.2073,1.8815,1.9753,2.0427,1.9350,2.1975,2.3636,2.3245,2.1671,2.1729,2.0700,2.2632,2.2427,2.2400]\n", + "beakerx.spread = [0.3086,0.4711,0.4186,0.7450,0.7514,0.4933,0.5967,1.0583,1.2953,1.3195,1.1015,1.1255,1.6814,1.7384,2.0165,2.2136,2.4341,2.5330,2.5218,2.4023,2.2765,2.3832,2.7279,2.9043,3.1262,3.3879,3.4036,3.6414,3.6730,3.5168,3.5650,3.3867,3.4467,3.6562,3.6674,3.4786,3.5289,3.2663,2.9609,3.0390,3.0105,2.8182,2.6943,2.5873,2.3514,2.2420,2.5445,2.6464,2.7060,2.6426,2.8917,3.1916,2.9157,2.8523,2.8415,2.6243,2.7057,2.6420,2.5060,2.0490,1.8780,1.5300,1.2943,1.2247,0.7850,0.5291,0.6850,0.9226,0.7635,0.6005,0.4124,0.4175,0.5019,0.8410,1.1710,1.4236,1.5832,1.6805,1.5673,1.4464,1.5945,1.4105,1.0342,1.2619,1.4133,1.2774,1.4100,1.5809,1.5143,1.4229,1.0259,1.0190,1.1238,0.9168,0.5939,0.5041,0.3655,0.3447,0.4855,0.5548,0.5125,0.3723,0.3668,0.3010,0.0676,0.4595,0.2963,0.1482,0.2758,0.4437,0.6661,0.7755,0.9085,1.1841,1.1062,1.0655,1.0929,1.0935,0.8065,0.9186,1.1620,0.7925,0.3926,0.1689,0.4459,0.2355,-0.0885,-0.4513,-0.3760,-0.5562,-0.6390,-0.6965,-0.1243,0.0937,0.3500,1.1750,1.6882,1.7190,1.6429,1.5335,2.0394,2.3691,2.7450,3.3675,3.3510,3.1547,3.4590,3.4659,3.4041,3.1945,2.9395,2.6100,2.2110,2.3309,2.7937,2.8214,2.8595,2.7174,2.6562,2.8062,2.4810,2.3981,3.0550,3.4762,3.3190,3.3486,3.3478,3.3532,3.2500,3.1395,2.8730,3.3895,3.6760,3.4457,3.1405,2.7782,2.4443,2.3025,2.0865,2.0082,1.8505,1.5858,1.7018,1.5043,1.2424,0.9618,0.8900,0.7443,0.7048,0.6710,0.5605,0.4962,0.0800,0.0295,0.0961,0.2689,0.2736,0.1886,0.0110,-0.2139,-0.2110,-0.3171,-0.4781,-0.4085,-0.3457,-0.4405,-0.5155,-0.3129,-0.1214,0.3610,0.0433,0.3578,0.5274,0.5250,0.7940,1.0310,0.9243,1.5635,2.2265,2.3632,2.1157,2.2090,2.3527,2.1329,2.5395,3.1282,3.3328,2.3769,2.3880,2.5747,2.6036,2.7690,3.1160,3.5432,3.3786,3.4152,3.2786,3.3133,3.3505,3.5355,3.6716,3.5821,3.5770,3.6845,3.2600,3.0805,2.8538,2.5436,2.4957,2.4055,2.6200,3.1500,3.2420,3.4442,3.3139,3.3965,3.1276,2.9650,2.9655,2.2787,1.9614,2.1330,1.9995,1.9667,1.9320,1.8755,2.0886,1.9690,1.7136,1.5310,1.4295,1.5757,1.6179,1.6414,1.5605,1.6490,1.8405,1.8853,1.8705,1.6991,1.8841,2.2495,2.5468,2.6936,2.7935,2.5686,2.6505,2.8352,2.8148,2.6568,2.6710,2.6743,2.5267,2.5629,2.5159,2.3871,2.5143,2.2873,2.3039,2.1782,1.8540,1.9574,2.0150,1.9118,2.1810,2.3486,2.2923,2.0952,2.1486,2.0533,2.1368,2.0141,2.0200]\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%javascript\n", + "\n", + "var x = [1, 4, 6, 8, 10]\n", + "var y = [3, 6, 4, 5, 9]\n", + "var pp = new beakerx.Plot({title: 'Bars, Lines, Points and 2nd yAxis', \n", + " xLabel: \"xLabel\", \n", + " yLabel: \"yLabel\", \n", + " legendLayout: beakerx.LegendLayout.HORIZONTAL,\n", + " legendPosition: beakerx.LegendPosition.RIGHT,\n", + " omitCheckboxes: true})\n", + "pp.add(new beakerx.YAxis({label: \"Right yAxis\"}))\n", + "pp.add(new beakerx.Bars({displayName: \"Bar\", \n", + " x: [1,3,5,7,10], \n", + " y: [100, 120,90,100,80], \n", + " width: 1}))\n", + "pp.add(new beakerx.Line({displayName: \"Line\", \n", + " x: x, \n", + " y: y, \n", + " width: 6, \n", + " yAxis: \"Right yAxis\"}))\n", + "pp.add(new beakerx.Points({x: x, \n", + " y: y, \n", + " size: 10, \n", + " shape: beakerx.ShapeType.DIAMOND,\n", + " yAxis: \"Right yAxis\"}))\n", + "\n", + "pp.display(this);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%javascript\n", + "\n", + "var ch = new beakerx.Crosshair({color: beakerx.prefs.theme.plotColors[0], width: 2, style: beakerx.StrokeType.DOT})\n", + "var plot = new beakerx.Plot({crosshair: ch});\n", + "var y1 = [4, 8, 16, 20, 32]\n", + "var base = [2, 4, 8, 10, 16];\n", + "var cs = [beakerx.Color.black, beakerx.Color.orange, beakerx.Color.gray, beakerx.Color.yellow, beakerx.Color.pink]\n", + "var ss = [beakerx.StrokeType.SOLID, \n", + " beakerx.StrokeType.SOLID, \n", + " beakerx.StrokeType.DASH, \n", + " beakerx.StrokeType.DOT, \n", + " beakerx.StrokeType.DASHDOT, \n", + " beakerx.StrokeType.LONGDASH]\n", + "plot.add(new beakerx.Area({y: y1, base: base, color: new beakerx.Color(255, 0, 0, 50)}))\n", + "plot.add(new beakerx.Stems({y: y1, base: base, color: cs, style: ss, width: 5}))\n", + "\n", + "plot.display(this);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%javascript\n", + "\n", + "var POSITIVE_INFINITY = 1.0 / 0.0;\n", + "var NEGATIVE_INFINITY = -1.0 / 0.0;\n", + "var p = new beakerx.Plot()\n", + "p.add(new beakerx.Line({y: [-3, 1, 3, 4, 5]}))\n", + "p.add(new beakerx.ConstantBand({x: [NEGATIVE_INFINITY, 0.5], color: new beakerx.Color(128, 128, 128, 50)}))\n", + "p.add(new beakerx.ConstantLine({x: 0.5, style: beakerx.StrokeType.DOT, color: beakerx.Color.blue}))\n", + "p.add(new beakerx.ConstantLine({y: -1, style: beakerx.StrokeType.DASHDOT, color: beakerx.Color.blue}))\n", + "p.add(new beakerx.ConstantLine({x: 3, y: 4, color: beakerx.Color.gray, width: 5, showLabel: true}))\n", + "p.add(new beakerx.ConstantBand({x: [1, 2], y: [1, 3]}))\n", + "p.add(new beakerx.ConstantBand({x: [4, POSITIVE_INFINITY], color: new beakerx.Color(128, 128, 128, 50)}))\n", + "\n", + "p.display(this);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%javascript\n", + "\n", + "var POSITIVE_INFINITY = 1.0 / 0.0;\n", + "var NEGATIVE_INFINITY = -1.0 / 0.0;\n", + "var p = new beakerx.Plot() \n", + "p.add(new beakerx.Line({x: [-3, 1, 2, 4, 5], y: [4, 2, 6, 1, 5]}))\n", + "p.add(new beakerx.ConstantBand({x: [NEGATIVE_INFINITY, 1], color: new beakerx.Color(128, 128, 128, 50)}))\n", + "p.add(new beakerx.ConstantBand({x: [1, 2]}))\n", + "p.add(new beakerx.ConstantBand({x: [4, POSITIVE_INFINITY]}))\n", + "\n", + "p.display(this);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%javascript\n", + "\n", + "var today = new Date();\n", + "var millis = today.getTime();\n", + "var hour = 1000 * 60 * 60;\n", + "var xs = [];\n", + "var ys = [];\n", + "for(var i=0; i <= 10; i++){\n", + " xs.push(millis + hour * i);\n", + " ys.push(i);\n", + "}\n", + "\n", + "var plot = new beakerx.TimePlot({timeZone: \"America/New_York\"});\n", + "//list of milliseconds\n", + "plot.add(new beakerx.Points({x: xs, y: ys, size: 10, displayName: \"milliseconds\"}));\n", + "\n", + "plot.display(this);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%javascript\n", + "\n", + "var today = new Date()\n", + "var millis = today.getTime()\n", + "var nanos = millis * 1000 * 1000\n", + "var xs = [];\n", + "var ys = [];\n", + "for(var i=0; i <= 10; i++){\n", + " xs.push(new Big(nanos).plus(7 * i).toString());\n", + " ys.push(i);\n", + "}\n", + "var np = new beakerx.NanoPlot()\n", + "np.add(new beakerx.Points({x: xs, y: ys}))\n", + "\n", + "np.display(this);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%javascript\n", + "\n", + "var y1 = [1,5,3,2,3]\n", + "var y2 = [7,2,4,1,3]\n", + "var p = new beakerx.Plot({title: 'Plot with XYStacker', initHeight: 200})\n", + "var a1 = new beakerx.Area({y: y1, displayName: 'y1'})\n", + "var a2 = new beakerx.Area({y: y2, displayName: 'y2'})\n", + "p.add(beakerx.XYStacker.stack([a1, a2]))\n", + "\n", + "p.display(this);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%javascript\n", + "\n", + "var plot = new beakerx.SimpleTimePlot(beakerx.rates, [\"y1\", \"y10\"]);\n", + "\n", + "plot.display(this);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%javascript\n", + "\n", + "var plot = new beakerx.SimpleTimePlot(beakerx.rates, [\"y1\", \"y10\"], // column names\n", + " {timeColumn : \"time\", // time is default value for a timeColumn\n", + " yLabel: \"Price\", \n", + " displayNames: [\"1 Year\", \"10 Year\"],\n", + " colors : [[216, 154, 54], beakerx.Color.lightGray],\n", + " displayLines: false, // no lines (true by default)\n", + " displayPoints: true}) // show points (false by default))\n", + "\n", + "plot.display(this);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%javascript\n", + "\n", + "var lehmanDate = Date.parse(\"2008 Sep 15\")\n", + "var bubbleBottomDate = Date.parse(\"2002 Oct 09\")\n", + "var inversion1 = [Date.parse(\"2000 Jul 22\"), Date.parse(\"2001 Feb 16\")]\n", + "var inversion2 = [Date.parse(\"2006 Aug 01\"), Date.parse(\"2007 Jun 07\")]\n", + "\"ok\"\n", + "// The CombinedPlot allows for stacked plots with linked X axis.\n", + "var c = new beakerx.CombinedPlot({title: \"US Treasuries\", initWidth: 1000})\n", + "var ch = new beakerx.Crosshair({color: beakerx.Color.gray, width: 2, style: beakerx.StrokeType.DOT})\n", + "\n", + "// The top plot has 2 lines.\n", + "var p1 = new beakerx.TimePlot({yLabel: \"Interest Rate\", crosshair: ch})\n", + "p1.add(new beakerx.Line({x: beakerx.time, y: beakerx.m3, displayName: \"3 month\"}))\n", + "p1.add(new beakerx.Line({x: beakerx.time, y: beakerx.y10, displayName: \"10 year\"}))\n", + "\n", + "// The bottom plot has an area filled in.\n", + "var p2 = new beakerx.TimePlot({yLabel: \"Spread\", crosshair: ch})\n", + "p2.add(new beakerx.Area({x: beakerx.time, y: beakerx.spread, color: new beakerx.Color(120, 60, 0)}))\n", + "\n", + "// Highlight the inversion intervals\n", + "var b1 = new beakerx.ConstantBand({x: inversion1, color: new beakerx.Color(240, 100, 100, 55)})\n", + "var b2 = new beakerx.ConstantBand({x: inversion2, color: new beakerx.Color(240, 100, 100, 55)})\n", + "\n", + "// Add notation and line for Lehman Bankruptcy.\n", + "p1.add(new beakerx.Text({x: lehmanDate, y: 7.5, text: \"Lehman Brothers Bankruptcy\"}))\n", + "var l1 = new beakerx.ConstantLine({x: lehmanDate, style: beakerx.StrokeType.DOT, color: beakerx.Color.gray})\n", + "\n", + "// Add notation and line for Stocks Nadir.\n", + "p1.add(new beakerx.Text({x: bubbleBottomDate, y: 5.75, text: \"Stocks Hit Bottom\"}))\n", + "var l2 = new beakerx.ConstantLine({x: bubbleBottomDate, style: beakerx.StrokeType.DOT, color: beakerx.Color.gray})\n", + "\n", + "// add the notations and highlight bands to both plots\n", + "p1.add(l1).add(l2).add(b1).add(b2)\n", + "p2.add(l1).add(l2).add(b1).add(b2)\n", + "\n", + "// add both plots to the combined plot, and including their relative heights.\n", + "c.add(p1, 3)\n", + "c.add(p2, 1)\n", + "\n", + "c.display(this);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": false, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/doc/ScrollZoom.ipynb b/doc/ScrollZoom.ipynb new file mode 100644 index 0000000..e7e84b6 --- /dev/null +++ b/doc/ScrollZoom.ipynb @@ -0,0 +1,102 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Scroll and Zoom\n", + "\n", + "BeakerX's table and plot widgets both support the scroll wheel.\n", + "For tables, the scroll wheel scrolls vertically.\n", + "For plots, the scroll wheel zooms.\n", + "However, for the notebook as a whole, the scroll wheel also has a meaning, to scroll the notebook.\n", + "So there's an ambiguity, and the UI needs a way to resolve it and decide where the scroll events go.\n", + "\n", + "BeakerX's approach starts with idea of *focus*, a widget that would be the target of any user commands.\n", + "And indeed, in BeakerX when you click on a table or a plot, it gets the focus.\n", + "This is represented by a green outline around the widget.\n", + "The outline is styled to match the blue one that Jupyter uses to indicate the current cell.\n", + "\n", + "The widget will keep the focus as long as the mouse remains over it. Feel free to interact with the widget, clicking, scrolling, and zooming away. When you are done and move the mouse elsewhere, the green outline will vanish. Focus is returned to the notebook, and the next scroll event will go to the page, and not to a widget.\n", + "\n", + "This **keyless blur** is a BeakerX innovation.\n", + "\n", + "Try it with the widgets below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "from beakerx_widgets import *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# TableDisplay([{'y1':4, 'm3':2, 'z2':1}, {'m3':4, 'z2':2}])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pd.read_csv('./resources/data/interest-rates.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rng = pd.date_range('1/1/2011', periods=1000, freq='H')\n", + "ts = pd.Series(np.random.randn(len(rng)), index=rng)\n", + "df = pd.DataFrame(ts, columns=['mV'])\n", + "SimpleTimePlot(df, ['mV'])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": false, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/doc/Treemap.ipynb b/doc/Treemap.ipynb new file mode 100644 index 0000000..eee5c43 --- /dev/null +++ b/doc/Treemap.ipynb @@ -0,0 +1,569 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from beakerx_widgets import *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class ToolTipBuilder:\n", + " def getToolTip(self,node):\n", + " tooltip = \"\"\n", + " if node.isLeaf():\n", + " tooltip+=\"Label: \"+ (node.label)\n", + "\n", + " tooltip+=\"
\"\n", + " tooltip+=\"Weight: \"+ str(node.weight)\n", + " tooltip+=\"
\"\n", + " tooltip+=\"Value: \"+ node.labelValue\n", + "\n", + " return tooltip\n", + " \n", + "toolTipBuilder = ToolTipBuilder()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from beakerx_widgets import *\n", + "node = TreeMapNode(\"0\")\n", + "node00 = TreeMapNode(\"00\", 2, DefaultValue(2))\n", + "\n", + "node01 = TreeMapNode(\"01\")\n", + "node01.add(TreeMapNode(\"011\", 1, DefaultValue(1)))\n", + "node01.add(TreeMapNode(\"012\", 2, DefaultValue(1)))\n", + "node01.add(TreeMapNode(\"013\", 3, DefaultValue(1)))\n", + "\n", + "node02 = TreeMapNode(\"02\")\n", + "node02.add(TreeMapNode(\"020\", 2, DefaultValue(2)))\n", + "node02.add(TreeMapNode(\"021\", 1, DefaultValue(1)))\n", + "node02.add(TreeMapNode(\"022\", 1, DefaultValue(1)))\n", + "node02.add(TreeMapNode(\"023\", 1, DefaultValue(1)))\n", + "node02.add(TreeMapNode(\"024\", 2, DefaultValue(2)))\n", + "\n", + "node03 = TreeMapNode(\"03\")\n", + "node03.add(TreeMapNode(\"030\", 1, DefaultValue(2)))\n", + "node03.add(TreeMapNode(\"031\", 2, DefaultValue(2)))\n", + "node03.add(TreeMapNode(\"032\", 3, DefaultValue(2)))\n", + "node03.add(TreeMapNode(\"033\", 4, DefaultValue(2)))\n", + "\n", + "node04 = TreeMapNode(\"04\", 6, DefaultValue(5))\n", + "\n", + "node.add(node01)\n", + "node.add(node02)\n", + "node.add(node03)\n", + "node.add(node04)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "big_node = TreeMapNode(\"root\")\n", + "nodeX = TreeMapNode(\"branch1\")\n", + "nodeY = TreeMapNode(\"branch2\")\n", + "\n", + "for x in range(1, 501):\n", + " nodeX.add(TreeMapNode(str(x), x * 2, DefaultValue(x)))\n", + " \n", + "nodeX1 = TreeMapNode(\"branch11\")\n", + "\n", + "for x in range(1, 251):\n", + " nodeX1.add(TreeMapNode(str(x), x * 2, DefaultValue(x)))\n", + "\n", + "nodeX.add(nodeX1)\n", + "\n", + "for x in range(1, 501):\n", + " nodeY.add(TreeMapNode(str(x), x * 2, DefaultValue(x)))\n", + "\n", + "nodeY1 = TreeMapNode(\"branch21\")\n", + "\n", + "for x in range(1, 251):\n", + " nodeY1.add(TreeMapNode(str(x), x * 2, DefaultValue(x)))\n", + "nodeY.add(nodeY1)\n", + "\n", + "\n", + "big_node.add(nodeY)\n", + "big_node.add(nodeX)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "spjson = '{\"name\":\"flare\",\"children\":[{\"name\":\"analytics\",\"children\":[{\"name\":\"cluster\",\"children\":[{\"name\":\"AgglomerativeCluster\",\"size\":3938},{\"name\":\"CommunityStructure\",\"size\":3812},{\"name\":\"HierarchicalCluster\",\"size\":6714},{\"name\":\"MergeEdge\",\"size\":743}]},{\"name\":\"graph\",\"children\":[{\"name\":\"BetweennessCentrality\",\"size\":3534},{\"name\":\"LinkDistance\",\"size\":5731},{\"name\":\"MaxFlowMinCut\",\"size\":7840},{\"name\":\"ShortestPaths\",\"size\":5914},{\"name\":\"SpanningTree\",\"size\":3416}]},{\"name\":\"optimization\",\"children\":[{\"name\":\"AspectRatioBanker\",\"size\":7074}]}]},{\"name\":\"animate\",\"children\":[{\"name\":\"Easing\",\"size\":17010},{\"name\":\"FunctionSequence\",\"size\":5842},{\"name\":\"interpolate\",\"children\":[{\"name\":\"ArrayInterpolator\",\"size\":1983},{\"name\":\"ColorInterpolator\",\"size\":2047},{\"name\":\"DateInterpolator\",\"size\":1375},{\"name\":\"Interpolator\",\"size\":8746},{\"name\":\"MatrixInterpolator\",\"size\":2202},{\"name\":\"NumberInterpolator\",\"size\":1382},{\"name\":\"ObjectInterpolator\",\"size\":1629},{\"name\":\"PointInterpolator\",\"size\":1675},{\"name\":\"RectangleInterpolator\",\"size\":2042}]},{\"name\":\"ISchedulable\",\"size\":1041},{\"name\":\"Parallel\",\"size\":5176},{\"name\":\"Pause\",\"size\":449},{\"name\":\"Scheduler\",\"size\":5593},{\"name\":\"Sequence\",\"size\":5534},{\"name\":\"Transition\",\"size\":9201},{\"name\":\"Transitioner\",\"size\":19975},{\"name\":\"TransitionEvent\",\"size\":1116},{\"name\":\"Tween\",\"size\":6006}]},{\"name\":\"data\",\"children\":[{\"name\":\"converters\",\"children\":[{\"name\":\"Converters\",\"size\":721},{\"name\":\"DelimitedTextConverter\",\"size\":4294},{\"name\":\"GraphMLConverter\",\"size\":9800},{\"name\":\"IDataConverter\",\"size\":1314},{\"name\":\"JSONConverter\",\"size\":2220}]},{\"name\":\"DataField\",\"size\":1759},{\"name\":\"DataSchema\",\"size\":2165},{\"name\":\"DataSet\",\"size\":586},{\"name\":\"DataSource\",\"size\":3331},{\"name\":\"DataTable\",\"size\":772},{\"name\":\"DataUtil\",\"size\":3322}]},{\"name\":\"display\",\"children\":[{\"name\":\"DirtySprite\",\"size\":8833},{\"name\":\"LineSprite\",\"size\":1732},{\"name\":\"RectSprite\",\"size\":3623},{\"name\":\"TextSprite\",\"size\":10066}]},{\"name\":\"flex\",\"children\":[{\"name\":\"FlareVis\",\"size\":4116}]},{\"name\":\"physics\",\"children\":[{\"name\":\"DragForce\",\"size\":1082},{\"name\":\"GravityForce\",\"size\":1336},{\"name\":\"IForce\",\"size\":319},{\"name\":\"NBodyForce\",\"size\":10498},{\"name\":\"Particle\",\"size\":2822},{\"name\":\"Simulation\",\"size\":9983},{\"name\":\"Spring\",\"size\":2213},{\"name\":\"SpringForce\",\"size\":1681}]},{\"name\":\"query\",\"children\":[{\"name\":\"AggregateExpression\",\"size\":1616},{\"name\":\"And\",\"size\":1027},{\"name\":\"Arithmetic\",\"size\":3891},{\"name\":\"Average\",\"size\":891},{\"name\":\"BinaryExpression\",\"size\":2893},{\"name\":\"Comparison\",\"size\":5103},{\"name\":\"CompositeExpression\",\"size\":3677},{\"name\":\"Count\",\"size\":781},{\"name\":\"DateUtil\",\"size\":4141},{\"name\":\"Distinct\",\"size\":933},{\"name\":\"Expression\",\"size\":5130},{\"name\":\"ExpressionIterator\",\"size\":3617},{\"name\":\"Fn\",\"size\":3240},{\"name\":\"If\",\"size\":2732},{\"name\":\"IsA\",\"size\":2039},{\"name\":\"Literal\",\"size\":1214},{\"name\":\"Match\",\"size\":3748},{\"name\":\"Maximum\",\"size\":843},{\"name\":\"methods\",\"children\":[{\"name\":\"add\",\"size\":593},{\"name\":\"and\",\"size\":330},{\"name\":\"average\",\"size\":287},{\"name\":\"count\",\"size\":277},{\"name\":\"distinct\",\"size\":292},{\"name\":\"div\",\"size\":595},{\"name\":\"eq\",\"size\":594},{\"name\":\"fn\",\"size\":460},{\"name\":\"gt\",\"size\":603},{\"name\":\"gte\",\"size\":625},{\"name\":\"iff\",\"size\":748},{\"name\":\"isa\",\"size\":461},{\"name\":\"lt\",\"size\":597},{\"name\":\"lte\",\"size\":619},{\"name\":\"max\",\"size\":283},{\"name\":\"min\",\"size\":283},{\"name\":\"mod\",\"size\":591},{\"name\":\"mul\",\"size\":603},{\"name\":\"neq\",\"size\":599},{\"name\":\"not\",\"size\":386},{\"name\":\"or\",\"size\":323},{\"name\":\"orderby\",\"size\":307},{\"name\":\"range\",\"size\":772},{\"name\":\"select\",\"size\":296},{\"name\":\"stddev\",\"size\":363},{\"name\":\"sub\",\"size\":600},{\"name\":\"sum\",\"size\":280},{\"name\":\"update\",\"size\":307},{\"name\":\"variance\",\"size\":335},{\"name\":\"where\",\"size\":299},{\"name\":\"xor\",\"size\":354},{\"name\":\"_\",\"size\":264}]},{\"name\":\"Minimum\",\"size\":843},{\"name\":\"Not\",\"size\":1554},{\"name\":\"Or\",\"size\":970},{\"name\":\"Query\",\"size\":13896},{\"name\":\"Range\",\"size\":1594},{\"name\":\"StringUtil\",\"size\":4130},{\"name\":\"Sum\",\"size\":791},{\"name\":\"Variable\",\"size\":1124},{\"name\":\"Variance\",\"size\":1876},{\"name\":\"Xor\",\"size\":1101}]},{\"name\":\"scale\",\"children\":[{\"name\":\"IScaleMap\",\"size\":2105},{\"name\":\"LinearScale\",\"size\":1316},{\"name\":\"LogScale\",\"size\":3151},{\"name\":\"OrdinalScale\",\"size\":3770},{\"name\":\"QuantileScale\",\"size\":2435},{\"name\":\"QuantitativeScale\",\"size\":4839},{\"name\":\"RootScale\",\"size\":1756},{\"name\":\"Scale\",\"size\":4268},{\"name\":\"ScaleType\",\"size\":1821},{\"name\":\"TimeScale\",\"size\":5833}]},{\"name\":\"util\",\"children\":[{\"name\":\"Arrays\",\"size\":8258},{\"name\":\"Colors\",\"size\":10001},{\"name\":\"Dates\",\"size\":8217},{\"name\":\"Displays\",\"size\":12555},{\"name\":\"Filter\",\"size\":2324},{\"name\":\"Geometry\",\"size\":10993},{\"name\":\"heap\",\"children\":[{\"name\":\"FibonacciHeap\",\"size\":9354},{\"name\":\"HeapNode\",\"size\":1233}]},{\"name\":\"IEvaluable\",\"size\":335},{\"name\":\"IPredicate\",\"size\":383},{\"name\":\"IValueProxy\",\"size\":874},{\"name\":\"math\",\"children\":[{\"name\":\"DenseMatrix\",\"size\":3165},{\"name\":\"IMatrix\",\"size\":2815},{\"name\":\"SparseMatrix\",\"size\":3366}]},{\"name\":\"Maths\",\"size\":17705},{\"name\":\"Orientation\",\"size\":1486},{\"name\":\"palette\",\"children\":[{\"name\":\"ColorPalette\",\"size\":6367},{\"name\":\"Palette\",\"size\":1229},{\"name\":\"ShapePalette\",\"size\":2059},{\"name\":\"SizePalette\",\"size\":2291}]},{\"name\":\"Property\",\"size\":5559},{\"name\":\"Shapes\",\"size\":19118},{\"name\":\"Sort\",\"size\":6887},{\"name\":\"Stats\",\"size\":6557},{\"name\":\"Strings\",\"size\":22026}]},{\"name\":\"vis\",\"children\":[{\"name\":\"axis\",\"children\":[{\"name\":\"Axes\",\"size\":1302},{\"name\":\"Axis\",\"size\":24593},{\"name\":\"AxisGridLine\",\"size\":652},{\"name\":\"AxisLabel\",\"size\":636},{\"name\":\"CartesianAxes\",\"size\":6703}]},{\"name\":\"controls\",\"children\":[{\"name\":\"AnchorControl\",\"size\":2138},{\"name\":\"ClickControl\",\"size\":3824},{\"name\":\"Control\",\"size\":1353},{\"name\":\"ControlList\",\"size\":4665},{\"name\":\"DragControl\",\"size\":2649},{\"name\":\"ExpandControl\",\"size\":2832},{\"name\":\"HoverControl\",\"size\":4896},{\"name\":\"IControl\",\"size\":763},{\"name\":\"PanZoomControl\",\"size\":5222},{\"name\":\"SelectionControl\",\"size\":7862},{\"name\":\"TooltipControl\",\"size\":8435}]},{\"name\":\"data\",\"children\":[{\"name\":\"Data\",\"size\":20544},{\"name\":\"DataList\",\"size\":19788},{\"name\":\"DataSprite\",\"size\":10349},{\"name\":\"EdgeSprite\",\"size\":3301},{\"name\":\"NodeSprite\",\"size\":19382},{\"name\":\"render\",\"children\":[{\"name\":\"ArrowType\",\"size\":698},{\"name\":\"EdgeRenderer\",\"size\":5569},{\"name\":\"IRenderer\",\"size\":353},{\"name\":\"ShapeRenderer\",\"size\":2247}]},{\"name\":\"ScaleBinding\",\"size\":11275},{\"name\":\"Tree\",\"size\":7147},{\"name\":\"TreeBuilder\",\"size\":9930}]},{\"name\":\"events\",\"children\":[{\"name\":\"DataEvent\",\"size\":2313},{\"name\":\"SelectionEvent\",\"size\":1880},{\"name\":\"TooltipEvent\",\"size\":1701},{\"name\":\"VisualizationEvent\",\"size\":1117}]},{\"name\":\"legend\",\"children\":[{\"name\":\"Legend\",\"size\":20859},{\"name\":\"LegendItem\",\"size\":4614},{\"name\":\"LegendRange\",\"size\":10530}]},{\"name\":\"operator\",\"children\":[{\"name\":\"distortion\",\"children\":[{\"name\":\"BifocalDistortion\",\"size\":4461},{\"name\":\"Distortion\",\"size\":6314},{\"name\":\"FisheyeDistortion\",\"size\":3444}]},{\"name\":\"encoder\",\"children\":[{\"name\":\"ColorEncoder\",\"size\":3179},{\"name\":\"Encoder\",\"size\":4060},{\"name\":\"PropertyEncoder\",\"size\":4138},{\"name\":\"ShapeEncoder\",\"size\":1690},{\"name\":\"SizeEncoder\",\"size\":1830}]},{\"name\":\"filter\",\"children\":[{\"name\":\"FisheyeTreeFilter\",\"size\":5219},{\"name\":\"GraphDistanceFilter\",\"size\":3165},{\"name\":\"VisibilityFilter\",\"size\":3509}]},{\"name\":\"IOperator\",\"size\":1286},{\"name\":\"label\",\"children\":[{\"name\":\"Labeler\",\"size\":9956},{\"name\":\"RadialLabeler\",\"size\":3899},{\"name\":\"StackedAreaLabeler\",\"size\":3202}]},{\"name\":\"layout\",\"children\":[{\"name\":\"AxisLayout\",\"size\":6725},{\"name\":\"BundledEdgeRouter\",\"size\":3727},{\"name\":\"CircleLayout\",\"size\":9317},{\"name\":\"CirclePackingLayout\",\"size\":12003},{\"name\":\"DendrogramLayout\",\"size\":4853},{\"name\":\"ForceDirectedLayout\",\"size\":8411},{\"name\":\"IcicleTreeLayout\",\"size\":4864},{\"name\":\"IndentedTreeLayout\",\"size\":3174},{\"name\":\"Layout\",\"size\":7881},{\"name\":\"NodeLinkTreeLayout\",\"size\":12870},{\"name\":\"PieLayout\",\"size\":2728},{\"name\":\"RadialTreeLayout\",\"size\":12348},{\"name\":\"RandomLayout\",\"size\":870},{\"name\":\"StackedAreaLayout\",\"size\":9121},{\"name\":\"TreeMapLayout\",\"size\":9191}]},{\"name\":\"Operator\",\"size\":2490},{\"name\":\"OperatorList\",\"size\":5248},{\"name\":\"OperatorSequence\",\"size\":4190},{\"name\":\"OperatorSwitch\",\"size\":2581},{\"name\":\"SortOperator\",\"size\":2023}]},{\"name\":\"Visualization\",\"size\":16540}]}]}'\n", + "aa = json.loads(spjson)\n", + "\n", + "def process(node, parent):\n", + " treeNode = TreeMapNode(node.get('name'))\n", + " if node.get('size') is None:\n", + " treeNode = TreeMapNode(node.get('name'))\n", + " else:\n", + " treeNode = TreeMapNode(node.get('name'), node.get('size'), DefaultValue(node.get('size')))\n", + " \n", + " parent.add(treeNode)\n", + " \n", + " children = node.get('children')\n", + " if children is not None:\n", + " for node in node.get('children'):\n", + " process(node, treeNode)\n", + "\n", + "\n", + "real_node = TreeMapNode(\"root\")\n", + "\n", + "\n", + "process(aa, real_node)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = TreeMap(\n", + " root= node,\n", + " showLegend= True,\n", + " title= \"Simple TreeChart\"\n", + ")\n", + "x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Other Properties" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* mode\n", + "* ratio\n", + "* round\n", + "* sticky\n", + "* valueAccessor" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Mode property" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If mode is specified, sets the layout algorithm. If mode is not specified, returns the current layout algorithm, which defaults to \"squarify\". The following modes are supported:\n", + "\n", + "* squarify - rectangular subdivision; squareness controlled via the target ratio.\n", + "* slice - horizontal subdivision.\n", + "* dice - vertical subdivision.\n", + "* slice-dice - alternating between horizontal and vertical subdivision." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = TreeMap(\n", + " root= node,\n", + " mode= Mode.SQUARIFY,\n", + " title= \"Mode.SQUARIFY\"\n", + ")\n", + "x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = TreeMap(\n", + " root= node,\n", + " mode= Mode.SLICE,\n", + " title= \"Mode.SLICE\"\n", + ")\n", + "x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = TreeMap(\n", + " root= node,\n", + " mode= Mode.DICE,\n", + " title= \"Mode.DICE\"\n", + ")\n", + "x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = TreeMap(\n", + " root= node,\n", + " mode= Mode.SLICE_DIC,\n", + " title= \"Mode.SLICE_DIC\"\n", + ")\n", + "x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sticky property" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If sticky is specified, sets whether or not the treemap layout is \"sticky\": a sticky treemap layout will preserve the relative arrangement of nodes across transitions. The allocation of nodes into squarified horizontal and vertical rows is persisted across updates \n", + "by storing a z attribute on the last element in each row; this allows nodes to be resized smoothly, without shuffling or occlusion that would impede perception of changing values. Note, however, that this results in a suboptimal layout for one of the two states. \n", + "If sticky is not specified, returns whether the treemap layout is sticky.\n", + "\n", + "Implementation note: sticky treemaps cache the array of nodes internally; therefore, it is not possible to reuse the same layout instance on multiple datasets. To reset the cached state when switching datasets with a sticky layout, call sticky(true) again. Since version \n", + "1.25.0, hierarchy layouts no longer copy the input data by default on each invocation, so it may be possible to eliminate caching and make the layout fully stateless." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Round property" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If round is specified, sets whether or not the treemap layout will round to exact pixel boundaries. This can be nice to avoid antialiasing artifacts in SVG. If round is not specified, returns whether the treemap will be rounded." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Ratio property" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If ratio is specified, sets the layout ratio. If ratio is not specified, returns the current layout ratio, which defaults to .5 * (1 + Math.sqrt(5))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = TreeMap(\n", + " root= node,\n", + " ratio= 0.5,\n", + " title= \"Property 'ratio' is 0.5\"\n", + ")\n", + "x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = TreeMap(\n", + " root= node,\n", + " toolTipBuilder= toolTipBuilder,\n", + " colorProvider= RandomColorProvider(),\n", + " valueAccessor= ValueAccessor.VALUE,\n", + " title= \"ValueAccessor.VALUE\"\n", + ")\n", + "x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = TreeMap(\n", + " root= node,\n", + " toolTipBuilder= toolTipBuilder,\n", + " colorProvider= RandomColorProvider(),\n", + " valueAccessor= ValueAccessor.WEIGHT,\n", + " title= \"ValueAccessor.WEIGHT\"\n", + ")\n", + "x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = TreeMap(\n", + " root= node,\n", + " toolTipBuilder= toolTipBuilder,\n", + " title= \"Using custom ToolTipBuilder\"\n", + ")\n", + "x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ColorProviders" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In curent moment we are supporting next ColorProviders\n", + "\n", + "* RandomColorProvider (default)\n", + "* GradientColorProvider\n", + "\n", + "You can set property 'fromValue' for providers thar uses min and max values. If this property is 'true' then value uses for color calculating. Otherwise - wight." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### RandomColorProvider" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = TreeMap(\n", + " root= node,\n", + " toolTipBuilder= toolTipBuilder,\n", + " colorProvider= RandomColorProvider(),\n", + " title= \"Default RandomColorProvider\"\n", + ")\n", + "x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "colours = [\n", + " Color(255, 0, 0),\n", + " Color(0, 255, 0),\n", + " Color(0, 0, 255),\n", + " Color(255, 255, 0),\n", + " Color(255, 0, 255),\n", + " Color(0, 255, 255),\n", + " Color(102, 102, 51),\n", + " Color(255, 51, 153),\n", + " Color(255, 153, 51),\n", + " Color(204, 204, 51),\n", + " Color(205, 102, 204),\n", + " Color(51, 153, 255),\n", + " Color(153, 102, 0)\n", + " ]\n", + "\n", + "colorProvider = RandomColorProvider(colours)\n", + "\n", + "y = TreeMap(\n", + " root= node,\n", + " toolTipBuilder= toolTipBuilder,\n", + " colorProvider= colorProvider,\n", + " title= \"RandomColorProvider with different colours\"\n", + ")\n", + "y" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### GradientColorProvider" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = TreeMap(\n", + " root= node,\n", + " toolTipBuilder= toolTipBuilder,\n", + " title= \"GradientColorProvider with default colours\"\n", + ")\n", + "x.setColorProvider(GradientColorProvider(x))\n", + "x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = TreeMap(\n", + " root= node,\n", + " toolTipBuilder= toolTipBuilder,\n", + " title= \"GradientColorProvider with overrides colours\"\n", + ")\n", + "x.setColorProvider(GradientColorProvider(x, Color.LIGHT_GRAY, Color.DARK_GRAY))\n", + "x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = TreeMap(\n", + " root= big_node,\n", + " showLegend= False,\n", + " toolTipBuilder= toolTipBuilder,\n", + " title= \"Big TreeMap chart (1500 entities)\"\n", + ")\n", + "x.setColorProvider(GradientColorProvider(x))\n", + "x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example from http://bl.ocks.org/mbostock/4063582" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "colorProvider = RandomColorProvider(\n", + " [\n", + " '#3182bd',\n", + " '#6baed6',\n", + " '#9ecae1',\n", + " '#c6dbef',\n", + " '#e6550d',\n", + " '#fd8d3c',\n", + " '#fdae6b',\n", + " '#fdd0a2',\n", + " '#31a354',\n", + " '#74c476',\n", + " '#a1d99b',\n", + " '#c7e9c0',\n", + " '#756bb1',\n", + " '#9e9ac8',\n", + " '#bcbddc',\n", + " '#dadaeb',\n", + " '#636363',\n", + " '#969696',\n", + " '#bdbdbd',\n", + " '#d9d9d9'\n", + " ]\n", + ")\n", + "colorProvider.setGroupByParent(True)\n", + "\n", + "x = TreeMap(\n", + " root= real_node,\n", + " showLegend= False,\n", + " toolTipBuilder= toolTipBuilder,\n", + " colorProvider = colorProvider,\n", + " title= \"Flare\",\n", + " initWidth= 1300,\n", + " initHeight= 600\n", + ")\n", + "x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": false, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/doc/resources/data/interest-rates.csv b/doc/resources/data/interest-rates.csv new file mode 100644 index 0000000..993e781 --- /dev/null +++ b/doc/resources/data/interest-rates.csv @@ -0,0 +1,314 @@ +"m3","y30","y1","m6","y2","y10","y3","time","y5","y7","spread" +"7.8981","8.2586","7.9210","7.9562","8.0852","8.2067","8.1324","1990-01-30 19:00:00.000 -0500","8.1195","8.1962","0.3086" +"8.0021","8.5037","8.1111","8.1211","8.3705","8.4732","8.3868","1990-02-27 19:00:00.000 -0500","8.4247","8.4758","0.4711" +"8.1700","8.5632","8.3500","8.2800","8.6268","8.5886","8.6273","1990-03-30 19:00:00.000 -0500","8.6005","8.6482","0.4186" +"8.0405","8.7560","8.4045","8.2700","8.7240","8.7855","8.7825","1990-04-29 20:00:00.000 -0400","8.7680","8.8130","0.7450" +"8.0068","8.7314","8.3164","8.1909","8.6423","8.7582","8.6923","1990-05-30 20:00:00.000 -0400","8.7359","8.7836","0.7514" +"7.9867","8.4576","8.0962","8.0452","8.3510","8.4800","8.3981","1990-06-29 20:00:00.000 -0400","8.4305","8.5176","0.4933" +"7.8748","8.4981","7.9410","7.9233","8.1571","8.4714","8.2648","1990-07-30 20:00:00.000 -0400","8.3310","8.4552","0.5967" +"7.6943","8.8635","7.7791","7.7661","8.0613","8.7526","8.2187","1990-08-30 20:00:00.000 -0400","8.4365","8.6439","1.0583" +"7.5979","9.0289","7.7632","7.6984","8.0795","8.8932","8.2674","1990-09-29 20:00:00.000 -0400","8.5137","8.7868","1.2953" +"7.4000","8.8577","7.5514","7.5295","7.8777","8.7195","8.0686","1990-10-30 19:00:00.000 -0500","8.3277","8.5936","1.3195" +"7.2905","8.5405","7.3135","7.3850","7.5995","8.3920","7.7370","1990-11-29 19:00:00.000 -0500","8.0225","8.2765","1.1015" +"6.9495","8.2370","7.0505","7.0265","7.3140","8.0750","7.4660","1990-12-30 19:00:00.000 -0500","7.7265","8.0005","1.1255" +"6.4105","8.2695","6.6443","6.5757","7.1252","8.0919","7.3776","1991-01-30 19:00:00.000 -0500","7.7000","7.9705","1.6814" +"6.1163","8.0342","6.2668","6.1947","6.8668","7.8547","7.0774","1991-02-27 19:00:00.000 -0500","7.4726","7.7326","1.7384" +"6.0935","8.2880","6.3960","6.1985","7.1030","8.1100","7.3535","1991-03-30 19:00:00.000 -0500","7.7720","8.0025","2.0165" +"5.8255","8.2095","6.2359","5.9768","6.9482","8.0391","7.2318","1991-04-29 20:00:00.000 -0400","7.7009","7.9227","2.2136" +"5.6336","8.2673","6.1305","5.8732","6.7836","8.0677","7.1168","1991-05-30 20:00:00.000 -0400","7.7014","7.9423","2.4341" +"5.7510","8.4720","6.3585","6.0240","6.9560","8.2840","7.3915","1991-06-29 20:00:00.000 -0400","7.9370","8.1710","2.5330" +"5.7509","8.4523","6.3055","5.9745","6.9177","8.2727","7.3759","1991-07-30 20:00:00.000 -0400","7.9114","8.1468","2.5218" +"5.4977","8.1436","5.7782","5.6336","6.4332","7.9000","6.7973","1991-08-30 20:00:00.000 -0400","7.4250","7.7400","2.4023" +"5.3735","7.9480","5.5725","5.4790","6.1815","7.6500","6.5000","1991-09-29 20:00:00.000 -0400","7.1360","7.4765","2.2765" +"5.1441","7.9305","5.3336","5.2591","5.9123","7.5273","6.2291","1991-10-30 19:00:00.000 -0500","6.8714","7.2464","2.3832" +"4.6895","7.9216","4.8895","4.8011","5.5589","7.4174","5.9037","1991-11-29 19:00:00.000 -0500","6.6179","7.0589","2.7279" +"4.1843","7.7019","4.3781","4.2614","5.0257","7.0886","5.3929","1991-12-30 19:00:00.000 -0500","6.1862","6.6852","2.9043" +"3.9062","7.5819","4.1510","4.0100","4.9581","7.0324","5.3957","1992-01-30 19:00:00.000 -0500","6.2429","6.7048","3.1262" +"3.9500","7.8547","4.2874","4.0763","5.2121","7.3379","5.7200","1992-02-28 19:00:00.000 -0500","6.5779","6.9621","3.3879" +"4.1386","7.9695","4.6345","4.3309","5.6850","7.5423","6.1832","1992-03-30 19:00:00.000 -0500","6.9455","7.2573","3.4036" +"3.8390","7.9624","4.2990","4.0029","5.3443","7.4805","5.9252","1992-04-29 20:00:00.000 -0400","6.7843","7.1529","3.6414" +"3.7190","7.8910","4.1895","3.8815","5.2255","7.3920","5.8135","1992-05-30 20:00:00.000 -0400","6.6925","7.0615","3.6730" +"3.7450","7.8418","4.1659","3.8950","5.0482","7.2618","5.5973","1992-06-29 20:00:00.000 -0400","6.4827","6.9045","3.5168" +"3.2795","7.5982","3.5959","3.3832","4.3555","6.8445","4.9064","1992-07-30 20:00:00.000 -0400","5.8368","6.3573","3.5650" +"3.1990","7.3905","3.4719","3.3110","4.1929","6.5857","4.7248","1992-08-30 20:00:00.000 -0400","5.5962","6.1219","3.3867" +"2.9686","7.3410","3.1843","3.0419","3.8943","6.4152","4.4152","1992-09-29 20:00:00.000 -0400","5.3800","5.9610","3.4467" +"2.9329","7.5319","3.3024","3.1276","4.0829","6.5890","4.6381","1992-10-30 19:00:00.000 -0500","5.6005","6.1548","3.6562" +"3.2058","7.6068","3.6821","3.4395","4.5795","6.8732","5.1405","1992-11-29 19:00:00.000 -0500","6.0379","6.4900","3.6674" +"3.2914","7.4364","3.7114","3.4686","4.6736","6.7700","5.2136","1992-12-30 19:00:00.000 -0500","6.0759","6.4568","3.4786" +"3.0711","7.3416","3.4963","3.2379","4.3900","6.6000","4.9326","1993-01-30 19:00:00.000 -0500","5.8337","6.2600","3.5289" +"2.9926","7.0895","3.3858","3.1605","4.0984","6.2589","4.5789","1993-02-27 19:00:00.000 -0500","5.4289","5.8689","3.2663" +"3.0143","6.8217","3.3330","3.1457","3.9496","5.9752","4.4013","1993-03-30 19:00:00.000 -0500","5.1948","5.6557","2.9609" +"2.9305","6.8543","3.2443","3.0629","3.8376","5.9695","4.3024","1993-04-29 20:00:00.000 -0400","5.1329","5.5938","3.0390" +"3.0250","6.9190","3.3635","3.1655","3.9770","6.0355","4.4045","1993-05-30 20:00:00.000 -0400","5.1980","5.6590","3.0105" +"3.1445","6.8073","3.5364","3.2945","4.1636","5.9627","4.5309","1993-06-29 20:00:00.000 -0400","5.2168","5.6118","2.8182" +"3.1110","6.6257","3.4748","3.2576","4.0719","5.8052","4.4295","1993-07-30 20:00:00.000 -0400","5.0910","5.4762","2.6943" +"3.0905","6.3232","3.4436","3.2355","4.0014","5.6777","4.3636","1993-08-30 20:00:00.000 -0400","5.0250","5.3532","2.5873" +"3.0086","5.9971","3.3562","3.1510","3.8471","5.3600","4.1652","1993-09-29 20:00:00.000 -0400","4.7348","5.0762","2.3514" +"3.0920","5.9390","3.3920","3.2190","3.8745","5.3340","4.1750","1993-10-30 20:00:00.000 -0400","4.7070","5.0540","2.2420" +"3.1795","6.2100","3.5790","3.3615","4.1555","5.7240","4.4980","1993-11-29 19:00:00.000 -0500","5.0630","5.4470","2.5445" +"3.1277","6.2518","3.6073","3.3355","4.2123","5.7741","4.5400","1993-12-30 19:00:00.000 -0500","5.1468","5.4841","2.6464" +"3.0445","6.2910","3.5425","3.2530","4.1400","5.7505","4.4790","1994-01-30 19:00:00.000 -0500","5.0870","5.4310","2.7060" +"3.3305","6.4905","3.8658","3.5347","4.4742","5.9732","4.8321","1994-02-27 19:00:00.000 -0500","5.3953","5.7195","2.6426" +"3.5909","6.9065","4.3191","3.9148","4.9952","6.4826","5.3957","1994-03-30 19:00:00.000 -0500","5.9413","6.2800","2.8917" +"3.7805","7.2689","4.8158","4.2474","5.5495","6.9721","5.9942","1994-04-29 20:00:00.000 -0400","6.5242","6.8042","3.1916" +"4.2676","7.4124","5.3143","4.7857","5.9681","7.1833","6.3362","1994-05-30 20:00:00.000 -0400","6.7800","7.0062","2.9157" +"4.2491","7.3950","5.2673","4.7195","5.9314","7.1014","6.2682","1994-06-29 20:00:00.000 -0400","6.6955","6.9068","2.8523" +"4.4565","7.5790","5.4765","4.9535","6.1300","7.2980","6.4760","1994-07-30 20:00:00.000 -0400","6.9095","7.1245","2.8415" +"4.6117","7.4861","5.5622","5.0817","6.1774","7.2361","6.4957","1994-08-30 20:00:00.000 -0400","6.8778","7.0622","2.6243" +"4.7514","7.7124","5.7629","5.2400","6.3924","7.4571","6.6929","1994-09-29 20:00:00.000 -0400","7.0833","7.2833","2.7057" +"5.1020","7.9350","6.1145","5.6210","6.7285","7.7440","7.0415","1994-10-30 19:00:00.000 -0500","7.4030","7.5815","2.6420" +"5.4490","8.0815","6.5400","5.9750","7.1485","7.9550","7.4400","1994-11-29 19:00:00.000 -0500","7.7175","7.8300","2.5060" +"5.7648","7.8714","7.1362","6.5048","7.5895","7.8138","7.7095","1994-12-30 19:00:00.000 -0500","7.7762","7.8014","2.0490" +"5.9015","7.8465","7.0505","6.5060","7.5070","7.7795","7.6625","1995-01-30 19:00:00.000 -0500","7.7560","7.7915","1.8780" +"5.9395","7.6116","6.6968","6.3084","7.1116","7.4695","7.2474","1995-02-27 19:00:00.000 -0500","7.3658","7.4405","1.5300" +"5.9104","7.4478","6.4317","6.1726","6.7757","7.2048","6.8922","1995-03-30 19:00:00.000 -0500","7.0474","7.1426","1.2943" +"5.8379","7.3605","6.2658","6.0463","6.5679","7.0626","6.6842","1995-04-29 20:00:00.000 -0400","6.8600","6.9511","1.2247" +"5.8477","6.9500","5.9977","5.9305","6.1709","6.6327","6.2686","1995-05-30 20:00:00.000 -0400","6.4145","6.5032","0.7850" +"5.6391","6.5732","5.6423","5.6618","5.7159","6.1682","5.7959","1995-06-29 20:00:00.000 -0400","5.9286","6.0473","0.5291" +"5.5930","6.7210","5.5940","5.6185","5.7800","6.2780","5.8855","1995-07-30 20:00:00.000 -0400","6.0085","6.1990","0.6850" +"5.5657","6.8604","5.7526","5.6504","5.9809","6.4883","6.1026","1995-08-30 20:00:00.000 -0400","6.2439","6.4117","0.9226" +"5.4340","6.5490","5.6200","5.5370","5.8060","6.1975","5.8910","1995-09-29 20:00:00.000 -0400","6.0015","6.1315","0.7635" +"5.4443","6.3748","5.5905","5.5648","5.6971","6.0448","5.7719","1995-10-30 19:00:00.000 -0500","5.8629","5.9671","0.6005" +"5.5181","6.2629","5.4329","5.5057","5.4786","5.9305","5.5695","1995-11-29 19:00:00.000 -0500","5.6943","5.8300","0.4124" +"5.2940","6.0625","5.3070","5.3490","5.3245","5.7115","5.3865","1995-12-30 19:00:00.000 -0500","5.5135","5.6315","0.4175" +"5.1505","6.0519","5.0876","5.1329","5.1124","5.6524","5.2048","1996-01-30 19:00:00.000 -0500","5.3624","5.5390","0.5019" +"4.9645","6.2420","4.9420","4.9665","5.0255","5.8055","5.1430","1996-02-28 19:00:00.000 -0500","5.3795","5.6380","0.8410" +"5.0976","6.6043","5.3424","5.1586","5.6610","6.2686","5.7857","1996-03-30 19:00:00.000 -0500","5.9662","6.1895","1.1710" +"5.0877","6.7941","5.5377","5.2677","5.9573","6.5114","6.1077","1996-04-29 20:00:00.000 -0400","6.3009","6.4800","1.4236" +"5.1536","6.9277","5.6368","5.3309","6.0950","6.7368","6.2714","1996-05-30 20:00:00.000 -0400","6.4832","6.6568","1.5832" +"5.2315","7.0590","5.8100","5.4620","6.2970","6.9120","6.4850","1996-06-29 20:00:00.000 -0400","6.6895","6.8340","1.6805" +"5.2982","7.0332","5.8509","5.5241","6.2700","6.8655","6.4536","1996-07-30 20:00:00.000 -0400","6.6359","6.7582","1.5673" +"5.1891","6.8418","5.6668","5.3414","6.0291","6.6355","6.2064","1996-08-30 20:00:00.000 -0400","6.3891","6.5150","1.4464" +"5.2375","7.0260","5.8330","5.4535","6.2340","6.8320","6.4075","1996-09-29 20:00:00.000 -0400","6.5970","6.7300","1.5945" +"5.1232","6.8086","5.5500","5.3164","5.9127","6.5336","6.0786","1996-10-30 19:00:00.000 -0500","6.2705","6.4200","1.4105" +"5.1695","6.4816","5.4232","5.2737","5.7016","6.2037","5.8184","1996-11-29 19:00:00.000 -0500","5.9732","6.0974","1.0342" +"5.0405","6.5524","5.4719","5.2433","5.7819","6.3024","5.9067","1996-12-30 19:00:00.000 -0500","6.0681","6.1990","1.2619" +"5.1657","6.8267","5.6124","5.3076","6.0081","6.5790","6.1557","1997-01-30 19:00:00.000 -0500","6.3333","6.4714","1.4133" +"5.1421","6.6879","5.5253","5.2653","5.8984","6.4195","6.0326","1997-02-27 19:00:00.000 -0500","6.1989","6.3189","1.2774" +"5.2845","6.9325","5.7955","5.4790","6.2220","6.6945","6.3785","1997-03-30 19:00:00.000 -0500","6.5370","6.6535","1.4100" +"5.3045","7.0927","5.9882","5.5950","6.4482","6.8855","6.6050","1997-04-29 20:00:00.000 -0400","6.7591","6.8600","1.5809" +"5.1967","6.9357","5.8695","5.5276","6.2767","6.7110","6.4214","1997-05-30 20:00:00.000 -0400","6.5676","6.6586","1.5143" +"5.0710","6.7724","5.6919","5.3386","6.0948","6.4938","6.2371","1997-06-29 20:00:00.000 -0400","6.3752","6.4581","1.4229" +"5.1945","6.5100","5.5418","5.3295","5.8909","6.2205","6.0014","1997-07-30 20:00:00.000 -0400","6.1209","6.2018","1.0259" +"5.2795","6.5786","5.5648","5.4014","5.9390","6.2986","6.0595","1997-08-30 20:00:00.000 -0400","6.1600","6.2905","1.0190" +"5.0848","6.4952","5.5238","5.2957","5.8819","6.2086","5.9757","1997-09-29 20:00:00.000 -0400","6.1062","6.2010","1.1238" +"5.1127","6.3264","5.4564","5.2991","5.7718","6.0295","5.8391","1997-10-30 19:00:00.000 -0500","5.9282","6.0541","0.9168" +"5.2811","6.1094","5.4572","5.3767","5.7128","5.8750","5.7550","1997-11-29 19:00:00.000 -0500","5.8039","5.9017","0.5939" +"5.3045","5.9900","5.5250","5.4514","5.7150","5.8086","5.7418","1997-12-30 19:00:00.000 -0500","5.7732","5.8250","0.5041" +"5.1790","5.8110","5.2445","5.2305","5.3570","5.5445","5.3750","1998-01-30 19:00:00.000 -0500","5.4160","5.5305","0.3655" +"5.2300","5.8911","5.3084","5.2742","5.4179","5.5747","5.4342","1998-02-27 19:00:00.000 -0500","5.4926","5.6032","0.3447" +"5.1618","5.9482","5.3895","5.2482","5.5614","5.6473","5.5668","1998-03-30 19:00:00.000 -0500","5.6118","5.7091","0.4855" +"5.0829","5.9238","5.3790","5.2629","5.5643","5.6376","5.5810","1998-04-29 20:00:00.000 -0400","5.6119","5.6957","0.5548" +"5.1400","5.9265","5.4400","5.3565","5.5935","5.6525","5.6075","1998-05-30 20:00:00.000 -0400","5.6280","5.7185","0.5125" +"5.1241","5.7036","5.4086","5.3236","5.5200","5.4964","5.5186","1998-06-29 20:00:00.000 -0400","5.5241","5.5632","0.3723" +"5.0945","5.6768","5.3582","5.2277","5.4582","5.4614","5.4664","1998-07-30 20:00:00.000 -0400","5.4614","5.5209","0.3668" +"5.0410","5.5405","5.2052","5.1519","5.2686","5.3419","5.2390","1998-08-30 20:00:00.000 -0400","5.2748","5.3586","0.3010" +"4.7390","5.2048","4.7110","4.8105","4.6662","4.8067","4.6157","1998-09-29 20:00:00.000 -0400","4.6238","4.7619","0.0676" +"4.0705","5.0105","4.1214","4.1962","4.0919","4.5300","4.1790","1998-10-30 19:00:00.000 -0500","4.1843","4.4576","0.4595" +"4.5311","5.2484","4.5253","4.5868","4.5400","4.8274","4.5721","1998-11-29 19:00:00.000 -0500","4.5374","4.7758","0.2963" +"4.4968","5.0591","4.5186","4.5682","4.5064","4.6450","4.4845","1998-12-30 19:00:00.000 -0500","4.4500","4.6491","0.1482" +"4.4463","5.1579","4.5142","4.4884","4.6153","4.7221","4.6116","1999-01-30 19:00:00.000 -0500","4.6005","4.7953","0.2758" +"4.5553","5.3653","4.7026","4.6074","4.8763","4.9989","4.9005","1999-02-27 19:00:00.000 -0500","4.9147","5.0984","0.4437" +"4.5665","5.5804","4.7813","4.6465","5.0530","5.2326","5.1057","1999-03-30 19:00:00.000 -0500","5.1404","5.3600","0.6661" +"4.4091","5.5477","4.6900","4.5445","4.9768","5.1845","5.0323","1999-04-29 20:00:00.000 -0400","5.0795","5.2800","0.7755" +"4.6310","5.8055","4.8490","4.7460","5.2545","5.5395","5.3330","1999-05-30 20:00:00.000 -0400","5.4370","5.6445","0.9085" +"4.7155","6.0418","5.0968","5.0295","5.6186","5.8995","5.6973","1999-06-29 20:00:00.000 -0400","5.8091","6.0477","1.1841" +"4.6857","5.9833","5.0319","4.7538","5.5548","5.7919","5.6181","1999-07-30 20:00:00.000 -0400","5.6786","5.9443","1.1062" +"4.8736","6.0686","5.1977","5.0882","5.6782","5.9391","5.7664","1999-08-30 20:00:00.000 -0400","5.8414","6.1509","1.0655" +"4.8224","6.0714","5.2514","5.0843","5.6619","5.9152","5.7457","1999-09-29 20:00:00.000 -0400","5.8019","6.1190","1.0929" +"5.0185","6.2635","5.4290","5.2005","5.8625","6.1120","5.9425","1999-10-30 20:00:00.000 -0400","6.0335","6.3295","1.0935" +"5.2275","6.1450","5.5535","5.4280","5.8620","6.0340","5.9185","1999-11-29 19:00:00.000 -0500","5.9690","6.1730","0.8065" +"5.3568","6.3514","5.8423","5.6841","6.1045","6.2755","6.1427","1999-12-30 19:00:00.000 -0500","6.1864","6.3768","0.9186" +"5.4990","6.6255","6.1215","5.7595","6.4400","6.6610","6.4890","2000-01-30 19:00:00.000 -0500","6.5795","6.7025","1.1620" +"5.7270","6.2320","6.2180","5.9955","6.6105","6.5195","6.6525","2000-02-28 19:00:00.000 -0500","6.6780","6.7165","0.7925" +"5.8639","6.0535","6.2222","6.1135","6.5283","6.2565","6.5283","2000-03-30 19:00:00.000 -0500","6.5039","6.5065","0.3926" +"5.8216","5.8463","6.1505","6.0695","6.4037","5.9905","6.3563","2000-04-29 20:00:00.000 -0400","6.2626","6.2658","0.1689" +"5.9945","6.1486","6.3264","6.3891","6.8095","6.4405","6.7677","2000-05-30 20:00:00.000 -0400","6.6877","6.6895","0.4459" +"5.8618","5.9264","6.1727","6.2386","6.4818","6.0973","6.4264","2000-06-29 20:00:00.000 -0400","6.3009","6.3268","0.2355" +"6.1425","5.8510","6.0825","6.2735","6.3390","6.0540","6.2770","2000-07-30 20:00:00.000 -0400","6.1790","6.2235","-0.0885" +"6.2774","5.7161","6.1830","6.3548","6.2287","5.8261","6.1743","2000-08-30 20:00:00.000 -0400","6.0609","6.0504","-0.4513" +"6.1750","5.8265","6.1255","6.2495","6.0815","5.7990","6.0165","2000-09-29 20:00:00.000 -0400","5.9345","5.9805","-0.3760" +"6.2948","5.8033","6.0124","6.3157","5.9124","5.7386","5.8457","2000-10-30 19:00:00.000 -0500","5.7829","5.8371","-0.5562" +"6.3562","5.7757","6.0919","6.3390","5.8752","5.7171","5.7867","2000-11-29 19:00:00.000 -0500","5.6976","5.7814","-0.6390" +"5.9370","5.4905","5.6025","5.9230","5.3520","5.2405","5.2595","2000-12-30 19:00:00.000 -0500","5.1680","5.2830","-0.6965" +"5.2852","5.5410","4.8148","5.1462","4.7600","5.1610","4.7743","2001-01-30 19:00:00.000 -0500","4.8586","5.1305","-0.1243" +"5.0053","5.4547","4.6842","4.8889","4.6568","5.0989","4.7079","2001-02-27 19:00:00.000 -0500","4.8863","5.0974","0.0937" +"4.5355","5.3395","4.2986","4.4355","4.3423","4.8855","4.4282","2001-03-30 19:00:00.000 -0500","4.6427","4.8827","0.3500" +"3.9660","5.6460","3.9765","3.9855","4.2340","5.1410","4.4230","2001-04-29 20:00:00.000 -0400","4.7635","5.0345","1.1750" +"3.7032","5.7800","3.7814","3.7382","4.2600","5.3914","4.5073","2001-05-30 20:00:00.000 -0400","4.9277","5.2400","1.6882" +"3.5652","5.6695","3.5762","3.5590","4.0800","5.2843","4.3481","2001-06-29 20:00:00.000 -0400","4.8071","5.1381","1.7190" +"3.5933","5.6133","3.6171","3.5567","4.0386","5.2362","4.3133","2001-07-30 20:00:00.000 -0400","4.7619","5.0610","1.6429" +"3.4378","5.4835","3.4704","3.3865","3.7574","4.9713","4.0378","2001-08-30 20:00:00.000 -0400","4.5739","4.8435","1.5335" +"2.6924","5.4829","2.8247","2.7059","3.1188","4.7318","3.4506","2001-09-29 20:00:00.000 -0400","4.1153","4.5124","2.0394" +"2.1977","5.3155","2.3305","2.1709","2.7259","4.5668","3.1368","2001-10-30 19:00:00.000 -0500","3.9100","4.3073","2.3691" +"1.9065","5.1180","2.1820","1.9240","2.7825","4.6515","3.2235","2001-11-29 19:00:00.000 -0500","3.9725","4.4220","2.7450" +"1.7200","5.4800","2.2155","1.8155","3.1070","5.0875","3.6245","2001-12-30 19:00:00.000 -0500","4.3895","4.8590","3.3675" +"1.6848","5.4452","2.1586","1.7710","3.0281","5.0357","3.5567","2002-01-30 19:00:00.000 -0500","4.3400","4.7895","3.3510" +"1.7568","5.4009","2.2326","1.8637","3.0153","4.9116","3.5463","2002-02-27 19:00:00.000 -0500","4.2984","4.7147","3.1547" +"1.8250","NaN","2.5670","2.0565","3.5575","5.2840","4.1420","2002-03-30 19:00:00.000 -0500","4.7380","5.1365","3.4590" +"1.7450","NaN","2.4759","1.9750","3.4227","5.2109","4.0073","2002-04-29 20:00:00.000 -0400","4.6468","5.0164","3.4659" +"1.7605","NaN","2.3541","1.9114","3.2641","5.1645","3.8018","2002-05-30 20:00:00.000 -0400","4.4945","4.9005","3.4041" +"1.7320","NaN","2.1965","1.8325","2.9930","4.9265","3.4850","2002-06-29 20:00:00.000 -0400","4.1860","4.6010","3.1945" +"1.7136","NaN","1.9609","1.7364","2.5568","4.6532","3.0109","2002-07-30 20:00:00.000 -0400","3.8073","4.3041","2.9395" +"1.6473","NaN","1.7573","1.6373","2.1336","4.2573","2.5223","2002-08-30 20:00:00.000 -0400","3.2945","3.8750","2.6100" +"1.6590","NaN","1.7150","1.6385","2.0045","3.8700","2.3170","2002-09-29 20:00:00.000 -0400","2.9380","3.4960","2.2110" +"1.6100","NaN","1.6500","1.5895","1.9118","3.9409","2.2536","2002-10-30 19:00:00.000 -0500","2.9450","3.5368","2.3309" +"1.2547","NaN","1.4916","1.2979","1.9232","4.0484","2.3153","2002-11-29 19:00:00.000 -0500","3.0547","3.6442","2.7937" +"1.2110","NaN","1.4500","1.2676","1.8362","4.0324","2.2348","2002-12-30 19:00:00.000 -0500","3.0333","3.6267","2.8214" +"1.1890","NaN","1.3643","1.2219","1.7433","4.0486","2.1824","2003-01-30 19:00:00.000 -0500","3.0524","3.6019","2.8595" +"1.1853","NaN","1.2963","1.1953","1.6279","3.9026","2.0505","2003-02-27 19:00:00.000 -0500","2.8979","3.4479","2.7174" +"1.1510","NaN","1.2400","1.1567","1.5738","3.8071","1.9752","2003-03-30 19:00:00.000 -0500","2.7838","3.3448","2.6562" +"1.1524","NaN","1.2671","1.1690","1.6224","3.9586","2.0590","2003-04-29 20:00:00.000 -0400","2.9286","3.4743","2.8062" +"1.0881","NaN","1.1814","1.1062","1.4152","3.5690","1.7476","2003-05-30 20:00:00.000 -0400","2.5157","3.0714","2.4810" +"0.9362","NaN","1.0095","0.9433","1.2271","3.3343","1.5143","2003-06-29 20:00:00.000 -0400","2.2657","2.8395","2.3981" +"0.9205","NaN","1.1164","0.9727","1.4741","3.9755","1.9327","2003-07-30 20:00:00.000 -0400","2.8723","3.4532","3.0550" +"0.9690","NaN","1.3086","1.0524","1.8643","4.4452","2.4352","2003-08-30 20:00:00.000 -0400","3.3700","3.9624","3.4762" +"0.9552","NaN","1.2376","1.0290","1.7062","4.2743","2.2333","2003-09-29 20:00:00.000 -0400","3.1848","3.7443","3.3190" +"0.9418","NaN","1.2541","1.0195","1.7477","4.2905","2.2636","2003-10-30 19:00:00.000 -0500","3.1859","3.7514","3.3486" +"0.9522","NaN","1.3367","1.0422","1.9267","4.3000","2.4506","2003-11-29 19:00:00.000 -0500","3.2872","3.8083","3.3478" +"0.9145","NaN","1.3059","1.0109","1.9073","4.2677","2.4400","2003-12-30 19:00:00.000 -0500","3.2686","3.7905","3.3532" +"0.9005","NaN","1.2395","0.9900","1.7610","4.1505","2.2710","2004-01-30 19:00:00.000 -0500","3.1215","3.6465","3.2500" +"0.9447","NaN","1.2437","1.0111","1.7400","4.0842","2.2468","2004-02-28 19:00:00.000 -0500","3.0679","3.5853","3.1395" +"0.9535","NaN","1.1874","1.0091","1.5778","3.8265","2.0000","2004-03-30 19:00:00.000 -0500","2.7870","3.3061","2.8730" +"0.9581","NaN","1.4338","1.1090","2.0710","4.3476","2.5681","2004-04-29 20:00:00.000 -0400","3.3895","3.8924","3.3895" +"1.0395","NaN","1.7775","1.3335","2.5335","4.7155","3.0975","2004-05-30 20:00:00.000 -0400","3.8505","4.3130","3.6760" +"1.2881","NaN","2.1152","1.6352","2.7633","4.7338","3.2562","2004-06-29 20:00:00.000 -0400","3.9290","4.3524","3.4457" +"1.3576","NaN","2.0952","1.6962","2.6386","4.4981","3.0462","2004-07-30 20:00:00.000 -0400","3.6886","4.1119","3.1405" +"1.5032","NaN","2.0159","1.7600","2.5082","4.2814","2.8845","2004-08-30 20:00:00.000 -0400","3.4745","3.9041","2.7782" +"1.6814","NaN","2.1167","1.9105","2.5252","4.1257","2.8295","2004-09-29 20:00:00.000 -0400","3.3552","3.7514","2.4443" +"1.7945","NaN","2.2280","2.0515","2.5845","4.0970","2.8535","2004-10-30 20:00:00.000 -0400","3.3475","3.7480","2.3025" +"2.1075","NaN","2.5000","2.3245","2.8520","4.1940","3.0935","2004-11-29 19:00:00.000 -0500","3.5250","3.8820","2.0865" +"2.2227","NaN","2.6705","2.4968","3.0118","4.2309","3.2141","2004-12-30 19:00:00.000 -0500","3.5982","3.9277","2.0082" +"2.3710","NaN","2.8605","2.6755","3.2225","4.2215","3.3900","2005-01-30 19:00:00.000 -0500","3.7070","3.9735","1.8505" +"2.5795","NaN","3.0295","2.8458","3.3847","4.1653","3.5353","2005-02-27 19:00:00.000 -0500","3.7663","3.9658","1.5858" +"2.7959","NaN","3.3023","3.0868","3.7268","4.4977","3.9132","2005-03-30 19:00:00.000 -0500","4.1655","4.3277","1.7018" +"2.8367","NaN","3.3167","3.1443","3.6538","4.3410","3.7895","2005-04-29 20:00:00.000 -0400","3.9986","4.1581","1.5043" +"2.9019","NaN","3.3310","3.1710","3.6438","4.1443","3.7248","2005-05-30 20:00:00.000 -0400","3.8529","3.9414","1.2424" +"3.0364","NaN","3.3632","3.2218","3.6405","3.9982","3.6850","2005-06-29 20:00:00.000 -0400","3.7723","3.8605","0.9618" +"3.2875","NaN","3.6410","3.5265","3.8710","4.1775","3.9140","2005-07-30 20:00:00.000 -0400","3.9790","4.0580","0.8900" +"3.5183","NaN","3.8722","3.7848","4.0443","4.2626","4.0787","2005-08-30 20:00:00.000 -0400","4.1222","4.1778","0.7443" +"3.4943","NaN","3.8452","3.7938","3.9462","4.1990","3.9629","2005-09-29 20:00:00.000 -0400","4.0062","4.0805","0.7048" +"3.7925","NaN","4.1755","4.1250","4.2710","4.4635","4.2905","2005-10-30 19:00:00.000 -0500","4.3285","4.3820","0.6710" +"3.9745","NaN","4.3340","4.2950","4.4165","4.5350","4.4335","2005-11-29 19:00:00.000 -0500","4.4525","4.4820","0.5605" +"3.9710","NaN","4.3529","4.3276","4.4043","4.4671","4.3919","2005-12-30 19:00:00.000 -0500","4.3924","4.4138","0.4962" +"4.3360","NaN","4.4450","4.4695","4.3955","4.4160","4.3515","2006-01-30 19:00:00.000 -0500","4.3455","4.3650","0.0800" +"4.5395","4.5369","4.6847","4.6916","4.6684","4.5689","4.6374","2006-02-27 19:00:00.000 -0500","4.5721","4.5642","0.0295" +"4.6278","4.7343","4.7735","4.7922","4.7339","4.7239","4.7383","2006-03-30 19:00:00.000 -0500","4.7161","4.7139","0.0961" +"4.7216","5.0626","4.8974","4.9000","4.8889","4.9905","4.8853","2006-04-29 20:00:00.000 -0400","4.9021","4.9353","0.2689" +"4.8364","5.2014","4.9950","5.0100","4.9682","5.1100","4.9745","2006-05-30 20:00:00.000 -0400","4.9977","5.0323","0.2736" +"4.9177","5.1541","5.1550","5.1727","5.1218","5.1064","5.0882","2006-06-29 20:00:00.000 -0400","5.0673","5.0755","0.1886" +"5.0765","5.1345","5.2175","5.2665","5.1185","5.0875","5.0700","2006-07-30 20:00:00.000 -0400","5.0400","5.0485","0.0110" +"5.0904","4.9961","5.0826","5.1726","4.9035","4.8765","4.8461","2006-08-30 20:00:00.000 -0400","4.8222","4.8287","-0.2139" +"4.9300","4.8520","4.9745","5.0785","4.7690","4.7190","4.6925","2006-09-29 20:00:00.000 -0400","4.6675","4.6760","-0.2110" +"5.0462","4.8548","5.0100","5.1190","4.7957","4.7290","4.7219","2006-10-30 19:00:00.000 -0500","4.6867","4.6900","-0.3171" +"5.0733","4.6857","5.0110","5.1471","4.7405","4.5952","4.6429","2006-11-29 19:00:00.000 -0500","4.5824","4.5819","-0.4781" +"4.9730","4.6825","4.9415","5.0720","4.6735","4.5645","4.5780","2006-12-30 19:00:00.000 -0500","4.5330","4.5355","-0.4085" +"5.1052","4.8519","5.0571","5.1486","4.8762","4.7595","4.7943","2007-01-30 19:00:00.000 -0500","4.7529","4.7524","-0.3457" +"5.1632","4.8216","5.0537","5.1579","4.8489","4.7226","4.7537","2007-02-27 19:00:00.000 -0500","4.7105","4.7111","-0.4405" +"5.0800","4.7218","4.9205","5.0968","4.5736","4.5645","4.5064","2007-03-30 20:00:00.000 -0400","4.4809","4.4986","-0.5155" +"5.0067","4.8662","4.9324","5.0676","4.6662","4.6938","4.6014","2007-04-29 20:00:00.000 -0400","4.5933","4.6167","-0.3129" +"4.8677","4.9014","4.9091","4.9786","4.7664","4.7464","4.6936","2007-05-30 20:00:00.000 -0400","4.6673","4.6855","-0.1214" +"4.7419","5.2033","4.9624","4.9548","4.9805","5.1029","4.9990","2007-06-29 20:00:00.000 -0400","5.0262","5.0538","0.3610" +"4.9610","5.1081","4.9643","5.0352","4.8190","5.0043","4.8238","2007-07-30 20:00:00.000 -0400","4.8848","4.9329","0.0433" +"4.3170","4.9322","4.4722","4.5530","4.3130","4.6748","4.3387","2007-08-30 20:00:00.000 -0400","4.4322","4.5270","0.3578" +"3.9942","4.7932","4.1368","4.2016","4.0116","4.5216","4.0605","2007-09-29 20:00:00.000 -0400","4.1989","4.3300","0.5274" +"4.0027","4.7736","4.0968","4.1641","3.9677","4.5277","4.0109","2007-10-30 20:00:00.000 -0400","4.1982","4.3318","0.5250" +"3.3545","4.5200","3.4990","3.5810","3.3365","4.1485","3.3470","2007-11-29 19:00:00.000 -0500","3.6670","3.8660","0.7940" +"3.0665","4.5270","3.2630","3.3370","3.1170","4.0975","3.1325","2007-12-30 19:00:00.000 -0500","3.4875","3.7430","1.0310" +"2.8200","4.3305","2.7114","2.8362","2.4757","3.7443","2.5114","2008-01-30 19:00:00.000 -0500","2.9805","3.3071","0.9243" +"2.1740","4.5170","2.0535","2.0980","1.9730","3.7375","2.1880","2008-02-28 19:00:00.000 -0500","2.7790","3.2095","1.5635" +"1.2835","4.3930","1.5440","1.5070","1.6170","3.5100","1.7965","2008-03-30 20:00:00.000 -0400","2.4835","2.9320","2.2265" +"1.3118","4.4432","1.7382","1.5827","2.0473","3.6750","2.2327","2008-04-29 20:00:00.000 -0400","2.8414","3.1936","2.3632" +"1.7643","4.5962","2.0557","1.8633","2.4457","3.8800","2.6895","2008-05-30 20:00:00.000 -0400","3.1533","3.4600","2.1157" +"1.8905","4.6890","2.4195","2.1852","2.7738","4.0995","3.0752","2008-06-29 20:00:00.000 -0400","3.4852","3.7338","2.2090" +"1.6550","4.5709","2.2818","1.9759","2.5732","4.0077","2.8727","2008-07-30 20:00:00.000 -0400","3.3032","3.5959","2.3527" +"1.7529","4.5019","2.1771","1.9671","2.4205","3.8857","2.6990","2008-08-30 20:00:00.000 -0400","3.1419","3.4590","2.1329" +"1.1467","4.2690","1.9129","1.6410","2.0762","3.6862","2.3157","2008-09-29 20:00:00.000 -0400","2.8843","3.2486","2.5395" +"0.6859","4.1732","1.4205","1.2259","1.6123","3.8141","1.8645","2008-10-30 20:00:00.000 -0400","2.7264","3.1868","3.1282" +"0.1939","4.0044","1.0667","0.7411","1.2128","3.5267","1.5144","2008-11-29 19:00:00.000 -0500","2.2917","2.8189","3.3328" +"0.0395","2.8700","0.4945","0.2559","0.8209","2.4164","1.0700","2008-12-30 19:00:00.000 -0500","1.5218","1.8927","2.3769" +"0.1295","3.1280","0.4445","0.3045","0.8070","2.5175","1.1340","2009-01-30 19:00:00.000 -0500","1.5965","1.9780","2.3880" +"0.2953","3.5868","0.6226","0.4589","0.9753","2.8700","1.3689","2009-02-27 19:00:00.000 -0500","1.8716","2.3032","2.5747" +"0.2159","3.6432","0.6441","0.4259","0.9314","2.8195","1.3123","2009-03-30 20:00:00.000 -0400","1.8159","2.4177","2.6036" +"0.1581","3.7600","0.5476","0.3505","0.9271","2.9271","1.3167","2009-04-29 20:00:00.000 -0400","1.8581","2.4652","2.7690" +"0.1770","4.2270","0.5015","0.3035","0.9295","3.2930","1.3925","2009-05-30 20:00:00.000 -0400","2.1340","2.8105","3.1160" +"0.1786","4.5164","0.5136","0.3145","1.1841","3.7218","1.7595","2009-06-29 20:00:00.000 -0400","2.7059","3.3650","3.5432" +"0.1836","4.4068","0.4786","0.2795","1.0191","3.5623","1.5482","2009-07-30 20:00:00.000 -0400","2.4627","3.1382","3.3786" +"0.1719","4.3710","0.4590","0.2690","1.1152","3.5871","1.6486","2009-08-30 20:00:00.000 -0400","2.5710","3.2138","3.4152" +"0.1233","4.1857","0.4048","0.2071","0.9562","3.4019","1.4776","2009-09-29 20:00:00.000 -0400","2.3690","3.0167","3.2786" +"0.0743","4.1886","0.3748","0.1605","0.9490","3.3876","1.4638","2009-10-30 20:00:00.000 -0400","2.3329","2.9633","3.3133" +"0.0521","4.3147","0.3132","0.1547","0.8032","3.4026","1.3163","2009-11-29 19:00:00.000 -0500","2.2305","2.9216","3.3505" +"0.0545","4.4941","0.3714","0.1677","0.8745","3.5900","1.3832","2009-12-30 19:00:00.000 -0500","2.3405","3.0745","3.5355" +"0.0616","4.6047","0.3458","0.1484","0.9289","3.7332","1.4921","2010-01-30 19:00:00.000 -0500","2.4842","3.2089","3.6716" +"0.1089","4.6195","0.3458","0.1811","0.8568","3.6911","1.4011","2010-02-27 19:00:00.000 -0500","2.3637","3.1226","3.5821" +"0.1504","4.6448","0.3957","0.2257","0.9587","3.7274","1.5057","2010-03-30 20:00:00.000 -0400","2.4330","3.1617","3.5770" +"0.1623","4.6932","0.4450","0.2441","1.0605","3.8468","1.6382","2010-04-29 20:00:00.000 -0400","2.5814","3.2818","3.6845" +"0.1600","4.2860","0.3710","0.2235","0.8305","3.4200","1.3190","2010-05-30 20:00:00.000 -0400","2.1800","2.8610","3.2600" +"0.1236","4.1277","0.3182","0.1909","0.7245","3.2041","1.1745","2010-06-29 20:00:00.000 -0400","1.9964","2.6559","3.0805" +"0.1576","3.9943","0.2914","0.2005","0.6167","3.0114","0.9786","2010-07-30 20:00:00.000 -0400","1.7629","2.4343","2.8538" +"0.1550","3.8032","0.2591","0.1914","0.5205","2.6986","0.7836","2010-08-30 20:00:00.000 -0400","1.4655","2.1005","2.5436" +"0.1519","3.7733","0.2567","0.1933","0.4800","2.6476","0.7419","2010-09-29 20:00:00.000 -0400","1.4105","2.0514","2.4957" +"0.1345","3.8725","0.2280","0.1765","0.3760","2.5400","0.5685","2010-10-30 20:00:00.000 -0400","1.1825","1.8515","2.4055" +"0.1430","4.1860","0.2520","0.1820","0.4540","2.7630","0.6740","2010-11-29 19:00:00.000 -0500","1.3500","2.0235","2.6200" +"0.1409","4.4177","0.2941","0.1932","0.6182","3.2909","0.9932","2010-12-30 19:00:00.000 -0500","1.9345","2.6577","3.1500" +"0.1520","4.5230","0.2735","0.1815","0.6145","3.3940","1.0275","2011-01-30 19:00:00.000 -0500","1.9945","2.7235","3.2420" +"0.1321","4.6521","0.2863","0.1684","0.7726","3.5763","1.2832","2011-02-27 19:00:00.000 -0500","2.2579","2.9616","3.4442" +"0.1004","4.5139","0.2591","0.1565","0.6974","3.4143","1.1743","2011-03-30 20:00:00.000 -0400","2.1135","2.7991","3.3139" +"0.0585","4.5015","0.2465","0.1195","0.7345","3.4550","1.2100","2011-04-29 20:00:00.000 -0400","2.1690","2.8370","3.3965" +"0.0410","4.2933","0.1881","0.0867","0.5552","3.1686","0.9352","2011-05-30 20:00:00.000 -0400","1.8424","2.5138","3.1276" +"0.0373","4.2327","0.1809","0.1014","0.4105","3.0023","0.7114","2011-06-29 20:00:00.000 -0400","1.5800","2.2868","2.9650" +"0.0375","4.2705","0.1850","0.0840","0.4065","3.0030","0.6800","2011-07-30 20:00:00.000 -0400","1.5410","2.2770","2.9655" +"0.0243","3.6513","0.1148","0.0622","0.2304","2.3030","0.3830","2011-08-30 20:00:00.000 -0400","1.0213","1.6291","2.2787" +"0.0138","3.1824","0.1048","0.0433","0.2114","1.9752","0.3538","2011-09-29 20:00:00.000 -0400","0.9005","1.4195","1.9614" +"0.0190","3.1280","0.1145","0.0545","0.2805","2.1520","0.4710","2011-10-30 20:00:00.000 -0400","1.0615","1.6190","2.1330" +"0.0140","3.0155","0.1120","0.0465","0.2535","2.0135","0.3935","2011-11-29 19:00:00.000 -0500","0.9080","1.4520","1.9995" +"0.0114","2.9824","0.1152","0.0486","0.2571","1.9781","0.3871","2011-12-30 19:00:00.000 -0500","0.8914","1.4290","1.9667" +"0.0345","3.0260","0.1150","0.0655","0.2405","1.9665","0.3580","2012-01-30 19:00:00.000 -0500","0.8350","1.3805","1.9320" +"0.0920","3.1090","0.1610","0.1240","0.2780","1.9675","0.3845","2012-02-28 19:00:00.000 -0500","0.8310","1.3735","1.8755" +"0.0841","3.2814","0.1900","0.1423","0.3445","2.1727","0.5077","2012-03-30 20:00:00.000 -0400","1.0173","1.5645","2.0886" +"0.0838","3.1843","0.1833","0.1390","0.2919","2.0529","0.4319","2012-04-29 20:00:00.000 -0400","0.8948","1.4252","1.9690" +"0.0895","2.9309","0.1918","0.1455","0.2850","1.8032","0.3882","2012-05-30 20:00:00.000 -0400","0.7618","1.2132","1.7136" +"0.0914","2.6981","0.1890","0.1481","0.2919","1.6224","0.3890","2012-06-29 20:00:00.000 -0400","0.7114","1.0800","1.5310" +"0.0971","2.5900","0.1857","0.1457","0.2481","1.5267","0.3310","2012-07-30 20:00:00.000 -0400","0.6195","0.9843","1.4295" +"0.1026","2.7709","0.1835","0.1404","0.2687","1.6783","0.3678","2012-08-30 20:00:00.000 -0400","0.7139","1.1352","1.5757" +"0.1053","2.8816","0.1758","0.1374","0.2553","1.7232","0.3384","2012-09-29 20:00:00.000 -0400","0.6689","1.1184","1.6179" +"0.1048","2.9005","0.1795","0.1490","0.2771","1.7462","0.3690","2012-10-30 20:00:00.000 -0400","0.7086","1.1457","1.6414" +"0.0935","2.8035","0.1785","0.1445","0.2680","1.6540","0.3550","2012-11-29 19:00:00.000 -0500","0.6650","1.0765","1.5605" +"0.0700","2.8835","0.1585","0.1195","0.2570","1.7190","0.3540","2012-12-30 19:00:00.000 -0500","0.6960","1.1345","1.6490" +"0.0743","3.0805","0.1452","0.1076","0.2652","1.9148","0.3905","2013-01-30 19:00:00.000 -0500","0.8052","1.2986","1.8405" +"0.0989","3.1653","0.1574","0.1242","0.2674","1.9842","0.3984","2013-02-27 19:00:00.000 -0500","0.8463","1.3484","1.8853" +"0.0870","3.1625","0.1475","0.1145","0.2560","1.9575","0.3860","2013-03-30 20:00:00.000 -0400","0.8185","1.3175","1.8705" +"0.0600","2.9327","0.1245","0.0941","0.2323","1.7591","0.3405","2013-04-29 20:00:00.000 -0400","0.7105","1.1523","1.6991" +"0.0441","3.1127","0.1186","0.0809","0.2505","1.9282","0.3959","2013-05-30 20:00:00.000 -0400","0.8409","1.3109","1.8841" +"0.0505","3.4000","0.1420","0.0875","0.3335","2.3000","0.5770","2013-06-29 20:00:00.000 -0400","1.2035","1.7140","2.2495" +"0.0355","3.6050","0.1218","0.0736","0.3409","2.5823","0.6441","2013-07-30 20:00:00.000 -0400","1.4032","1.9914","2.5468" +"0.0436","3.7577","0.1268","0.0718","0.3564","2.7373","0.7045","2013-08-30 20:00:00.000 -0400","1.5205","2.1523","2.6936" +"0.0160","3.7870","0.1185","0.0415","0.4040","2.8095","0.7795","2013-09-29 20:00:00.000 -0400","1.5960","2.2165","2.7935" +"0.0473","3.6759","0.1214","0.0759","0.3355","2.6159","0.6277","2013-10-30 20:00:00.000 -0400","1.3668","1.9936","2.5686" +"0.0679","3.8000","0.1216","0.0984","0.3037","2.7184","0.5800","2013-11-29 19:00:00.000 -0500","1.3711","2.0695","2.6505" +"0.0667","3.8890","0.1329","0.0952","0.3400","2.9019","0.6852","2013-12-30 19:00:00.000 -0500","1.5762","2.2857","2.8352" +"0.0433","3.7690","0.1162","0.0690","0.3938","2.8581","0.7795","2014-01-30 19:00:00.000 -0500","1.6467","2.2938","2.8148" +"0.0526","3.6626","0.1168","0.0811","0.3268","2.7095","0.6884","2014-02-27 19:00:00.000 -0500","1.5158","2.1526","2.6568" +"0.0524","3.6210","0.1281","0.0786","0.3990","2.7233","0.8167","2014-03-30 20:00:00.000 -0400","1.6395","2.2333","2.6710" +"0.0310","3.5176","0.1076","0.0533","0.4171","2.7052","0.8848","2014-04-29 20:00:00.000 -0400","1.7010","2.2695","2.6743" +"0.0324","3.3900","0.0967","0.0519","0.3890","2.5590","0.8262","2014-05-30 20:00:00.000 -0400","1.5929","2.1210","2.5267" +"0.0357","3.4200","0.1048","0.0605","0.4524","2.5986","0.9048","2014-06-29 20:00:00.000 -0400","1.6790","2.1933","2.5629" +"0.0264","3.3318","0.1114","0.0595","0.5064","2.5423","0.9736","2014-07-30 20:00:00.000 -0400","1.6995","2.1727","2.5159" +"0.0329","3.2010","0.1071","0.0524","0.4724","2.4200","0.9276","2014-08-30 20:00:00.000 -0400","1.6314","2.0805","2.3871" +"0.0200","3.2600","0.1095","0.0438","0.5667","2.5343","1.0510","2014-09-29 20:00:00.000 -0400","1.7738","2.2205","2.5143" +"0.0168","3.0400","0.1045","0.0505","0.4455","2.3041","0.8750","2014-10-30 20:00:00.000 -0400","1.5459","1.9805","2.2873" +"0.0217","3.0383","0.1344","0.0694","0.5272","2.3256","0.9628","2014-11-29 19:00:00.000 -0500","1.6206","2.0289","2.3039" +"0.0291","2.8332","0.2145","0.1064","0.6355","2.2073","1.0641","2014-12-30 19:00:00.000 -0500","1.6400","1.9805","2.1782" +"0.0275","2.4550","0.1955","0.0830","0.5515","1.8815","0.8970","2015-01-30 19:00:00.000 -0500","1.3745","1.6715","1.8540" +"0.0179","2.5663","0.2242","0.0711","0.6189","1.9753","0.9916","2015-02-27 19:00:00.000 -0500","1.4726","1.7879","1.9574" +"0.0277","2.6264","0.2536","0.1100","0.6405","2.0427","1.0168","2015-03-30 20:00:00.000 -0400","1.5191","1.8423","2.0150" +"0.0232","2.5859","0.2336","0.0936","0.5400","1.9350","0.8655","2015-04-29 20:00:00.000 -0400","1.3545","1.6909","1.9118" +"0.0165","2.9550","0.2410","0.0795","0.6090","2.1975","0.9770","2015-05-30 20:00:00.000 -0400","1.5385","1.9330","2.1810" +"0.0150","3.1118","0.2755","0.0868","0.6886","2.3636","1.0650","2015-06-29 20:00:00.000 -0400","1.6836","2.0995","2.3486" +"0.0323","3.0664","0.2968","0.1164","0.6677","2.3245","1.0264","2015-07-30 20:00:00.000 -0400","1.6323","2.0418","2.2923" +"0.0719","2.8557","0.3757","0.2200","0.6976","2.1671","1.0300","2015-08-30 20:00:00.000 -0400","1.5414","1.9081","2.0952" +"0.0243","2.9529","0.3733","0.1795","0.7133","2.1729","1.0133","2015-09-29 20:00:00.000 -0400","1.4900","1.8762","2.1486" +"0.0167","2.8881","0.2633","0.1138","0.6448","2.0700","0.9267","2015-10-30 20:00:00.000 -0400","1.3857","1.7624","2.0533" +"0.1263","3.0300","0.4768","0.3279","0.8847","2.2632","1.2042","2015-11-29 19:00:00.000 -0500","1.6711","2.0247","2.1368" +"0.2286","2.9700","0.6536","0.4991","0.9827","2.2427","1.2805","2015-12-30 19:00:00.000 -0500","1.6986","2.0382","2.0141" +"0.2200","2.9800","0.6100","0.4900","1.0200","2.2400","1.3100","2016-01-30 19:00:00.000 -0500","1.7300","2.0600","2.0200" diff --git a/js/.eslintignore b/js/.eslintignore new file mode 100644 index 0000000..f06235c --- /dev/null +++ b/js/.eslintignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/js/.eslintrc b/js/.eslintrc new file mode 100644 index 0000000..5a73dce --- /dev/null +++ b/js/.eslintrc @@ -0,0 +1,19 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "prettier/@typescript-eslint", + "plugin:prettier/recommended" + ], + "rules": { + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-empty-function": "off" + } +} diff --git a/js/.prettierrc b/js/.prettierrc new file mode 100644 index 0000000..09ee8e5 --- /dev/null +++ b/js/.prettierrc @@ -0,0 +1,7 @@ +{ + semi: true, + trailingComma: "all", + singleQuote: true, + printWidth: 120, + tabWidth: 2 +} diff --git a/js/_old/lab-theme-dark/.gitignore b/js/_old/lab-theme-dark/.gitignore new file mode 100644 index 0000000..73334cb --- /dev/null +++ b/js/_old/lab-theme-dark/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +*.tsbuildinfo \ No newline at end of file diff --git a/js/_old/lab-theme-dark/.npmignore b/js/_old/lab-theme-dark/.npmignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/js/_old/lab-theme-dark/.npmignore @@ -0,0 +1 @@ +node_modules/ diff --git a/js/_old/lab-theme-dark/README.md b/js/_old/lab-theme-dark/README.md new file mode 100644 index 0000000..dfecd51 --- /dev/null +++ b/js/_old/lab-theme-dark/README.md @@ -0,0 +1,3 @@ +# beakerx-jupyterlab-theme-dark-extension + +A JupyterLab theme extension using default dark-colored theme with beakerx additions. \ No newline at end of file diff --git a/js/_old/lab-theme-dark/package-lock.json b/js/_old/lab-theme-dark/package-lock.json new file mode 100644 index 0000000..dcec2e3 --- /dev/null +++ b/js/_old/lab-theme-dark/package-lock.json @@ -0,0 +1,1203 @@ +{ + "name": "beakerx-jupyterlab-theme-dark-extension", + "version": "1.5.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/runtime": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz", + "integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@blueprintjs/core": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@blueprintjs/core/-/core-3.26.1.tgz", + "integrity": "sha512-f5U9RBvSMV/eLEXlBeaYbx+LRAcrsSrnK8gScM4Jwnuto6d9m6CeVxNe6YkQxvKOs4lhiC+UdPN2A9yJcXrGHA==", + "requires": { + "@blueprintjs/icons": "^3.16.0", + "@types/dom4": "^2.0.1", + "classnames": "^2.2", + "dom4": "^2.1.5", + "normalize.css": "^8.0.1", + "popper.js": "^1.15.0", + "react-lifecycles-compat": "^3.0.4", + "react-popper": "^1.3.7", + "react-transition-group": "^2.9.0", + "resize-observer-polyfill": "^1.5.1", + "tslib": "~1.10.0" + } + }, + "@blueprintjs/icons": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@blueprintjs/icons/-/icons-3.16.0.tgz", + "integrity": "sha512-VVBvgnpz8eQFhnX5OQGLdRqka9DU/CTtF3w9vnwKK2r9N/xRF0BPe9Ev1RzuUzP4GJG4GUIRaqiAX9+l/FviDg==", + "requires": { + "classnames": "^2.2", + "tslib": "~1.10.0" + } + }, + "@blueprintjs/select": { + "version": "3.12.3", + "resolved": "https://registry.npmjs.org/@blueprintjs/select/-/select-3.12.3.tgz", + "integrity": "sha512-WU6RGk2NSlPS4VjcpljmGQY+w4ezMCazoQfh37JMDNq5Xa/wzrKaleXifMmZUUWJNvjtsZ0sNDNLzI+DGuX0dQ==", + "requires": { + "@blueprintjs/core": "^3.26.1", + "classnames": "^2.2", + "tslib": "~1.10.0" + } + }, + "@jupyterlab/application": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/application/-/application-1.2.7.tgz", + "integrity": "sha512-V2nN9C7jirhAhrJPvp+2iBAJUcOruit5AL9tNYGjjATTetY/7pcuSOjh9yCS7eADp1TnsrzvcKeNJ6iH3S2FmQ==", + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/docregistry": "^1.2.7", + "@jupyterlab/rendermime": "^1.2.7", + "@jupyterlab/rendermime-interfaces": "^1.5.0", + "@jupyterlab/services": "^4.2.2", + "@jupyterlab/ui-components": "^1.2.1", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/application": "^1.7.0", + "@phosphor/commands": "^1.7.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "font-awesome": "~4.7.0" + } + }, + "@jupyterlab/apputils": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/apputils/-/apputils-1.2.7.tgz", + "integrity": "sha512-WdCJEWTiWgFlS/DTFYra+67UiG85xm+WnIutMnmuIz7Lq/3TI7yDsCpr3fd2AHOGg3E2OGo2qi5uNQoR+HPXRg==", + "requires": { + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/services": "^4.2.2", + "@jupyterlab/ui-components": "^1.2.1", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/domutils": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.0", + "@phosphor/virtualdom": "^1.2.0", + "@phosphor/widgets": "^1.9.0", + "@types/react": "~16.8.18", + "react": "~16.8.4", + "react-dom": "~16.8.4", + "sanitize-html": "~1.20.1" + } + }, + "@jupyterlab/codeeditor": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/codeeditor/-/codeeditor-1.2.0.tgz", + "integrity": "sha512-toejhF/a80X10SZyvEnsnnlS9SxR5W4cz67ju7e/2lsZ8RMwZEDDJAJXyW3mw/EEjt8oVRNP2QpM8L5clE9XyQ==", + "requires": { + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/dragdrop": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0" + } + }, + "@jupyterlab/codemirror": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/codemirror/-/codemirror-1.2.7.tgz", + "integrity": "sha512-CfGwmdXXkqLowrVXuXDIlftECgjQe8QZe+ZT67iTWkcFc7jyW3vDZTLy22n6inMx9lygEkWKr3UxIVdvTxMUeg==", + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/codeeditor": "^1.2.0", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@jupyterlab/statusbar": "^1.2.7", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "codemirror": "~5.47.0", + "react": "~16.8.4" + } + }, + "@jupyterlab/coreutils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/coreutils/-/coreutils-3.2.0.tgz", + "integrity": "sha512-LATiUsHuwze/h3JC2EZOBV+kGBoUKO3npqw/Pcgge4bz09xF/oTDrx4G8jl5eew3w1dCUNp9eLduNh8Orrw7xQ==", + "requires": { + "@phosphor/commands": "^1.7.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.0", + "ajv": "^6.5.5", + "json5": "^2.1.0", + "minimist": "~1.2.0", + "moment": "^2.24.0", + "path-posix": "~1.0.0", + "url-parse": "~1.4.3" + } + }, + "@jupyterlab/docregistry": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/docregistry/-/docregistry-1.2.7.tgz", + "integrity": "sha512-V9P3FJNjl06vCxJxTWzUhWE/tZwuNNCQmcgkddn8+1vjx/LlFC1UFpJuigkY+hUKIBGcIerx9tLjsEGNKdj4XA==", + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/codeeditor": "^1.2.0", + "@jupyterlab/codemirror": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@jupyterlab/rendermime": "^1.2.7", + "@jupyterlab/rendermime-interfaces": "^1.5.0", + "@jupyterlab/services": "^4.2.2", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0" + } + }, + "@jupyterlab/observables": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/observables/-/observables-2.4.0.tgz", + "integrity": "sha512-M/fhAnPqd6F4Zwt4IIsvHCkJmwbSw1Tko/hUXgdUQG86lPsJiTOh98sB3qwV1gtzb9oFF+kH21XsHnQZ6Yl6Pw==", + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0" + } + }, + "@jupyterlab/rendermime": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/rendermime/-/rendermime-1.2.7.tgz", + "integrity": "sha512-+EqMljh/ysXgTET2VeoQUtALl76vHsLZEIA84luxr7XzeqQ+L/imUZ3zEBJwvG30FLkoeAqZQFQ3pQsrVpGUQw==", + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/codemirror": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@jupyterlab/rendermime-interfaces": "^1.5.0", + "@jupyterlab/services": "^4.2.2", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "lodash.escape": "^4.0.1", + "marked": "^0.7.0" + } + }, + "@jupyterlab/rendermime-interfaces": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/rendermime-interfaces/-/rendermime-interfaces-1.5.0.tgz", + "integrity": "sha512-k6DjX/srKl1FA1CZyrAzz1qA2v1arXUIAmbEddZ5L3O+dnvDlOKjkI/NexaRQvmQ62aziSln+wKrr2P1JPNmGg==", + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/widgets": "^1.9.0" + } + }, + "@jupyterlab/services": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@jupyterlab/services/-/services-4.2.2.tgz", + "integrity": "sha512-1CNSsPbaAWaRkb3s5a+qpqMq/62RjLggL3MoSlGlp0Mi7jfJ8EJFIrnlRn4/a9/QwY3E3vsOFXaQ13v49PMWfA==", + "requires": { + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "node-fetch": "^2.6.0", + "ws": "^7.0.0" + } + }, + "@jupyterlab/statusbar": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/statusbar/-/statusbar-1.2.7.tgz", + "integrity": "sha512-a3vRn7upAIRBmDgqg6UKaQ4+bqFyjA9HFUlDQ65R/23S3NXlOGxR24RR1mnVHxZwu0sVTx/l407GdhCGxnLs3g==", + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/codeeditor": "^1.2.0", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/services": "^4.2.2", + "@jupyterlab/ui-components": "^1.2.1", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "react": "~16.8.4", + "typestyle": "^2.0.1" + } + }, + "@jupyterlab/ui-components": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jupyterlab/ui-components/-/ui-components-1.2.1.tgz", + "integrity": "sha512-GUtIRwTmFnlJaPUM8SiFw1STmsyMVGjchLKqIoQnn0qYAJvaSUGyRqqoSD5iIpwov6OHCOOyxH6fQ5OAtH1kwA==", + "requires": { + "@blueprintjs/core": "^3.9.0", + "@blueprintjs/select": "^3.3.0", + "@jupyterlab/coreutils": "^3.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/messaging": "^1.3.0", + "@phosphor/virtualdom": "^1.2.0", + "@phosphor/widgets": "^1.9.0", + "react": "~16.8.4", + "typestyle": "^2.0.1" + } + }, + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==" + }, + "@phosphor/application": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@phosphor/application/-/application-1.7.3.tgz", + "integrity": "sha512-ohxrW7rv5Tms4PSyPRZT6YArZQQGQNG4MgTeFzkoLJ+7mp/BcbFuvEoaV1/CUKQArofl0DCkKDOTOIkXP+/32A==", + "requires": { + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/widgets": "^1.9.3" + } + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/commands": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@phosphor/commands/-/commands-1.7.2.tgz", + "integrity": "sha512-iSyBIWMHsus323BVEARBhuVZNnVel8USo+FIPaAxGcq+icTSSe6+NtSxVQSmZblGN6Qm4iw6I6VtiSx0e6YDgQ==", + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/coreutils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/coreutils/-/coreutils-1.3.1.tgz", + "integrity": "sha512-9OHCn8LYRcPU/sbHm5v7viCA16Uev3gbdkwqoQqlV+EiauDHl70jmeL7XVDXdigl66Dz0LI11C99XOxp+s3zOA==" + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==" + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/keyboard": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@phosphor/keyboard/-/keyboard-1.1.3.tgz", + "integrity": "sha512-dzxC/PyHiD6mXaESRy6PZTd9JeK+diwG1pyngkyUf127IXOEzubTIbu52VSdpGBklszu33ws05BAGDa4oBE4mQ==" + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@phosphor/properties/-/properties-1.1.3.tgz", + "integrity": "sha512-GiglqzU77s6+tFVt6zPq9uuyu/PLQPFcqZt914ZhJ4cN/7yNI/SLyMzpYZ56IRMXvzK9TUgbRna6URE3XAwFUg==" + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + } + }, + "@types/dom4": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/dom4/-/dom4-2.0.1.tgz", + "integrity": "sha512-kSkVAvWmMZiCYtvqjqQEwOmvKwcH+V4uiv3qPQ8pAh1Xl39xggGEo8gHUqV4waYGHezdFw0rKBR8Jt0CrQSDZA==" + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + }, + "@types/react": { + "version": "16.8.25", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.25.tgz", + "integrity": "sha512-ydAAkLnNTC4oYSxJ3zwK/4QcVmEecACJ4ZdxXITbxz/dhahBSDKY6OQ1uawAW6rE/7kfHccxulYLSAIZVrSq0A==", + "requires": { + "@types/prop-types": "*", + "csstype": "^2.2.0" + } + }, + "ajv": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "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==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, + "codemirror": { + "version": "5.47.0", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.47.0.tgz", + "integrity": "sha512-kV49Fr+NGFHFc/Imsx6g180hSlkGhuHxTSDDmDHOuyln0MQYFLixDY4+bFkBVeCEiepYfDimAF/e++9jPJk4QA==" + }, + "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==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "create-react-context": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz", + "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==", + "requires": { + "gud": "^1.0.0", + "warning": "^4.0.3" + } + }, + "csstype": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz", + "integrity": "sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q==" + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "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==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" + }, + "entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" + } + } + }, + "dom4": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/dom4/-/dom4-2.1.5.tgz", + "integrity": "sha512-gJbnVGq5zaBUY0lUh0LUEVGYrtN75Ks8ZwpwOYvnVFrKy/qzXK4R/1WuLIFExWj/tBxbRAkTzZUGJHXmqsBNjQ==" + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" + }, + "free-style": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/free-style/-/free-style-3.1.0.tgz", + "integrity": "sha512-vJujYSIyT30iDoaoeigNAxX4yB1RUrh+N2ZMhIElMr3BvCuGXOw7XNJMEEJkDUeamK2Rnb/IKFGKRKlTWIGRWA==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "gud": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", + "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "requires": { + "minimist": "^1.2.5" + } + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "lodash.escape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", + "integrity": "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=" + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "marked": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", + "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "moment": { + "version": "2.25.3", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.25.3.tgz", + "integrity": "sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg==" + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "normalize.css": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", + "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-is": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", + "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-posix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-posix/-/path-posix-1.0.0.tgz", + "integrity": "sha1-BrJhE/Vr6rBCVFojv6iAA8ysJg8=" + }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" + }, + "postcss": { + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.29.tgz", + "integrity": "sha512-ba0ApvR3LxGvRMMiUa9n0WR4HjzcYm7tS+ht4/2Nd0NLtHpPIH77fuB9Xh1/yJVz9O/E/95Y/dn8ygWsyffXtw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" + }, + "react": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz", + "integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.13.6" + } + }, + "react-dom": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz", + "integrity": "sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.13.6" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-popper": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.7.tgz", + "integrity": "sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==", + "requires": { + "@babel/runtime": "^7.1.2", + "create-react-context": "^0.3.0", + "deep-equal": "^1.1.1", + "popper.js": "^1.14.4", + "prop-types": "^15.6.1", + "typed-styles": "^0.0.7", + "warning": "^4.0.2" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "sanitize-html": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.1.tgz", + "integrity": "sha512-txnH8TQjaQvg2Q0HY06G6CDJLVYCpbnxrdO0WN8gjCKaU5J0KbyGYhZxx5QJg3WLZ1lB7XU9kDkfrCXUozqptA==", + "requires": { + "chalk": "^2.4.1", + "htmlparser2": "^3.10.0", + "lodash.clonedeep": "^4.5.0", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.mergewith": "^4.6.1", + "postcss": "^7.0.5", + "srcset": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "scheduler": { + "version": "0.13.6", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", + "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "srcset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", + "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", + "requires": { + "array-uniq": "^1.0.2", + "number-is-nan": "^1.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimleft": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" + } + }, + "string.prototype.trimright": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, + "typed-styles": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz", + "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==" + }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "dev": true + }, + "typestyle": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typestyle/-/typestyle-2.1.0.tgz", + "integrity": "sha512-6uCYPdG4xWLeEcl9O0GtNFnNGhami+irKiLsXSuvWHC/aTS7wdj49WeikWAKN+xHN3b1hm+9v0svwwgSBhCsNA==", + "requires": { + "csstype": "2.6.9", + "free-style": "3.1.0" + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz", + "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + } + } +} diff --git a/js/_old/lab-theme-dark/package.json b/js/_old/lab-theme-dark/package.json new file mode 100644 index 0000000..9ea6a61 --- /dev/null +++ b/js/_old/lab-theme-dark/package.json @@ -0,0 +1,42 @@ +{ + "name": "beakerx-jupyterlab-theme-dark-extension", + "version": "1.5.0", + "description": "BeakerX: Beaker Dark Theme Extension for JupyterLab", + "author": "Two Sigma Open Source, LLC", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "" + }, + "keywords": [], + "files": [ + "lib/*.d.ts", + "lib/*.js.map", + "lib/*.js", + "style/**/*.{css,ttf,woff,woff2}" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "style": "style/index.css", + "directories": { + "lib": "lib/" + }, + "scripts": { + "build": "tsc -b", + "clean": "rimraf lib", + "prepublish": "npm run build", + "watch": "tsc -b --watch" + }, + "dependencies": { + "@jupyterlab/application": "^1.2.7", + "@jupyterlab/apputils": "^1.2.7" + }, + "devDependencies": { + "rimraf": "^3.0.2", + "typescript": "^3.8.3" + }, + "jupyterlab": { + "extension": true, + "themePath": "style/index.css" + } +} diff --git a/js/_old/lab-theme-dark/src/index.ts b/js/_old/lab-theme-dark/src/index.ts new file mode 100644 index 0000000..b8992dd --- /dev/null +++ b/js/_old/lab-theme-dark/src/index.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; + +import { IThemeManager } from '@jupyterlab/apputils'; + +const plugin: JupyterFrontEndPlugin = { + id: 'beakerx:theme-dark:plugin', + requires: [IThemeManager], + activate: (app: JupyterFrontEnd, manager: IThemeManager) => { + const originalStyle = '@jupyterlab/theme-dark-extension/index.css'; + const beakerxStyle = 'beakerx-jupyterlab-theme-dark-extension/index.css'; + + manager.register({ + name: 'BeakerX Dark', + isLight: false, + themeScrollbars: false, + load: async () => { + await manager.loadCSS(originalStyle); + await manager.loadCSS(beakerxStyle); + + }, + unload: () => Promise.resolve(undefined) + }); + }, + autoStart: true +}; + +export default plugin; diff --git a/js/_old/lab-theme-dark/style/base.css b/js/_old/lab-theme-dark/style/base.css new file mode 100644 index 0000000..90aa293 --- /dev/null +++ b/js/_old/lab-theme-dark/style/base.css @@ -0,0 +1,106 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.widget-text input[type="password"] { + box-sizing: border-box; + border: var(--jp-widgets-input-border-width) solid var(--jp-widgets-input-border-color); + background-color: var(--jp-widgets-input-background-color); + color: var(--jp-widgets-input-color); + font-size: var(--jp-widgets-font-size); + padding: var(--jp-widgets-input-padding) calc( var(--jp-widgets-input-padding) * 2 ); + flex-grow: 1; + min-width: 0; + flex-shrink: 1; + outline: none !important; + height: var(--jp-widgets-inline-height); + line-height: var(--jp-widgets-inline-height); +} + +.widget-text input[type="password"]:focus { + border-color: var(--jp-widgets-input-focus-border-color); +} + + +.widget-select-multiple select { + border: var(--jp-widgets-input-border-width) solid var(--jp-widgets-input-border-color); + background-color: var(--jp-widgets-input-background-color); + color: var(--jp-widgets-input-color); + font-size: var(--jp-widgets-font-size); +} + +.widget-select select:focus, +.widget-select-multiple select:focus { + border-color: var(--jp-widgets-input-focus-border-color); +} + +.easyform-combobox-input { + box-sizing: border-box; + border: var(--jp-widgets-input-border-width) solid var(--jp-widgets-input-border-color) !important; + background-color: var(--jp-widgets-input-background-color); + color: var(--jp-widgets-input-color); + font-size: var(--jp-widgets-font-size); + padding: var(--jp-widgets-input-padding) calc( var(--jp-widgets-input-padding) * 2 ); + outline: none !important; + height: calc(var(--jp-widgets-inline-height) + 2px); + line-height: var(--jp-widgets-inline-height); +} + +.easyform-combobox-toggle { + border-color: var(--jp-widgets-input-border-color); + background-color: var(--jp-widgets-input-background-color); +} +.easyform-combobox-toggle:focus, +.easyform-combobox-toggle:active, +.easyform-combobox-toggle:hover { + border-color: var(--jp-widgets-input-focus-border-color); + background-color: var(--jp-widgets-input-background-color); +} + +.beaker-easyform-container .datepicker-container.flatpickr input[type="text"] { + box-sizing: border-box; + border: var(--jp-widgets-input-border-width) solid var(--jp-widgets-input-border-color); + background-color: var(--jp-widgets-input-background-color); + color: var(--jp-widgets-input-color); + font-size: var(--jp-widgets-font-size); + padding: var(--jp-widgets-input-padding) calc( var(--jp-widgets-input-padding) * 2 ); + outline: none !important; + height: var(--jp-widgets-inline-height); + line-height: var(--jp-widgets-inline-height); + border-right-width: 0; +} + +.beaker-easyform-container .datepicker-container.flatpickr .date-picker-button { + border-color: var(--jp-widgets-input-border-color); + background-color: var(--jp-widgets-input-background-color); +} +.beaker-easyform-container .datepicker-container.flatpickr .date-picker-button:focus, +.beaker-easyform-container .datepicker-container.flatpickr .date-picker-button:active, +.beaker-easyform-container .datepicker-container.flatpickr .date-picker-button:hover { + border-color: var(--jp-widgets-input-focus-border-color); + outline: none !important; +} + +.hidden { + display: none !important; +} + +.bx-stats .bx-label, .bx-spark-stageProgressLabels .bx-label { + line-height: 1.3; +} + +.bx-stats .label { + box-sizing: border-box; +} \ No newline at end of file diff --git a/js/_old/lab-theme-dark/style/fonts.css b/js/_old/lab-theme-dark/style/fonts.css new file mode 100644 index 0000000..374f24a --- /dev/null +++ b/js/_old/lab-theme-dark/style/fonts.css @@ -0,0 +1,78 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@font-face { + font-family: 'Roboto Mono'; + src: url('fonts/robotomono/robotomono.woff') format('woff'), + url('fonts/robotomono/robotomono.ttf') format('truetype'); + + font-style: normal; + font-weight: normal; +} + +@font-face { + font-family: 'Roboto Mono'; + src: url('fonts/robotomono/robotomono-bold.woff') format('woff'), + url('fonts/robotomono/robotomono-bold.ttf') format('truetype'); + font-weight: bold; +} + +@font-face { + font-family: 'Lato'; + src: url('fonts/lato/Lato-Regular.woff') format('woff'), + url('fonts/lato/Lato-Regular.ttf') format('truetype'); + + font-style: normal; + font-weight: normal; +} + +@font-face { + font-family: 'Lato'; + src: url('fonts/lato/Lato-Black.woff') format('woff'), + url('fonts/lato/Lato-Black.ttf') format('truetype'); + font-weight: bold; +} + +.improveFonts .CodeMirror pre, +.improveFonts .rendered_html pre, +.improveFonts .rendered_html code, +.improveFonts .output_area pre { + font-family: "Roboto Mono", monospace, sans-serif; +} + +div.output_text { + line-height: 1.3em; +} + +.improveFonts .widget-html-content, +.improveFonts .rendered_html, +.improveFonts .cm-header-1, +.improveFonts .cm-header-2, +.improveFonts .cm-header-3, +.improveFonts .cm-header-4, +.improveFonts .cm-header-5, +.improveFonts .cm-header-6 { + font-family: "Lato", Helvetica, sans-serif; +} + +.improveFonts .jp-RenderedText pre { + font-family: "Roboto Mono", monospace, sans-serif; +} + +.improveFonts .jp-RenderedMarkdown, +.improveFonts .jp-RenderedHTML { + font-family: "Lato", Helvetica, sans-serif; +} diff --git a/js/_old/lab-theme-dark/style/fonts/lato/Lato-Black.ttf b/js/_old/lab-theme-dark/style/fonts/lato/Lato-Black.ttf new file mode 100644 index 0000000..3f7819f Binary files /dev/null and b/js/_old/lab-theme-dark/style/fonts/lato/Lato-Black.ttf differ diff --git a/js/_old/lab-theme-dark/style/fonts/lato/Lato-Black.woff b/js/_old/lab-theme-dark/style/fonts/lato/Lato-Black.woff new file mode 100644 index 0000000..a0ab25e Binary files /dev/null and b/js/_old/lab-theme-dark/style/fonts/lato/Lato-Black.woff differ diff --git a/js/_old/lab-theme-dark/style/fonts/lato/Lato-Regular.ttf b/js/_old/lab-theme-dark/style/fonts/lato/Lato-Regular.ttf new file mode 100644 index 0000000..74decd9 Binary files /dev/null and b/js/_old/lab-theme-dark/style/fonts/lato/Lato-Regular.ttf differ diff --git a/js/_old/lab-theme-dark/style/fonts/lato/Lato-Regular.woff b/js/_old/lab-theme-dark/style/fonts/lato/Lato-Regular.woff new file mode 100644 index 0000000..ae1307f Binary files /dev/null and b/js/_old/lab-theme-dark/style/fonts/lato/Lato-Regular.woff differ diff --git a/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono-bold.ttf b/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono-bold.ttf new file mode 100755 index 0000000..07ef607 Binary files /dev/null and b/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono-bold.ttf differ diff --git a/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono-bold.woff b/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono-bold.woff new file mode 100644 index 0000000..1a76a58 Binary files /dev/null and b/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono-bold.woff differ diff --git a/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono.ttf b/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono.ttf new file mode 100644 index 0000000..aacaedf Binary files /dev/null and b/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono.ttf differ diff --git a/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono.woff b/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono.woff new file mode 100644 index 0000000..54ebc1d Binary files /dev/null and b/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono.woff differ diff --git a/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono.woff2 b/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono.woff2 new file mode 100644 index 0000000..73ad9a8 Binary files /dev/null and b/js/_old/lab-theme-dark/style/fonts/robotomono/robotomono.woff2 differ diff --git a/js/_old/lab-theme-dark/style/index.css b/js/_old/lab-theme-dark/style/index.css new file mode 100644 index 0000000..2333958 --- /dev/null +++ b/js/_old/lab-theme-dark/style/index.css @@ -0,0 +1,295 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import './fonts.css'; +@import './base.css'; + +body { + background-color: var(--jp-layout-color3); +} + +pre { + background-color: var(--jp-layout-color2); + color: var(--jp-content-font-color2); +} + +.plot-plotcontainer, +.combplot-plotcontainer { + background-color: var(--jp-layout-color0); + fill: var(--jp-content-font-color0); +} + +.plot-coverbox, +.combplot-plotcoverbox { + fill: var(--jp-layout-color0); +} + +.plot-title { + color: var(--jp-content-font-color0); +} + +.plot-legend { + background-color: var(--jp-layout-color0); + color: var(--jp-content-font-color0); + border-color: var(--jp-border-color0); +} + +.plot-gridline-base, +.plot-gridline, +.plot-tick { + stroke: var(--jp-border-color0); +} + +.plot-tooltip { + background-color: var(--jp-layout-color1); + color: var(--jp-content-font-color1); +} + +#beakerx-tree-widget { + color: var(--jp-content-font-color1); +} + +#beakerx-tree-widget svg { + fill: var(--jp-content-font-color1); +} + +#beakerx-tree-widget .p-TabBar-content { + border-color: var(--jp-border-color2); +} + +#beakerx-tree-widget .p-TabBar-tab.p-mod-current { + background-color: var(--jp-layout-color1); + border-color: var(--jp-border-color2); +} + +#beakerx-tree-widget .p-TabBar-tab { + background-color: var(--jp-layout-color2); +} + +#beakerx-tree-widget .p-TabBar-tab:hover:not(.p-mod-current) { + background-color: var(--jp-layout-color0); +} + +#beakerx-tree-widget #default_options .bx-wrapper input { + background-color: var(--jp-layout-color1); + color: var(--jp-content-font-color1); + border-color: var(--jp-border-color2); +} +#beakerx-tree-widget #default_options .bx-wrapper span { + background-color: var(--jp-layout-color0); + border-color: var(--jp-border-color2); +} + +#beakerx-tree-widget .bx-panel .bx-panel-heading { + background-color: var(--jp-layout-color1); +} + +#beakerx-tree-widget .bx-panel, +#beakerx-tree-widget .bx-panel .bx-panel-heading, +#beakerx-tree-widget .bx-panel .bx-panel-body { + border-color: var(--jp-border-color2); +} + +#beakerx-tree-widget .bx-panel .bx-panel-heading .bx-btn, +#beakerx-tree-widget .bx-panel .bx-panel-body .bx-form-row .bx-btn { + background-color: var(--jp-layout-color2); + color: var(--jp-content-font-color2); + border-color: var(--jp-border-color2); +} + +#beakerx-tree-widget .bx-panel .bx-panel-heading .bx-btn:hover, +#beakerx-tree-widget .bx-panel .bx-panel-body .bx-form-row .bx-btn:hover { + background-color: var(--jp-layout-color0); +} + +#beakerx-tree-widget .bx-panel .bx-panel-body .bx-form-row .bx-input-text { + background-color: var(--jp-layout-color1); + color: var(--jp-content-font-color1); + border-color: var(--jp-border-color2); +} + +.p-DataGrid { + border-color: transparent; +} + +.p-DataGrid-viewport { + border-color: var(--jp-border-color2); +} +.p-DataGrid-viewport input[type="text"] { + background-color: var(--jp-layout-color1); + color: var(--jp-content-font-color1); + border-color: var(--jp-border-color2); +} +.input-clear span { + color: var(--jp-content-font-color1); +} + +.p-DataGrid-scrollBar { + border-color: var(--jp-border-color2) !important; +} +.p-DataGrid-scrollBar .p-ScrollBar-button { + background-color: var(--jp-layout-color0); +} +.p-DataGrid-scrollBar .p-ScrollBar-track { + background-color: var(--jp-layout-color2); +} +.p-DataGrid-scrollBar .p-ScrollBar-thumb { + background-color: var(--jp-layout-color0); + border-color: var(--jp-border-color2) !important; +} + +.p-DataGrid-tooltip { + background-color: var(--jp-layout-color1); + color: var(--jp-content-font-color1); + border-color: var(--jp-border-color1); +} + +.p-DataGrid-scrollCorner { + background-color: var(--jp-layout-color0); + border-color: var(--jp-border-color2); +} + +.foldout-widget.widget-box > .foldout-label { + color: var(--jp-content-font-color1); + background-color: var(--jp-layout-color1); + border-color: var(--jp-border-color2); +} + +.foldout-widget.widget-box > .foldout-content { + border-color: var(--jp-border-color2); +} + +.foldout-widget.widget-box > .foldout-label:before { + background-image: var(--jp-icon-caretright); +} + +.foldout-widget.widget-box.active .foldout-label:before, +.foldout-widget.widget-box.active .widget-label:before { + background-image: var(--jp-icon-caretdown); +} + +.bx-button[class*="icon"] { + border-color: var(--jp-border-color2); +} + +.bko-table-menu { + background-color: var(--jp-layout-color0); +} + +.bko-table-menu .p-Menu-content .p-Menu-item { + color: var(--jp-ui-font-color0); +} + +.bko-table-menu .p-Menu-content .p-Menu-item:hover { + background-color: var(--jp-layout-color2); + color: var(--jp-ui-font-color0); +} + +.p-Menu-item.p-mod-active { + background-color: var(--jp-layout-color2); +} + +.bko-table-menu .p-Menu-content [data-type="submenu"] > .p-Menu-itemSubmenuIcon:after { + color: var(--jp-ui-font-color0); +} + +.text-line-style { + fill: var(--jp-content-font-color2); + stroke: var(--jp-content-font-color2); +} + +.beaker-fieldset { + border-color: var(--jp-border-color2); +} + +.beaker-fieldset legend { + color: var(--jp-content-font-color0); +} + +.flatpickr-calendar { + background-color: var(--jp-layout-color2); + color: var(--jp-content-font-color2); + border-color: var(--jp-border-color2); + -webkit-box-shadow: 1px 0 0 var(--jp-border-color2), -1px 0 0 var(--jp-border-color2), 0 1px 0 var(--jp-border-color2), 0 -1px 0 var(--jp-border-color2), 0 3px 13px rgba(0,0,0,0.08); + box-shadow: 1px 0 0 var(--jp-border-color2), -1px 0 0 var(--jp-border-color2), 0 1px 0 var(--jp-border-color2), 0 -1px 0 var(--jp-border-color2), 0 3px 13px rgba(0,0,0,0.08); +} + +.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+2) .flatpickr-day.inRange:nth-child(7n+1) { + -webkit-box-shadow: -2px 0 0 var(--jp-border-color2), 5px 0 0 var(--jp-border-color2); + box-shadow: -2px 0 0 var(--jp-border-color2), 5px 0 0 var(--jp-border-color2); +} +.flatpickr-calendar.showTimeInput.hasTime .flatpickr-time { + border-top: 1px solid var(--jp-border-color2); +} +.flatpickr-calendar.arrowTop:before { + border-bottom-color: var(--jp-border-color2); +} +.flatpickr-calendar.arrowTop:after { + border-bottom-color: var(--jp-border-color2); +} +.flatpickr-calendar.arrowBottom:before { + border-top-color: var(--jp-border-color2); +} +.flatpickr-calendar.arrowBottom:after { + border-top-color: var(--jp-border-color2); +} +.flatpickr-months .flatpickr-month { + color: rgba(255,255,255,0.9); + fill: rgba(255,255,255,0.9); +} +.flatpickr-months .flatpickr-prev-month, +.flatpickr-months .flatpickr-next-month { + color: rgba(255,255,255,0.9); + fill: rgba(255,255,255,0.9); +} + +.flatpickr-day { + color: rgba(255,255,255,0.9); +} + +.flatpickr-day.inRange, +.flatpickr-day.prevMonthDay.inRange, +.flatpickr-day.nextMonthDay.inRange, +.flatpickr-day.today.inRange, +.flatpickr-day.prevMonthDay.today.inRange, +.flatpickr-day.nextMonthDay.today.inRange, +.flatpickr-day:hover, +.flatpickr-day.prevMonthDay:hover, +.flatpickr-day.nextMonthDay:hover, +.flatpickr-day:focus, +.flatpickr-day.prevMonthDay:focus, +.flatpickr-day.nextMonthDay:focus { + background-color: var(--jp-layout-color3); + border-color: var(--jp-layout-color3); +} + +.flatpickr-day.disabled, +.flatpickr-day.disabled:hover, +.flatpickr-day.prevMonthDay, +.flatpickr-day.nextMonthDay, +.flatpickr-day.notAllowed, +.flatpickr-day.notAllowed.prevMonthDay, +.flatpickr-day.notAllowed.nextMonthDay { + color: rgba(255,255,255,0.3); +} + +span.flatpickr-weekday { + color: rgba(255,255,255,0.54); +} + +.lds-spinner div { + background-color: #2196F3; +} \ No newline at end of file diff --git a/js/_old/lab-theme-dark/tsconfig.json b/js/_old/lab-theme-dark/tsconfig.json new file mode 100644 index 0000000..678ce56 --- /dev/null +++ b/js/_old/lab-theme-dark/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfigbase", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src", + "lib": ["dom", "es2017"] + }, + "include": ["src/*"] +} \ No newline at end of file diff --git a/js/_old/lab-theme-light/.gitignore b/js/_old/lab-theme-light/.gitignore new file mode 100644 index 0000000..73334cb --- /dev/null +++ b/js/_old/lab-theme-light/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +*.tsbuildinfo \ No newline at end of file diff --git a/js/_old/lab-theme-light/.npmignore b/js/_old/lab-theme-light/.npmignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/js/_old/lab-theme-light/.npmignore @@ -0,0 +1 @@ +node_modules/ diff --git a/js/_old/lab-theme-light/README.md b/js/_old/lab-theme-light/README.md new file mode 100644 index 0000000..217be9b --- /dev/null +++ b/js/_old/lab-theme-light/README.md @@ -0,0 +1,3 @@ +# beakerx-jupyterlab-theme-light-extension + +A JupyterLab theme extension using default light-colored theme with beakerx additions. \ No newline at end of file diff --git a/js/_old/lab-theme-light/package-lock.json b/js/_old/lab-theme-light/package-lock.json new file mode 100644 index 0000000..8d4f806 --- /dev/null +++ b/js/_old/lab-theme-light/package-lock.json @@ -0,0 +1,1203 @@ +{ + "name": "beakerx-jupyterlab-theme-light-extension", + "version": "1.5.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/runtime": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz", + "integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@blueprintjs/core": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@blueprintjs/core/-/core-3.26.1.tgz", + "integrity": "sha512-f5U9RBvSMV/eLEXlBeaYbx+LRAcrsSrnK8gScM4Jwnuto6d9m6CeVxNe6YkQxvKOs4lhiC+UdPN2A9yJcXrGHA==", + "requires": { + "@blueprintjs/icons": "^3.16.0", + "@types/dom4": "^2.0.1", + "classnames": "^2.2", + "dom4": "^2.1.5", + "normalize.css": "^8.0.1", + "popper.js": "^1.15.0", + "react-lifecycles-compat": "^3.0.4", + "react-popper": "^1.3.7", + "react-transition-group": "^2.9.0", + "resize-observer-polyfill": "^1.5.1", + "tslib": "~1.10.0" + } + }, + "@blueprintjs/icons": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@blueprintjs/icons/-/icons-3.16.0.tgz", + "integrity": "sha512-VVBvgnpz8eQFhnX5OQGLdRqka9DU/CTtF3w9vnwKK2r9N/xRF0BPe9Ev1RzuUzP4GJG4GUIRaqiAX9+l/FviDg==", + "requires": { + "classnames": "^2.2", + "tslib": "~1.10.0" + } + }, + "@blueprintjs/select": { + "version": "3.12.3", + "resolved": "https://registry.npmjs.org/@blueprintjs/select/-/select-3.12.3.tgz", + "integrity": "sha512-WU6RGk2NSlPS4VjcpljmGQY+w4ezMCazoQfh37JMDNq5Xa/wzrKaleXifMmZUUWJNvjtsZ0sNDNLzI+DGuX0dQ==", + "requires": { + "@blueprintjs/core": "^3.26.1", + "classnames": "^2.2", + "tslib": "~1.10.0" + } + }, + "@jupyterlab/application": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/application/-/application-1.2.7.tgz", + "integrity": "sha512-V2nN9C7jirhAhrJPvp+2iBAJUcOruit5AL9tNYGjjATTetY/7pcuSOjh9yCS7eADp1TnsrzvcKeNJ6iH3S2FmQ==", + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/docregistry": "^1.2.7", + "@jupyterlab/rendermime": "^1.2.7", + "@jupyterlab/rendermime-interfaces": "^1.5.0", + "@jupyterlab/services": "^4.2.2", + "@jupyterlab/ui-components": "^1.2.1", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/application": "^1.7.0", + "@phosphor/commands": "^1.7.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "font-awesome": "~4.7.0" + } + }, + "@jupyterlab/apputils": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/apputils/-/apputils-1.2.7.tgz", + "integrity": "sha512-WdCJEWTiWgFlS/DTFYra+67UiG85xm+WnIutMnmuIz7Lq/3TI7yDsCpr3fd2AHOGg3E2OGo2qi5uNQoR+HPXRg==", + "requires": { + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/services": "^4.2.2", + "@jupyterlab/ui-components": "^1.2.1", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/domutils": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.0", + "@phosphor/virtualdom": "^1.2.0", + "@phosphor/widgets": "^1.9.0", + "@types/react": "~16.8.18", + "react": "~16.8.4", + "react-dom": "~16.8.4", + "sanitize-html": "~1.20.1" + } + }, + "@jupyterlab/codeeditor": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/codeeditor/-/codeeditor-1.2.0.tgz", + "integrity": "sha512-toejhF/a80X10SZyvEnsnnlS9SxR5W4cz67ju7e/2lsZ8RMwZEDDJAJXyW3mw/EEjt8oVRNP2QpM8L5clE9XyQ==", + "requires": { + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/dragdrop": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0" + } + }, + "@jupyterlab/codemirror": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/codemirror/-/codemirror-1.2.7.tgz", + "integrity": "sha512-CfGwmdXXkqLowrVXuXDIlftECgjQe8QZe+ZT67iTWkcFc7jyW3vDZTLy22n6inMx9lygEkWKr3UxIVdvTxMUeg==", + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/codeeditor": "^1.2.0", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@jupyterlab/statusbar": "^1.2.7", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "codemirror": "~5.47.0", + "react": "~16.8.4" + } + }, + "@jupyterlab/coreutils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/coreutils/-/coreutils-3.2.0.tgz", + "integrity": "sha512-LATiUsHuwze/h3JC2EZOBV+kGBoUKO3npqw/Pcgge4bz09xF/oTDrx4G8jl5eew3w1dCUNp9eLduNh8Orrw7xQ==", + "requires": { + "@phosphor/commands": "^1.7.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.0", + "ajv": "^6.5.5", + "json5": "^2.1.0", + "minimist": "~1.2.0", + "moment": "^2.24.0", + "path-posix": "~1.0.0", + "url-parse": "~1.4.3" + } + }, + "@jupyterlab/docregistry": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/docregistry/-/docregistry-1.2.7.tgz", + "integrity": "sha512-V9P3FJNjl06vCxJxTWzUhWE/tZwuNNCQmcgkddn8+1vjx/LlFC1UFpJuigkY+hUKIBGcIerx9tLjsEGNKdj4XA==", + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/codeeditor": "^1.2.0", + "@jupyterlab/codemirror": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@jupyterlab/rendermime": "^1.2.7", + "@jupyterlab/rendermime-interfaces": "^1.5.0", + "@jupyterlab/services": "^4.2.2", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0" + } + }, + "@jupyterlab/observables": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/observables/-/observables-2.4.0.tgz", + "integrity": "sha512-M/fhAnPqd6F4Zwt4IIsvHCkJmwbSw1Tko/hUXgdUQG86lPsJiTOh98sB3qwV1gtzb9oFF+kH21XsHnQZ6Yl6Pw==", + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0" + } + }, + "@jupyterlab/rendermime": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/rendermime/-/rendermime-1.2.7.tgz", + "integrity": "sha512-+EqMljh/ysXgTET2VeoQUtALl76vHsLZEIA84luxr7XzeqQ+L/imUZ3zEBJwvG30FLkoeAqZQFQ3pQsrVpGUQw==", + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/codemirror": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@jupyterlab/rendermime-interfaces": "^1.5.0", + "@jupyterlab/services": "^4.2.2", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "lodash.escape": "^4.0.1", + "marked": "^0.7.0" + } + }, + "@jupyterlab/rendermime-interfaces": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/rendermime-interfaces/-/rendermime-interfaces-1.5.0.tgz", + "integrity": "sha512-k6DjX/srKl1FA1CZyrAzz1qA2v1arXUIAmbEddZ5L3O+dnvDlOKjkI/NexaRQvmQ62aziSln+wKrr2P1JPNmGg==", + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/widgets": "^1.9.0" + } + }, + "@jupyterlab/services": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@jupyterlab/services/-/services-4.2.2.tgz", + "integrity": "sha512-1CNSsPbaAWaRkb3s5a+qpqMq/62RjLggL3MoSlGlp0Mi7jfJ8EJFIrnlRn4/a9/QwY3E3vsOFXaQ13v49PMWfA==", + "requires": { + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "node-fetch": "^2.6.0", + "ws": "^7.0.0" + } + }, + "@jupyterlab/statusbar": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/statusbar/-/statusbar-1.2.7.tgz", + "integrity": "sha512-a3vRn7upAIRBmDgqg6UKaQ4+bqFyjA9HFUlDQ65R/23S3NXlOGxR24RR1mnVHxZwu0sVTx/l407GdhCGxnLs3g==", + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/codeeditor": "^1.2.0", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/services": "^4.2.2", + "@jupyterlab/ui-components": "^1.2.1", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "react": "~16.8.4", + "typestyle": "^2.0.1" + } + }, + "@jupyterlab/ui-components": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jupyterlab/ui-components/-/ui-components-1.2.1.tgz", + "integrity": "sha512-GUtIRwTmFnlJaPUM8SiFw1STmsyMVGjchLKqIoQnn0qYAJvaSUGyRqqoSD5iIpwov6OHCOOyxH6fQ5OAtH1kwA==", + "requires": { + "@blueprintjs/core": "^3.9.0", + "@blueprintjs/select": "^3.3.0", + "@jupyterlab/coreutils": "^3.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/messaging": "^1.3.0", + "@phosphor/virtualdom": "^1.2.0", + "@phosphor/widgets": "^1.9.0", + "react": "~16.8.4", + "typestyle": "^2.0.1" + } + }, + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==" + }, + "@phosphor/application": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@phosphor/application/-/application-1.7.3.tgz", + "integrity": "sha512-ohxrW7rv5Tms4PSyPRZT6YArZQQGQNG4MgTeFzkoLJ+7mp/BcbFuvEoaV1/CUKQArofl0DCkKDOTOIkXP+/32A==", + "requires": { + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/widgets": "^1.9.3" + } + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/commands": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@phosphor/commands/-/commands-1.7.2.tgz", + "integrity": "sha512-iSyBIWMHsus323BVEARBhuVZNnVel8USo+FIPaAxGcq+icTSSe6+NtSxVQSmZblGN6Qm4iw6I6VtiSx0e6YDgQ==", + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/coreutils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/coreutils/-/coreutils-1.3.1.tgz", + "integrity": "sha512-9OHCn8LYRcPU/sbHm5v7viCA16Uev3gbdkwqoQqlV+EiauDHl70jmeL7XVDXdigl66Dz0LI11C99XOxp+s3zOA==" + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==" + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/keyboard": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@phosphor/keyboard/-/keyboard-1.1.3.tgz", + "integrity": "sha512-dzxC/PyHiD6mXaESRy6PZTd9JeK+diwG1pyngkyUf127IXOEzubTIbu52VSdpGBklszu33ws05BAGDa4oBE4mQ==" + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@phosphor/properties/-/properties-1.1.3.tgz", + "integrity": "sha512-GiglqzU77s6+tFVt6zPq9uuyu/PLQPFcqZt914ZhJ4cN/7yNI/SLyMzpYZ56IRMXvzK9TUgbRna6URE3XAwFUg==" + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + } + }, + "@types/dom4": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/dom4/-/dom4-2.0.1.tgz", + "integrity": "sha512-kSkVAvWmMZiCYtvqjqQEwOmvKwcH+V4uiv3qPQ8pAh1Xl39xggGEo8gHUqV4waYGHezdFw0rKBR8Jt0CrQSDZA==" + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + }, + "@types/react": { + "version": "16.8.25", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.25.tgz", + "integrity": "sha512-ydAAkLnNTC4oYSxJ3zwK/4QcVmEecACJ4ZdxXITbxz/dhahBSDKY6OQ1uawAW6rE/7kfHccxulYLSAIZVrSq0A==", + "requires": { + "@types/prop-types": "*", + "csstype": "^2.2.0" + } + }, + "ajv": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "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==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, + "codemirror": { + "version": "5.47.0", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.47.0.tgz", + "integrity": "sha512-kV49Fr+NGFHFc/Imsx6g180hSlkGhuHxTSDDmDHOuyln0MQYFLixDY4+bFkBVeCEiepYfDimAF/e++9jPJk4QA==" + }, + "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==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "create-react-context": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz", + "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==", + "requires": { + "gud": "^1.0.0", + "warning": "^4.0.3" + } + }, + "csstype": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz", + "integrity": "sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q==" + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "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==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" + }, + "entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" + } + } + }, + "dom4": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/dom4/-/dom4-2.1.5.tgz", + "integrity": "sha512-gJbnVGq5zaBUY0lUh0LUEVGYrtN75Ks8ZwpwOYvnVFrKy/qzXK4R/1WuLIFExWj/tBxbRAkTzZUGJHXmqsBNjQ==" + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" + }, + "free-style": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/free-style/-/free-style-3.1.0.tgz", + "integrity": "sha512-vJujYSIyT30iDoaoeigNAxX4yB1RUrh+N2ZMhIElMr3BvCuGXOw7XNJMEEJkDUeamK2Rnb/IKFGKRKlTWIGRWA==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "gud": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", + "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "requires": { + "minimist": "^1.2.5" + } + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "lodash.escape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", + "integrity": "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=" + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "marked": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", + "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "moment": { + "version": "2.25.3", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.25.3.tgz", + "integrity": "sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg==" + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "normalize.css": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", + "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-is": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", + "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-posix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-posix/-/path-posix-1.0.0.tgz", + "integrity": "sha1-BrJhE/Vr6rBCVFojv6iAA8ysJg8=" + }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" + }, + "postcss": { + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.29.tgz", + "integrity": "sha512-ba0ApvR3LxGvRMMiUa9n0WR4HjzcYm7tS+ht4/2Nd0NLtHpPIH77fuB9Xh1/yJVz9O/E/95Y/dn8ygWsyffXtw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" + }, + "react": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz", + "integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.13.6" + } + }, + "react-dom": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz", + "integrity": "sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.13.6" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-popper": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.7.tgz", + "integrity": "sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==", + "requires": { + "@babel/runtime": "^7.1.2", + "create-react-context": "^0.3.0", + "deep-equal": "^1.1.1", + "popper.js": "^1.14.4", + "prop-types": "^15.6.1", + "typed-styles": "^0.0.7", + "warning": "^4.0.2" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "sanitize-html": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.1.tgz", + "integrity": "sha512-txnH8TQjaQvg2Q0HY06G6CDJLVYCpbnxrdO0WN8gjCKaU5J0KbyGYhZxx5QJg3WLZ1lB7XU9kDkfrCXUozqptA==", + "requires": { + "chalk": "^2.4.1", + "htmlparser2": "^3.10.0", + "lodash.clonedeep": "^4.5.0", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.mergewith": "^4.6.1", + "postcss": "^7.0.5", + "srcset": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "scheduler": { + "version": "0.13.6", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", + "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "srcset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", + "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", + "requires": { + "array-uniq": "^1.0.2", + "number-is-nan": "^1.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimleft": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" + } + }, + "string.prototype.trimright": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, + "typed-styles": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz", + "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==" + }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "dev": true + }, + "typestyle": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typestyle/-/typestyle-2.1.0.tgz", + "integrity": "sha512-6uCYPdG4xWLeEcl9O0GtNFnNGhami+irKiLsXSuvWHC/aTS7wdj49WeikWAKN+xHN3b1hm+9v0svwwgSBhCsNA==", + "requires": { + "csstype": "2.6.9", + "free-style": "3.1.0" + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz", + "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + } + } +} diff --git a/js/_old/lab-theme-light/package.json b/js/_old/lab-theme-light/package.json new file mode 100644 index 0000000..838625a --- /dev/null +++ b/js/_old/lab-theme-light/package.json @@ -0,0 +1,42 @@ +{ + "name": "beakerx-jupyterlab-theme-light-extension", + "version": "1.5.0", + "description": "BeakerX: Beaker Light Theme Extension for JupyterLab", + "author": "Two Sigma Open Source, LLC", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "" + }, + "keywords": [], + "files": [ + "lib/*.d.ts", + "lib/*.js.map", + "lib/*.js", + "style/**/*.{css,ttf,woff,woff2}" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "style": "style/index.css", + "directories": { + "lib": "lib/" + }, + "scripts": { + "build": "tsc -b", + "clean": "rimraf lib", + "prepublish": "npm run build", + "watch": "tsc -b --watch" + }, + "dependencies": { + "@jupyterlab/application": "^1.2.7", + "@jupyterlab/apputils": "^1.2.7" + }, + "devDependencies": { + "rimraf": "^3.0.2", + "typescript": "^3.8.3" + }, + "jupyterlab": { + "extension": true, + "themePath": "style/index.css" + } +} diff --git a/js/_old/lab-theme-light/src/index.ts b/js/_old/lab-theme-light/src/index.ts new file mode 100644 index 0000000..467a553 --- /dev/null +++ b/js/_old/lab-theme-light/src/index.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; + +import { IThemeManager } from '@jupyterlab/apputils'; + +const plugin: JupyterFrontEndPlugin = { + id: 'beakerx:theme-light:plugin', + requires: [IThemeManager], + activate: (app: JupyterFrontEnd, manager: IThemeManager) => { + const originalStyle = '@jupyterlab/theme-light-extension/index.css'; + const beakerxStyle = 'beakerx-jupyterlab-theme-light-extension/index.css'; + + manager.register({ + name: 'BeakerX Light', + isLight: true, + themeScrollbars: false, + load: async () => { + await manager.loadCSS(originalStyle); + await manager.loadCSS(beakerxStyle); + + }, + unload: () => Promise.resolve(undefined) + }); + }, + autoStart: true +}; + +export default plugin; diff --git a/js/_old/lab-theme-light/style/base.css b/js/_old/lab-theme-light/style/base.css new file mode 100644 index 0000000..90aa293 --- /dev/null +++ b/js/_old/lab-theme-light/style/base.css @@ -0,0 +1,106 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.widget-text input[type="password"] { + box-sizing: border-box; + border: var(--jp-widgets-input-border-width) solid var(--jp-widgets-input-border-color); + background-color: var(--jp-widgets-input-background-color); + color: var(--jp-widgets-input-color); + font-size: var(--jp-widgets-font-size); + padding: var(--jp-widgets-input-padding) calc( var(--jp-widgets-input-padding) * 2 ); + flex-grow: 1; + min-width: 0; + flex-shrink: 1; + outline: none !important; + height: var(--jp-widgets-inline-height); + line-height: var(--jp-widgets-inline-height); +} + +.widget-text input[type="password"]:focus { + border-color: var(--jp-widgets-input-focus-border-color); +} + + +.widget-select-multiple select { + border: var(--jp-widgets-input-border-width) solid var(--jp-widgets-input-border-color); + background-color: var(--jp-widgets-input-background-color); + color: var(--jp-widgets-input-color); + font-size: var(--jp-widgets-font-size); +} + +.widget-select select:focus, +.widget-select-multiple select:focus { + border-color: var(--jp-widgets-input-focus-border-color); +} + +.easyform-combobox-input { + box-sizing: border-box; + border: var(--jp-widgets-input-border-width) solid var(--jp-widgets-input-border-color) !important; + background-color: var(--jp-widgets-input-background-color); + color: var(--jp-widgets-input-color); + font-size: var(--jp-widgets-font-size); + padding: var(--jp-widgets-input-padding) calc( var(--jp-widgets-input-padding) * 2 ); + outline: none !important; + height: calc(var(--jp-widgets-inline-height) + 2px); + line-height: var(--jp-widgets-inline-height); +} + +.easyform-combobox-toggle { + border-color: var(--jp-widgets-input-border-color); + background-color: var(--jp-widgets-input-background-color); +} +.easyform-combobox-toggle:focus, +.easyform-combobox-toggle:active, +.easyform-combobox-toggle:hover { + border-color: var(--jp-widgets-input-focus-border-color); + background-color: var(--jp-widgets-input-background-color); +} + +.beaker-easyform-container .datepicker-container.flatpickr input[type="text"] { + box-sizing: border-box; + border: var(--jp-widgets-input-border-width) solid var(--jp-widgets-input-border-color); + background-color: var(--jp-widgets-input-background-color); + color: var(--jp-widgets-input-color); + font-size: var(--jp-widgets-font-size); + padding: var(--jp-widgets-input-padding) calc( var(--jp-widgets-input-padding) * 2 ); + outline: none !important; + height: var(--jp-widgets-inline-height); + line-height: var(--jp-widgets-inline-height); + border-right-width: 0; +} + +.beaker-easyform-container .datepicker-container.flatpickr .date-picker-button { + border-color: var(--jp-widgets-input-border-color); + background-color: var(--jp-widgets-input-background-color); +} +.beaker-easyform-container .datepicker-container.flatpickr .date-picker-button:focus, +.beaker-easyform-container .datepicker-container.flatpickr .date-picker-button:active, +.beaker-easyform-container .datepicker-container.flatpickr .date-picker-button:hover { + border-color: var(--jp-widgets-input-focus-border-color); + outline: none !important; +} + +.hidden { + display: none !important; +} + +.bx-stats .bx-label, .bx-spark-stageProgressLabels .bx-label { + line-height: 1.3; +} + +.bx-stats .label { + box-sizing: border-box; +} \ No newline at end of file diff --git a/js/_old/lab-theme-light/style/fonts.css b/js/_old/lab-theme-light/style/fonts.css new file mode 100644 index 0000000..374f24a --- /dev/null +++ b/js/_old/lab-theme-light/style/fonts.css @@ -0,0 +1,78 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@font-face { + font-family: 'Roboto Mono'; + src: url('fonts/robotomono/robotomono.woff') format('woff'), + url('fonts/robotomono/robotomono.ttf') format('truetype'); + + font-style: normal; + font-weight: normal; +} + +@font-face { + font-family: 'Roboto Mono'; + src: url('fonts/robotomono/robotomono-bold.woff') format('woff'), + url('fonts/robotomono/robotomono-bold.ttf') format('truetype'); + font-weight: bold; +} + +@font-face { + font-family: 'Lato'; + src: url('fonts/lato/Lato-Regular.woff') format('woff'), + url('fonts/lato/Lato-Regular.ttf') format('truetype'); + + font-style: normal; + font-weight: normal; +} + +@font-face { + font-family: 'Lato'; + src: url('fonts/lato/Lato-Black.woff') format('woff'), + url('fonts/lato/Lato-Black.ttf') format('truetype'); + font-weight: bold; +} + +.improveFonts .CodeMirror pre, +.improveFonts .rendered_html pre, +.improveFonts .rendered_html code, +.improveFonts .output_area pre { + font-family: "Roboto Mono", monospace, sans-serif; +} + +div.output_text { + line-height: 1.3em; +} + +.improveFonts .widget-html-content, +.improveFonts .rendered_html, +.improveFonts .cm-header-1, +.improveFonts .cm-header-2, +.improveFonts .cm-header-3, +.improveFonts .cm-header-4, +.improveFonts .cm-header-5, +.improveFonts .cm-header-6 { + font-family: "Lato", Helvetica, sans-serif; +} + +.improveFonts .jp-RenderedText pre { + font-family: "Roboto Mono", monospace, sans-serif; +} + +.improveFonts .jp-RenderedMarkdown, +.improveFonts .jp-RenderedHTML { + font-family: "Lato", Helvetica, sans-serif; +} diff --git a/js/_old/lab-theme-light/style/fonts/lato/Lato-Black.ttf b/js/_old/lab-theme-light/style/fonts/lato/Lato-Black.ttf new file mode 100644 index 0000000..3f7819f Binary files /dev/null and b/js/_old/lab-theme-light/style/fonts/lato/Lato-Black.ttf differ diff --git a/js/_old/lab-theme-light/style/fonts/lato/Lato-Black.woff b/js/_old/lab-theme-light/style/fonts/lato/Lato-Black.woff new file mode 100644 index 0000000..a0ab25e Binary files /dev/null and b/js/_old/lab-theme-light/style/fonts/lato/Lato-Black.woff differ diff --git a/js/_old/lab-theme-light/style/fonts/lato/Lato-Regular.ttf b/js/_old/lab-theme-light/style/fonts/lato/Lato-Regular.ttf new file mode 100644 index 0000000..74decd9 Binary files /dev/null and b/js/_old/lab-theme-light/style/fonts/lato/Lato-Regular.ttf differ diff --git a/js/_old/lab-theme-light/style/fonts/lato/Lato-Regular.woff b/js/_old/lab-theme-light/style/fonts/lato/Lato-Regular.woff new file mode 100644 index 0000000..ae1307f Binary files /dev/null and b/js/_old/lab-theme-light/style/fonts/lato/Lato-Regular.woff differ diff --git a/js/_old/lab-theme-light/style/fonts/robotomono/robotomono-bold.ttf b/js/_old/lab-theme-light/style/fonts/robotomono/robotomono-bold.ttf new file mode 100755 index 0000000..07ef607 Binary files /dev/null and b/js/_old/lab-theme-light/style/fonts/robotomono/robotomono-bold.ttf differ diff --git a/js/_old/lab-theme-light/style/fonts/robotomono/robotomono-bold.woff b/js/_old/lab-theme-light/style/fonts/robotomono/robotomono-bold.woff new file mode 100644 index 0000000..1a76a58 Binary files /dev/null and b/js/_old/lab-theme-light/style/fonts/robotomono/robotomono-bold.woff differ diff --git a/js/_old/lab-theme-light/style/fonts/robotomono/robotomono.ttf b/js/_old/lab-theme-light/style/fonts/robotomono/robotomono.ttf new file mode 100644 index 0000000..aacaedf Binary files /dev/null and b/js/_old/lab-theme-light/style/fonts/robotomono/robotomono.ttf differ diff --git a/js/_old/lab-theme-light/style/fonts/robotomono/robotomono.woff b/js/_old/lab-theme-light/style/fonts/robotomono/robotomono.woff new file mode 100644 index 0000000..54ebc1d Binary files /dev/null and b/js/_old/lab-theme-light/style/fonts/robotomono/robotomono.woff differ diff --git a/js/_old/lab-theme-light/style/fonts/robotomono/robotomono.woff2 b/js/_old/lab-theme-light/style/fonts/robotomono/robotomono.woff2 new file mode 100644 index 0000000..73ad9a8 Binary files /dev/null and b/js/_old/lab-theme-light/style/fonts/robotomono/robotomono.woff2 differ diff --git a/js/_old/lab-theme-light/style/index.css b/js/_old/lab-theme-light/style/index.css new file mode 100644 index 0000000..93e1a1c --- /dev/null +++ b/js/_old/lab-theme-light/style/index.css @@ -0,0 +1,18 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import './fonts.css'; +@import './base.css'; \ No newline at end of file diff --git a/js/_old/lab-theme-light/tsconfig.json b/js/_old/lab-theme-light/tsconfig.json new file mode 100644 index 0000000..678ce56 --- /dev/null +++ b/js/_old/lab-theme-light/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfigbase", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src", + "lib": ["dom", "es2017"] + }, + "include": ["src/*"] +} \ No newline at end of file diff --git a/js/_old/lab/.gitignore b/js/_old/lab/.gitignore new file mode 100644 index 0000000..a11ebda --- /dev/null +++ b/js/_old/lab/.gitignore @@ -0,0 +1,3 @@ +/lib +/dist +node_modules/ diff --git a/js/_old/lab/.npmignore b/js/_old/lab/.npmignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/js/_old/lab/.npmignore @@ -0,0 +1 @@ +node_modules/ diff --git a/js/_old/lab/README.md b/js/_old/lab/README.md new file mode 100644 index 0000000..d7b6c45 --- /dev/null +++ b/js/_old/lab/README.md @@ -0,0 +1,21 @@ + + +# BeakerX JupyterLab extension NPM module + +BeakerX: Beaker Extensions for JupyterLab. +This NPM module has the BeakerX widgets. +See http://BeakerX.com diff --git a/js/_old/lab/package-lock.json b/js/_old/lab/package-lock.json new file mode 100644 index 0000000..108eb05 --- /dev/null +++ b/js/_old/lab/package-lock.json @@ -0,0 +1,3276 @@ +{ + "name": "beakerx-jupyterlab", + "version": "1.5.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/runtime": { + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz", + "integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@blueprintjs/core": { + "version": "3.26.1", + "resolved": "https://registry.npmjs.org/@blueprintjs/core/-/core-3.26.1.tgz", + "integrity": "sha512-f5U9RBvSMV/eLEXlBeaYbx+LRAcrsSrnK8gScM4Jwnuto6d9m6CeVxNe6YkQxvKOs4lhiC+UdPN2A9yJcXrGHA==", + "dev": true, + "requires": { + "@blueprintjs/icons": "^3.16.0", + "@types/dom4": "^2.0.1", + "classnames": "^2.2", + "dom4": "^2.1.5", + "normalize.css": "^8.0.1", + "popper.js": "^1.15.0", + "react-lifecycles-compat": "^3.0.4", + "react-popper": "^1.3.7", + "react-transition-group": "^2.9.0", + "resize-observer-polyfill": "^1.5.1", + "tslib": "~1.10.0" + } + }, + "@blueprintjs/icons": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@blueprintjs/icons/-/icons-3.16.0.tgz", + "integrity": "sha512-VVBvgnpz8eQFhnX5OQGLdRqka9DU/CTtF3w9vnwKK2r9N/xRF0BPe9Ev1RzuUzP4GJG4GUIRaqiAX9+l/FviDg==", + "dev": true, + "requires": { + "classnames": "^2.2", + "tslib": "~1.10.0" + } + }, + "@blueprintjs/select": { + "version": "3.12.3", + "resolved": "https://registry.npmjs.org/@blueprintjs/select/-/select-3.12.3.tgz", + "integrity": "sha512-WU6RGk2NSlPS4VjcpljmGQY+w4ezMCazoQfh37JMDNq5Xa/wzrKaleXifMmZUUWJNvjtsZ0sNDNLzI+DGuX0dQ==", + "dev": true, + "requires": { + "@blueprintjs/core": "^3.26.1", + "classnames": "^2.2", + "tslib": "~1.10.0" + } + }, + "@jupyter-widgets/base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@jupyter-widgets/base/-/base-2.0.2.tgz", + "integrity": "sha512-nNpD+RGJ0As74XxDSGMeObfXSZ8XPBFHJ1AyugzYxpmxIigB2n3DxTyonASkR/3hXwxl3/nXBxHGlxQGs/+nOA==", + "dev": true, + "requires": { + "@jupyterlab/services": "^4.0.0", + "@phosphor/coreutils": "^1.2.0", + "@phosphor/messaging": "^1.2.1", + "@phosphor/widgets": "^1.3.0", + "@types/backbone": "^1.4.1", + "@types/lodash": "^4.14.134", + "backbone": "1.2.3", + "base64-js": "^1.2.1", + "jquery": "^3.1.1", + "lodash": "^4.17.4" + } + }, + "@jupyter-widgets/controls": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@jupyter-widgets/controls/-/controls-1.5.3.tgz", + "integrity": "sha512-eigiIdhYeziKslm+pddZSz5RkzbkDB1C1O25K/S1+yzHm5DTR4iPt00vliOn2wokTvZUsmaC2JPL05eaVAf2iA==", + "dev": true, + "requires": { + "@jupyter-widgets/base": "^2.0.2", + "@phosphor/algorithm": "^1.1.0", + "@phosphor/domutils": "^1.1.0", + "@phosphor/messaging": "^1.2.1", + "@phosphor/signaling": "^1.2.0", + "@phosphor/widgets": "^1.3.0", + "d3-format": "^1.3.0", + "jquery": "^3.1.1", + "jquery-ui": "^1.12.1", + "underscore": "^1.8.3" + } + }, + "@jupyter-widgets/output": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@jupyter-widgets/output/-/output-2.0.1.tgz", + "integrity": "sha512-8yljIRbn98XNAoLnTOGEiBGKc7SH/ZEueatcJqu8FBMv3FN8hUKc2mgPLfcrlFRDwfe09p51j793oBUYdJUKPg==", + "dev": true, + "requires": { + "@jupyter-widgets/base": "^2.0.2" + } + }, + "@jupyterlab/application": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/application/-/application-1.2.7.tgz", + "integrity": "sha512-V2nN9C7jirhAhrJPvp+2iBAJUcOruit5AL9tNYGjjATTetY/7pcuSOjh9yCS7eADp1TnsrzvcKeNJ6iH3S2FmQ==", + "dev": true, + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/docregistry": "^1.2.7", + "@jupyterlab/rendermime": "^1.2.7", + "@jupyterlab/rendermime-interfaces": "^1.5.0", + "@jupyterlab/services": "^4.2.2", + "@jupyterlab/ui-components": "^1.2.1", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/application": "^1.7.0", + "@phosphor/commands": "^1.7.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "font-awesome": "~4.7.0" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + } + } + } + }, + "@jupyterlab/apputils": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/apputils/-/apputils-1.2.7.tgz", + "integrity": "sha512-WdCJEWTiWgFlS/DTFYra+67UiG85xm+WnIutMnmuIz7Lq/3TI7yDsCpr3fd2AHOGg3E2OGo2qi5uNQoR+HPXRg==", + "dev": true, + "requires": { + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/services": "^4.2.2", + "@jupyterlab/ui-components": "^1.2.1", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/domutils": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.0", + "@phosphor/virtualdom": "^1.2.0", + "@phosphor/widgets": "^1.9.0", + "@types/react": "~16.8.18", + "react": "~16.8.4", + "react-dom": "~16.8.4", + "sanitize-html": "~1.20.1" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + }, + "dependencies": { + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + } + } + } + } + }, + "@jupyterlab/attachments": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/attachments/-/attachments-1.2.7.tgz", + "integrity": "sha512-ARqSfUakCWyzlt648Ucwpt5SFEbGsFQL4XHhHJbCzwjYaF91P/RbVZvQO52O7RfdOX8EmB7UukzcJj/yH6F07A==", + "dev": true, + "requires": { + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@jupyterlab/rendermime": "^1.2.7", + "@jupyterlab/rendermime-interfaces": "^1.5.0", + "@phosphor/disposable": "^1.3.0", + "@phosphor/signaling": "^1.3.0" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + } + } + }, + "@jupyterlab/cells": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@jupyterlab/cells/-/cells-1.2.8.tgz", + "integrity": "sha512-wJNUJcByLNUlusaf7G2RQzwhbOqiIuJJVKndMD7lCvSlIQxqavpDZoYn+2kXva98tHURFgz+k9sgjui9HzvNhQ==", + "dev": true, + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/attachments": "^1.2.7", + "@jupyterlab/codeeditor": "^1.2.0", + "@jupyterlab/codemirror": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/filebrowser": "^1.2.7", + "@jupyterlab/observables": "^2.4.0", + "@jupyterlab/outputarea": "^1.2.8", + "@jupyterlab/rendermime": "^1.2.7", + "@jupyterlab/services": "^4.2.2", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/dragdrop": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/virtualdom": "^1.2.0", + "@phosphor/widgets": "^1.9.0", + "react": "~16.8.4" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + }, + "dependencies": { + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + } + } + } + } + }, + "@jupyterlab/codeeditor": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/codeeditor/-/codeeditor-1.2.0.tgz", + "integrity": "sha512-toejhF/a80X10SZyvEnsnnlS9SxR5W4cz67ju7e/2lsZ8RMwZEDDJAJXyW3mw/EEjt8oVRNP2QpM8L5clE9XyQ==", + "dev": true, + "requires": { + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/dragdrop": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + }, + "dependencies": { + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + } + } + } + } + }, + "@jupyterlab/codemirror": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/codemirror/-/codemirror-1.2.7.tgz", + "integrity": "sha512-CfGwmdXXkqLowrVXuXDIlftECgjQe8QZe+ZT67iTWkcFc7jyW3vDZTLy22n6inMx9lygEkWKr3UxIVdvTxMUeg==", + "dev": true, + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/codeeditor": "^1.2.0", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@jupyterlab/statusbar": "^1.2.7", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "codemirror": "~5.47.0", + "react": "~16.8.4" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + } + } + } + }, + "@jupyterlab/coreutils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/coreutils/-/coreutils-3.2.0.tgz", + "integrity": "sha512-LATiUsHuwze/h3JC2EZOBV+kGBoUKO3npqw/Pcgge4bz09xF/oTDrx4G8jl5eew3w1dCUNp9eLduNh8Orrw7xQ==", + "dev": true, + "requires": { + "@phosphor/commands": "^1.7.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.0", + "ajv": "^6.5.5", + "json5": "^2.1.0", + "minimist": "~1.2.0", + "moment": "^2.24.0", + "path-posix": "~1.0.0", + "url-parse": "~1.4.3" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + } + } + }, + "@jupyterlab/docmanager": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/docmanager/-/docmanager-1.2.7.tgz", + "integrity": "sha512-3Cgz6quFQ960tO3H4Ux4HRJzlHA7UZRh7HhEvG4376dY6t7Bh63AzESttovJVD2b+8Nh3BejetKKYBNq+clmiQ==", + "dev": true, + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/docregistry": "^1.2.7", + "@jupyterlab/services": "^4.2.2", + "@jupyterlab/statusbar": "^1.2.7", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "react": "~16.8.4" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + } + } + } + }, + "@jupyterlab/docregistry": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/docregistry/-/docregistry-1.2.7.tgz", + "integrity": "sha512-V9P3FJNjl06vCxJxTWzUhWE/tZwuNNCQmcgkddn8+1vjx/LlFC1UFpJuigkY+hUKIBGcIerx9tLjsEGNKdj4XA==", + "dev": true, + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/codeeditor": "^1.2.0", + "@jupyterlab/codemirror": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@jupyterlab/rendermime": "^1.2.7", + "@jupyterlab/rendermime-interfaces": "^1.5.0", + "@jupyterlab/services": "^4.2.2", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + } + } + } + }, + "@jupyterlab/filebrowser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/filebrowser/-/filebrowser-1.2.7.tgz", + "integrity": "sha512-TetSU2wMUrmvHV0FEA7k3nohA8gdt/URSJQZqBFGwp3RWKGY+bkVdahHXYJOJYvAzyksrnaSrCHt/etT0vTy8Q==", + "dev": true, + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/docmanager": "^1.2.7", + "@jupyterlab/docregistry": "^1.2.7", + "@jupyterlab/services": "^4.2.2", + "@jupyterlab/statusbar": "^1.2.7", + "@jupyterlab/ui-components": "^1.2.1", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/domutils": "^1.1.3", + "@phosphor/dragdrop": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "react": "~16.8.4" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + }, + "dependencies": { + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + } + } + } + } + }, + "@jupyterlab/notebook": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@jupyterlab/notebook/-/notebook-1.2.8.tgz", + "integrity": "sha512-dnWN+pmGVO1aaeG01+VIbmWCuHquWe4iPH35su7EdA40rbMOBRvym0f+jFa87pyG5iCTjEz9uLul9ixBUeEjdQ==", + "dev": true, + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/cells": "^1.2.8", + "@jupyterlab/codeeditor": "^1.2.0", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/docregistry": "^1.2.7", + "@jupyterlab/observables": "^2.4.0", + "@jupyterlab/rendermime": "^1.2.7", + "@jupyterlab/services": "^4.2.2", + "@jupyterlab/statusbar": "^1.2.7", + "@jupyterlab/ui-components": "^1.2.1", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/domutils": "^1.1.3", + "@phosphor/dragdrop": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.0", + "@phosphor/virtualdom": "^1.2.0", + "@phosphor/widgets": "^1.9.0", + "react": "~16.8.4" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + }, + "dependencies": { + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + } + } + } + } + }, + "@jupyterlab/observables": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/observables/-/observables-2.4.0.tgz", + "integrity": "sha512-M/fhAnPqd6F4Zwt4IIsvHCkJmwbSw1Tko/hUXgdUQG86lPsJiTOh98sB3qwV1gtzb9oFF+kH21XsHnQZ6Yl6Pw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + } + } + }, + "@jupyterlab/outputarea": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@jupyterlab/outputarea/-/outputarea-1.2.8.tgz", + "integrity": "sha512-kkSRkaJewfXoBev9qV9eN0/889IVAyMMls/3ysViFDtTRynjayJqGsYPGcS0jqT4hzA8pQuZrdKgwxVM+/hSvA==", + "dev": true, + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@jupyterlab/rendermime": "^1.2.7", + "@jupyterlab/rendermime-interfaces": "^1.5.0", + "@jupyterlab/services": "^4.2.2", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + } + } + } + }, + "@jupyterlab/rendermime": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/rendermime/-/rendermime-1.2.7.tgz", + "integrity": "sha512-+EqMljh/ysXgTET2VeoQUtALl76vHsLZEIA84luxr7XzeqQ+L/imUZ3zEBJwvG30FLkoeAqZQFQ3pQsrVpGUQw==", + "dev": true, + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/codemirror": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@jupyterlab/rendermime-interfaces": "^1.5.0", + "@jupyterlab/services": "^4.2.2", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "lodash.escape": "^4.0.1", + "marked": "^0.7.0" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + } + } + } + }, + "@jupyterlab/rendermime-interfaces": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/rendermime-interfaces/-/rendermime-interfaces-1.5.0.tgz", + "integrity": "sha512-k6DjX/srKl1FA1CZyrAzz1qA2v1arXUIAmbEddZ5L3O+dnvDlOKjkI/NexaRQvmQ62aziSln+wKrr2P1JPNmGg==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/widgets": "^1.9.0" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + } + } + } + }, + "@jupyterlab/services": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@jupyterlab/services/-/services-4.2.2.tgz", + "integrity": "sha512-1CNSsPbaAWaRkb3s5a+qpqMq/62RjLggL3MoSlGlp0Mi7jfJ8EJFIrnlRn4/a9/QwY3E3vsOFXaQ13v49PMWfA==", + "dev": true, + "requires": { + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/observables": "^2.4.0", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "node-fetch": "^2.6.0", + "ws": "^7.0.0" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + } + } + }, + "@jupyterlab/statusbar": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@jupyterlab/statusbar/-/statusbar-1.2.7.tgz", + "integrity": "sha512-a3vRn7upAIRBmDgqg6UKaQ4+bqFyjA9HFUlDQ65R/23S3NXlOGxR24RR1mnVHxZwu0sVTx/l407GdhCGxnLs3g==", + "dev": true, + "requires": { + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/codeeditor": "^1.2.0", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/services": "^4.2.2", + "@jupyterlab/ui-components": "^1.2.1", + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0", + "react": "~16.8.4", + "typestyle": "^2.0.1" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + } + } + } + }, + "@jupyterlab/ui-components": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jupyterlab/ui-components/-/ui-components-1.2.1.tgz", + "integrity": "sha512-GUtIRwTmFnlJaPUM8SiFw1STmsyMVGjchLKqIoQnn0qYAJvaSUGyRqqoSD5iIpwov6OHCOOyxH6fQ5OAtH1kwA==", + "dev": true, + "requires": { + "@blueprintjs/core": "^3.9.0", + "@blueprintjs/select": "^3.3.0", + "@jupyterlab/coreutils": "^3.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/messaging": "^1.3.0", + "@phosphor/virtualdom": "^1.2.0", + "@phosphor/widgets": "^1.9.0", + "react": "~16.8.4", + "typestyle": "^2.0.1" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + } + } + } + }, + "@phosphor/algorithm": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.1.3.tgz", + "integrity": "sha512-+dkdYTBglR+qGnLVQdCvYojNZMGxf+xSl1Jeksha3pm7niQktSFz2aR5gEPu/nI5LM8T8slTpqE4Pjvq8P+IVA==", + "dev": true + }, + "@phosphor/application": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@phosphor/application/-/application-1.7.3.tgz", + "integrity": "sha512-ohxrW7rv5Tms4PSyPRZT6YArZQQGQNG4MgTeFzkoLJ+7mp/BcbFuvEoaV1/CUKQArofl0DCkKDOTOIkXP+/32A==", + "dev": true, + "requires": { + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/widgets": "^1.9.3" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + } + } + } + }, + "@phosphor/collections": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.1.3.tgz", + "integrity": "sha512-J2U1xd2e5LtqoOJt4kynrjDNeHhVpJjuY2/zA0InS5kyOuWmvy79pt/KJ22n0LBNcU/fjkImqtQmbrC2Z4q2xQ==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.1.3" + } + }, + "@phosphor/commands": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@phosphor/commands/-/commands-1.7.2.tgz", + "integrity": "sha512-iSyBIWMHsus323BVEARBhuVZNnVel8USo+FIPaAxGcq+icTSSe6+NtSxVQSmZblGN6Qm4iw6I6VtiSx0e6YDgQ==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/signaling": "^1.3.1" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + } + } + }, + "@phosphor/coreutils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/coreutils/-/coreutils-1.3.1.tgz", + "integrity": "sha512-9OHCn8LYRcPU/sbHm5v7viCA16Uev3gbdkwqoQqlV+EiauDHl70jmeL7XVDXdigl66Dz0LI11C99XOxp+s3zOA==", + "dev": true + }, + "@phosphor/datagrid": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@phosphor/datagrid/-/datagrid-0.1.11.tgz", + "integrity": "sha512-mGJDbkYx5Wd4X4SO8FO+IHY3bq2ukx63/Fk98AZbunzLO2BgM22c60j/h8KS/nFAThJC0pDB4isG6w3Z62f/Zw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.0", + "@phosphor/domutils": "^1.1.3", + "@phosphor/dragdrop": "^1.4.0", + "@phosphor/messaging": "^1.3.0", + "@phosphor/signaling": "^1.3.0", + "@phosphor/widgets": "^1.9.0" + }, + "dependencies": { + "@phosphor/algorithm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/algorithm/-/algorithm-1.2.0.tgz", + "integrity": "sha512-C9+dnjXyU2QAkWCW6QVDGExk4hhwxzAKf5/FIuYlHAI9X5vFv99PYm0EREDxX1PbMuvfFBZhPNu0PvuSDQ7sFA==", + "dev": true + }, + "@phosphor/collections": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/collections/-/collections-1.2.0.tgz", + "integrity": "sha512-T9/0EjSuY6+ga2LIFRZ0xupciOR3Qnyy8Q95lhGTC0FXZUFwC8fl9e8On6IcwasCszS+1n8dtZUWSIynfgdpzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/commands": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@phosphor/commands/-/commands-1.7.2.tgz", + "integrity": "sha512-iSyBIWMHsus323BVEARBhuVZNnVel8USo+FIPaAxGcq+icTSSe6+NtSxVQSmZblGN6Qm4iw6I6VtiSx0e6YDgQ==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/signaling": "^1.3.1" + }, + "dependencies": { + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + } + } + }, + "@phosphor/disposable": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.3.1.tgz", + "integrity": "sha512-0NGzoTXTOizWizK/brKKd5EjJhuuEH4903tLika7q6wl/u0tgneJlTh7R+MBVeih0iNxtuJAfBa3IEY6Qmj+Sw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/signaling": "^1.3.1" + } + }, + "@phosphor/dragdrop": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.4.1.tgz", + "integrity": "sha512-77paMoubIWk7pdwA2GVFkqba1WP48hTZZvS17N30+KVOeWfSqBL3flPSnW2yC4y6FnOP2PFOCtuPIbQv+pYhCA==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1" + } + }, + "@phosphor/messaging": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.3.0.tgz", + "integrity": "sha512-k0JE+BTMKlkM335S2AmmJxoYYNRwOdW5jKBqLgjJdGRvUQkM0+2i60ahM45+J23atGJDv9esKUUBINiKHFhLew==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/collections": "^1.2.0" + } + }, + "@phosphor/signaling": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.3.1.tgz", + "integrity": "sha512-Eq3wVCPQAhUd9+gUGaYygMr+ov7dhSGblSBXiDzpZlSIfa8OVD4P3cCvYXr/acDTNmZ/gHTcSFO8/n3rDkeXzg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/virtualdom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.2.0.tgz", + "integrity": "sha512-L9mKNhK2XtVjzjuHLG2uYuepSz8uPyu6vhF4EgCP0rt0TiLYaZeHwuNu3XeFbul9DMOn49eBpye/tfQVd4Ks+w==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0" + } + }, + "@phosphor/widgets": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.9.3.tgz", + "integrity": "sha512-61jsxloDrW/+WWQs8wOgsS5waQ/MSsXBuhONt0o6mtdeL93HVz7CYO5krOoot5owammfF6oX1z0sDaUYIYgcPA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.2.0", + "@phosphor/commands": "^1.7.2", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.3.1", + "@phosphor/domutils": "^1.1.4", + "@phosphor/dragdrop": "^1.4.1", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.3.0", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.3.1", + "@phosphor/virtualdom": "^1.2.0" + }, + "dependencies": { + "@phosphor/domutils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.4.tgz", + "integrity": "sha512-ivwq5TWjQpKcHKXO8PrMl+/cKqbgxPClPiCKc1gwbMd+6hnW5VLwNG0WBzJTxCzXK43HxX18oH+tOZ3E04wc3w==", + "dev": true + } + } + } + } + }, + "@phosphor/disposable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@phosphor/disposable/-/disposable-1.2.0.tgz", + "integrity": "sha512-4PoWoffdrLyWOW5Qv7I8//owvZmv57YhaxetAMWeJl13ThXc901RprL0Gxhtue2ZxL2PtUjM1207HndKo2FVjA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.1.3", + "@phosphor/signaling": "^1.2.3" + } + }, + "@phosphor/domutils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@phosphor/domutils/-/domutils-1.1.3.tgz", + "integrity": "sha512-5CtLAhURQXXHhNXfQydDk/luG1cDVnhlu/qw7gz8/9pht0KXIAmNg/M0LKxx2oJ9+YMNCLVWxAnHAU0yrDpWSA==", + "dev": true + }, + "@phosphor/dragdrop": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@phosphor/dragdrop/-/dragdrop-1.3.3.tgz", + "integrity": "sha512-+SrlGsVQwY8OHCWxE/Zvihpk6Rc6bytJDqOUUTZqdL8hvM9QZeopAFioPDxuo1pTj87Um6cR4ekvbTU4KZ/90w==", + "dev": true, + "requires": { + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.2.0" + } + }, + "@phosphor/keyboard": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@phosphor/keyboard/-/keyboard-1.1.3.tgz", + "integrity": "sha512-dzxC/PyHiD6mXaESRy6PZTd9JeK+diwG1pyngkyUf127IXOEzubTIbu52VSdpGBklszu33ws05BAGDa4oBE4mQ==", + "dev": true + }, + "@phosphor/messaging": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@phosphor/messaging/-/messaging-1.2.3.tgz", + "integrity": "sha512-89Ps4uSRNOEQoepB/0SDoyPpNUWd6VZnmbMetmeXZJHsuJ1GLxtnq3WBdl7UCVNsw3W9NC610pWaDCy/BafRlg==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.1.3", + "@phosphor/collections": "^1.1.3" + } + }, + "@phosphor/properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@phosphor/properties/-/properties-1.1.3.tgz", + "integrity": "sha512-GiglqzU77s6+tFVt6zPq9uuyu/PLQPFcqZt914ZhJ4cN/7yNI/SLyMzpYZ56IRMXvzK9TUgbRna6URE3XAwFUg==", + "dev": true + }, + "@phosphor/signaling": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@phosphor/signaling/-/signaling-1.2.3.tgz", + "integrity": "sha512-DMwS0m9OgfY5ljpTsklRQPUQpTyg4obz85FyImRDacUVxUVbas95djIDEbU4s1TMzdHBBO+gfki3V4giXUvXzw==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.1.3" + } + }, + "@phosphor/virtualdom": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@phosphor/virtualdom/-/virtualdom-1.1.3.tgz", + "integrity": "sha512-V8PHhhnZCRa5esrC4q5VthqlLtxTo9ZV1mZ6b4YEloapca1S1nggZSQhrSlltXQjtYNUaWJZUZ/BlFD8wFtIEQ==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.1.3" + } + }, + "@phosphor/widgets": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@phosphor/widgets/-/widgets-1.8.1.tgz", + "integrity": "sha512-OY5T0nAioYTitPks/lCHm7a6QpjRB/XviIT2j6WtYm5J1U8MluIPpClqZ/NQbZfm23BYpmJeiQQyZA+5YphsDA==", + "dev": true, + "requires": { + "@phosphor/algorithm": "^1.1.3", + "@phosphor/commands": "^1.6.3", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/disposable": "^1.2.0", + "@phosphor/domutils": "^1.1.3", + "@phosphor/dragdrop": "^1.3.3", + "@phosphor/keyboard": "^1.1.3", + "@phosphor/messaging": "^1.2.3", + "@phosphor/properties": "^1.1.3", + "@phosphor/signaling": "^1.2.3", + "@phosphor/virtualdom": "^1.1.3" + } + }, + "@types/backbone": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/backbone/-/backbone-1.4.2.tgz", + "integrity": "sha512-+yfi5cLeIPU3JuCrFP4Bodpv8oLrE5sbiqQIMPvHIKaVCz0JCBt9GEQKZsz2haibrTV4Axks6ovoHc2yUbpWzg==", + "dev": true, + "requires": { + "@types/jquery": "*", + "@types/underscore": "*" + } + }, + "@types/codemirror": { + "version": "0.0.76", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.76.tgz", + "integrity": "sha512-k/hpUb+Ebyn9z63qM8IbsRiW0eYHZ+pi/1e2reGzBKAZJzkjWmNTXXqLLiNv5d9ekyxkajxRBr5Hu2WZq/nokw==", + "dev": true, + "requires": { + "@types/tern": "*" + } + }, + "@types/dom4": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/dom4/-/dom4-2.0.1.tgz", + "integrity": "sha512-kSkVAvWmMZiCYtvqjqQEwOmvKwcH+V4uiv3qPQ8pAh1Xl39xggGEo8gHUqV4waYGHezdFw0rKBR8Jt0CrQSDZA==", + "dev": true + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "@types/jquery": { + "version": "3.3.38", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.38.tgz", + "integrity": "sha512-nkDvmx7x/6kDM5guu/YpXkGZ/Xj/IwGiLDdKM99YA5Vag7pjGyTJ8BNUh/6hxEn/sEu5DKtyRgnONJ7EmOoKrA==", + "dev": true, + "requires": { + "@types/sizzle": "*" + } + }, + "@types/lodash": { + "version": "4.14.150", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.150.tgz", + "integrity": "sha512-kMNLM5JBcasgYscD9x/Gvr6lTAv2NVgsKtet/hm93qMyf/D1pt+7jeEZklKJKxMVmXjxbRVQQGfqDSfipYCO6w==", + "dev": true + }, + "@types/node": { + "version": "13.13.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.4.tgz", + "integrity": "sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true + }, + "@types/react": { + "version": "16.8.25", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.25.tgz", + "integrity": "sha512-ydAAkLnNTC4oYSxJ3zwK/4QcVmEecACJ4ZdxXITbxz/dhahBSDKY6OQ1uawAW6rE/7kfHccxulYLSAIZVrSq0A==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^2.2.0" + } + }, + "@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==", + "dev": true + }, + "@types/tern": { + "version": "0.23.3", + "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.3.tgz", + "integrity": "sha512-imDtS4TAoTcXk0g7u4kkWqedB3E4qpjXzCpD2LU5M5NAXHzCDsypyvXSaG7mM8DKYkCRa7tFp4tS/lp/Wo7Q3w==", + "dev": true, + "requires": { + "@types/estree": "*" + } + }, + "@types/underscore": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.10.0.tgz", + "integrity": "sha512-ZAbqul7QAKpM2h1PFGa5ETN27ulmqtj0QviYHasw9LffvXZvVHuraOx/FOsIPPDNGZN0Qo1nASxxSfMYOtSoCw==", + "dev": true + }, + "ajv": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "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==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "backbone": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.2.3.tgz", + "integrity": "sha1-wiz9B/yG676uYdGJKe0RXpmdZbk=", + "dev": true, + "requires": { + "underscore": ">=1.7.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==", + "dev": true + }, + "codemirror": { + "version": "5.47.0", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.47.0.tgz", + "integrity": "sha512-kV49Fr+NGFHFc/Imsx6g180hSlkGhuHxTSDDmDHOuyln0MQYFLixDY4+bFkBVeCEiepYfDimAF/e++9jPJk4QA==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "create-react-context": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz", + "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==", + "dev": true, + "requires": { + "gud": "^1.0.0", + "warning": "^4.0.3" + } + }, + "csstype": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz", + "integrity": "sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q==", + "dev": true + }, + "d3-format": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.4.tgz", + "integrity": "sha512-TWks25e7t8/cqctxCmxpUuzZN11QxIA7YrMbram94zMQ0PXjE4LVIMe/f6a4+xxL8HQ3OsAFULOINQi1pE62Aw==", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "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==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", + "dev": true + }, + "entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", + "dev": true + } + } + }, + "dom4": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/dom4/-/dom4-2.1.5.tgz", + "integrity": "sha512-gJbnVGq5zaBUY0lUh0LUEVGYrtN75Ks8ZwpwOYvnVFrKy/qzXK4R/1WuLIFExWj/tBxbRAkTzZUGJHXmqsBNjQ==", + "dev": true + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=", + "dev": true + }, + "free-style": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/free-style/-/free-style-3.1.0.tgz", + "integrity": "sha512-vJujYSIyT30iDoaoeigNAxX4yB1RUrh+N2ZMhIElMr3BvCuGXOw7XNJMEEJkDUeamK2Rnb/IKFGKRKlTWIGRWA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "gud": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", + "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "jquery": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==", + "dev": true + }, + "jquery-ui": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.12.1.tgz", + "integrity": "sha1-vLQEXI3QU5wTS8FIjN0+dop6nlE=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.escape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", + "integrity": "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=", + "dev": true + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=", + "dev": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, + "lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "marked": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", + "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "moment": { + "version": "2.25.3", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.25.3.tgz", + "integrity": "sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg==", + "dev": true + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "dev": true + }, + "normalize.css": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", + "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + }, + "object-is": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", + "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-posix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-posix/-/path-posix-1.0.0.tgz", + "integrity": "sha1-BrJhE/Vr6rBCVFojv6iAA8ysJg8=", + "dev": true + }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "dev": true + }, + "postcss": { + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.29.tgz", + "integrity": "sha512-ba0ApvR3LxGvRMMiUa9n0WR4HjzcYm7tS+ht4/2Nd0NLtHpPIH77fuB9Xh1/yJVz9O/E/95Y/dn8ygWsyffXtw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", + "dev": true + }, + "react": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz", + "integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.13.6" + } + }, + "react-dom": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz", + "integrity": "sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.13.6" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "dev": true + }, + "react-popper": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.7.tgz", + "integrity": "sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==", + "dev": true, + "requires": { + "@babel/runtime": "^7.1.2", + "create-react-context": "^0.3.0", + "deep-equal": "^1.1.1", + "popper.js": "^1.14.4", + "prop-types": "^15.6.1", + "typed-styles": "^0.0.7", + "warning": "^4.0.2" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "dev": true, + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "dev": true + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "sanitize-html": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.1.tgz", + "integrity": "sha512-txnH8TQjaQvg2Q0HY06G6CDJLVYCpbnxrdO0WN8gjCKaU5J0KbyGYhZxx5QJg3WLZ1lB7XU9kDkfrCXUozqptA==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "htmlparser2": "^3.10.0", + "lodash.clonedeep": "^4.5.0", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.mergewith": "^4.6.1", + "postcss": "^7.0.5", + "srcset": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "scheduler": { + "version": "0.13.6", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz", + "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "srcset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", + "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", + "dev": true, + "requires": { + "array-uniq": "^1.0.2", + "number-is-nan": "^1.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimleft": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" + } + }, + "string.prototype.trimright": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + }, + "typed-styles": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz", + "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==", + "dev": true + }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "dev": true + }, + "typestyle": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typestyle/-/typestyle-2.1.0.tgz", + "integrity": "sha512-6uCYPdG4xWLeEcl9O0GtNFnNGhami+irKiLsXSuvWHC/aTS7wdj49WeikWAKN+xHN3b1hm+9v0svwwgSBhCsNA==", + "dev": true, + "requires": { + "csstype": "2.6.9", + "free-style": "3.1.0" + } + }, + "underscore": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", + "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz", + "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + } + } +} diff --git a/js/_old/lab/package.json b/js/_old/lab/package.json new file mode 100644 index 0000000..a410276 --- /dev/null +++ b/js/_old/lab/package.json @@ -0,0 +1,55 @@ +{ + "name": "beakerx-jupyterlab", + "version": "1.5.0", + "description": "BeakerX: Beaker Extensions for JupyterLab", + "author": "Two Sigma Open Source, LLC", + "main": "dist/index.js", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "" + }, + "keywords": [ + "ipywidgets", + "jupyterlab", + "jupyterlab-extension" + ], + "scripts": { + "build": "npm run build:src", + "build:src": "rimraf dist && tsc --project src", + "prepublish": "npm run build:src", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "devDependencies": { + "@jupyter-widgets/base": "^1.2.5 || ^2.0.2", + "@jupyter-widgets/controls": "^1.5.3", + "@jupyter-widgets/output": "^2.0.1", + "@jupyterlab/application": "^1.2.7", + "@jupyterlab/apputils": "^1.2.7", + "@jupyterlab/cells": "^1.2.8", + "@jupyterlab/codemirror": "^1.2.7", + "@jupyterlab/coreutils": "^3.2.0", + "@jupyterlab/docregistry": "^1.2.7", + "@jupyterlab/notebook": "^1.2.8", + "@jupyterlab/outputarea": "^1.2.8", + "@jupyterlab/rendermime": "^1.2.7", + "@jupyterlab/rendermime-interfaces": "^1.5.0", + "@jupyterlab/services": "^4.2.2", + "@phosphor/application": "^1.7.3", + "@phosphor/coreutils": "^1.3.1", + "@phosphor/commands": "^1.7.2", + "@phosphor/datagrid": "^0.1.11", + "@types/node": "^13.13.4", + "@types/codemirror": "^0.0.76", + "rimraf": "^3.0.2", + "typescript": "^3.8.3" + }, + "jupyterlab": { + "extension": "dist/index.js", + "mimeExtension": "dist/javascriptRendererExtension.js" + }, + "files": [ + "dist/**/*.js", + "lib/*" + ] +} diff --git a/js/_old/lab/src/AccessTokenProvider.ts b/js/_old/lab/src/AccessTokenProvider.ts new file mode 100644 index 0000000..08f8ae9 --- /dev/null +++ b/js/_old/lab/src/AccessTokenProvider.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import GistPublishModal from "./plugin/gistPublish/gistPublishModal"; + +export default class AccessTokenProvider { + public getPersonalAccessToken(): Promise { + return new Promise(function(resolve, reject) { + new GistPublishModal().show((personalAccessToken) => { + resolve(personalAccessToken); + }); + }); + } +} diff --git a/js/_old/lab/src/beakerx.ts b/js/_old/lab/src/beakerx.ts new file mode 100644 index 0000000..0af0d79 --- /dev/null +++ b/js/_old/lab/src/beakerx.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const beakerx = require('../lib/index.js'); + +export default beakerx; diff --git a/js/_old/lab/src/beakerx_tabledisplay.ts b/js/_old/lab/src/beakerx_tabledisplay.ts new file mode 100644 index 0000000..ccfa3a3 --- /dev/null +++ b/js/_old/lab/src/beakerx_tabledisplay.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const beakerx_tabledisplay = require('../lib/td_index.js'); + +export default beakerx_tabledisplay; diff --git a/js/_old/lab/src/global.env.ts b/js/_old/lab/src/global.env.ts new file mode 100644 index 0000000..a30cac1 --- /dev/null +++ b/js/_old/lab/src/global.env.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +declare interface Window { + beakerx: any; + beakerxHolder: any +} + +type Proxy = { + get(): T; + set(value: T): void; +} + +interface ProxyConstructor { + revocable(target: T, handler: ProxyHandler): { proxy: T; revoke: () => void; }; + new (target: T, handler: ProxyHandler): T; +} + +declare var Proxy: ProxyConstructor; diff --git a/js/_old/lab/src/index.ts b/js/_old/lab/src/index.ts new file mode 100644 index 0000000..586ea7c --- /dev/null +++ b/js/_old/lab/src/index.ts @@ -0,0 +1,62 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import './global.env'; +import {ILabShell, JupyterFrontEnd, JupyterFrontEndPlugin} from '@jupyterlab/application'; +import { ISettingRegistry } from "@jupyterlab/coreutils"; +import BeakerxExtension from './plugin'; +import BeakerxTreeJupyterLabPlugin from "./tree"; +import RequirejsLoader from "./plugin/requirejs"; +import {IJupyterWidgetRegistry} from "@jupyter-widgets/base"; +import beakerx from "./beakerx"; +import beakerx_tabledisplay from "./beakerx_tabledisplay"; + +export const beakerx_ext: JupyterFrontEndPlugin = { + id: 'beakerx', + requires: [IJupyterWidgetRegistry, ISettingRegistry, ILabShell], + activate: (app: JupyterFrontEnd, widgets: IJupyterWidgetRegistry, settings: ISettingRegistry, labShell: ILabShell): void => { + widgets.registerWidget({ + name: 'beakerx', + version: beakerx.version, + exports: beakerx + }); + widgets.registerWidget({ + name: 'beakerx_tabledisplay', + version: beakerx.version, + exports: beakerx_tabledisplay + }); + + app.docRegistry.addWidgetExtension('Notebook', new BeakerxExtension(app, settings, labShell)); + }, + autoStart: true +}; + +export const tree_ext: JupyterFrontEndPlugin = BeakerxTreeJupyterLabPlugin; + +export const requirejs_ext: JupyterFrontEndPlugin = { + id: 'beakerx:requirejs', + autoStart: true, + requires: [], + activate: (app: JupyterFrontEnd): Promise => { + return RequirejsLoader.load(); + } +}; + +export default [ + requirejs_ext, + beakerx_ext, + tree_ext, +]; diff --git a/js/_old/lab/src/interface/messageData.ts b/js/_old/lab/src/interface/messageData.ts new file mode 100644 index 0000000..73ceffb --- /dev/null +++ b/js/_old/lab/src/interface/messageData.ts @@ -0,0 +1,9 @@ +export interface messageData { + state?: messageState +} + +export interface messageState { + name?: string, + value?: any, + runByTag?: string +} diff --git a/js/_old/lab/src/javascriptRendererExtension.ts b/js/_old/lab/src/javascriptRendererExtension.ts new file mode 100644 index 0000000..c3d83b9 --- /dev/null +++ b/js/_old/lab/src/javascriptRendererExtension.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { IRenderMime } from '@jupyterlab/rendermime-interfaces'; +import { RenderedJavaScript } from '@jupyterlab/rendermime' + +export const TEXT_JAVASCRIPT_MIMETYPE = 'text/javascript'; +export const APPLICATION_JAVASCRIPT_MIMETYPE = 'application/javascript'; + +export class BeakerxRenderedJavascript extends RenderedJavaScript { + render(model: IRenderMime.IMimeModel): Promise { + const evalInContext = function(code: string) { + return eval(code); + }.bind(this); + + try { + evalInContext(String(model.data[this.mimeType])); + } catch (e) { + this.node.innerHTML = `
${String(e.message)} - please check console
`; + } + + return Promise.resolve(undefined); + } +} + +/** + * A mime renderer factory for text/javascript data. + */ +export +const rendererFactory: IRenderMime.IRendererFactory = { + safe: true, + mimeTypes: [TEXT_JAVASCRIPT_MIMETYPE, APPLICATION_JAVASCRIPT_MIMETYPE], + createRenderer: options => new BeakerxRenderedJavascript(options) +}; + +const extension: IRenderMime.IExtension = { + id: 'beakerx.javascript:factory', + rendererFactory, + rank: 0, + dataType: 'string' +}; + +export default extension; diff --git a/js/_old/lab/src/plugin/UIOptionFeaturesHelper.ts b/js/_old/lab/src/plugin/UIOptionFeaturesHelper.ts new file mode 100644 index 0000000..444ad68 --- /dev/null +++ b/js/_old/lab/src/plugin/UIOptionFeaturesHelper.ts @@ -0,0 +1,193 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as GistPublish from "./gistPublish/index"; +import { ISettingRegistry, PageConfig } from "@jupyterlab/coreutils"; +import { ServerConnection } from "@jupyterlab/services"; +import { NotebookPanel } from "@jupyterlab/notebook"; +import { CodeCell } from "@jupyterlab/cells"; +import {ILabShell, JupyterFrontEnd} from "@jupyterlab/application"; + +export default class UIOptionFeaturesHelper { + + private showPublicationFeature: ShowPublicationFeature; + private autoCloseBracketsFeature: AutoCloseBracketsFeature; + private autoSaveFeature: AutoSaveFeature; + private improveFontsFeature: ImproveFontsFeature; + + constructor( + private app: JupyterFrontEnd, + private settings: ISettingRegistry, + private panel: NotebookPanel, + private labShell: ILabShell + ) { + } + + public registerFeatures(): void { + + this.showPublicationFeature = new ShowPublicationFeature(this.panel, this.app.commands); + this.autoCloseBracketsFeature = new AutoCloseBracketsFeature(this.panel); + this.autoSaveFeature = new AutoSaveFeature(this.settings, this.app.commands); + this.improveFontsFeature = new ImproveFontsFeature(); + + this.labShell.activeChanged.connect((sender, args) => { + if (args.newValue !== this.panel) { + return; + } + document.body.dataset.notebookPath = this.panel.context.path; + this.onActiveChanged(); + }); + + this + .loadSettings() + .then((data) => { + this.initFeatures(data); + }) + .catch((e) => { + console.log(e); + }); + } + + private onActiveChanged(): void { + this.loadSettings() + .then((data) => { + this.updateFeatures(data); + }) + .catch((e) => { + console.log(e); + }); + } + + private initFeatures(data): void { + this.showPublicationFeature.init(data.beakerx.ui_options.show_publication); + this.autoCloseBracketsFeature.init(data.beakerx.ui_options.auto_close) + this.autoSaveFeature.init(data.beakerx.ui_options.auto_save); + this.improveFontsFeature.init(data.beakerx.ui_options.improve_fonts); + } + + private updateFeatures(data): void { + this.showPublicationFeature.update(data.beakerx.ui_options.show_publication); + this.autoCloseBracketsFeature.update(data.beakerx.ui_options.auto_close) + this.autoSaveFeature.update(data.beakerx.ui_options.auto_save); + this.improveFontsFeature.update(data.beakerx.ui_options.improve_fonts); + } + + private loadSettings(): Promise { + return new Promise((resolve, reject) => { + let serverSettings = ServerConnection.makeSettings(); + let settingsUrl = `${PageConfig.getBaseUrl()}beakerx/settings`; + ServerConnection.makeRequest( + settingsUrl, + { method: 'GET' }, + serverSettings + ) + .then(response => resolve(response.json())) + .catch(reason => { reject(reason); console.log(reason); }); + }); + } +} + +interface IUIOptionsFeature { + init(isEnabled: boolean): void; + update(isEnabled: boolean): void; +} + +class ShowPublicationFeature implements IUIOptionsFeature { + + constructor(private panel: NotebookPanel, private commands) {} + + public init(isEnabled: boolean) { + GistPublish.registerFeature(this.panel, this.commands, isEnabled); + } + + public update(isEnabled: boolean): void { + GistPublish.registerFeature(this.panel, this.commands, isEnabled); + } +} + +class AutoCloseBracketsFeature implements IUIOptionsFeature { + + constructor(private panel: NotebookPanel) {} + + public init(isEnabled: boolean): void { + this.setOptionForNewAndExistingCells(isEnabled); + } + + public update(isEnabled: boolean) { + this.setOptionForNewAndExistingCells(isEnabled); + } + + private setOptionForNewAndExistingCells(autoClosingBrackets: boolean) { + this.panel.content.editorConfig.code.autoClosingBrackets = autoClosingBrackets; + + for (let cell of this.getCodeCells()) { + cell.editor.setOption('autoClosingBrackets', autoClosingBrackets); + } + } + + private getCodeCells(): CodeCell[] { + if (this.panel.isDisposed) { return []; } + const cells = this.panel.content.widgets || []; + return cells.filter((cell) => { + return (cell instanceof CodeCell); + }); + } +} + +class AutoSaveFeature implements IUIOptionsFeature { + + private pluginId: string = '@jupyterlab/docmanager-extension:plugin'; + private commandId: string = 'docmanager:toggle-autosave'; + + constructor(private settings: ISettingRegistry, private commands) { + } + + public init(isEnabled: boolean): void { + this.runToggleAutoSaveCommandIfNeeded(isEnabled); + } + + public update(isEnabled: boolean): void { + this.runToggleAutoSaveCommandIfNeeded(isEnabled); + } + + private runToggleAutoSaveCommandIfNeeded(isEnabled: boolean): void { + this.settings + .get(this.pluginId, 'autosave') + .then((val) => { + if (val.composite !== isEnabled) { + this.commands.execute(this.commandId); + } + }); + } +} + +class ImproveFontsFeature implements IUIOptionsFeature { + + public init(isEnabled: boolean): void { + if (isEnabled) { + document.body.classList.add('improveFonts'); + } + } + + public update(isEnabled: boolean): void { + if (isEnabled) { + document.body.classList.add('improveFonts'); + } else { + document.body.classList.remove('improveFonts'); + } + } + +} diff --git a/js/_old/lab/src/plugin/autotranslation.ts b/js/_old/lab/src/plugin/autotranslation.ts new file mode 100644 index 0000000..e6e0f51 --- /dev/null +++ b/js/_old/lab/src/plugin/autotranslation.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2018 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/// + +import {BEAKER_AUTOTRANSLATION} from "./comm"; + +export namespace Autotranslation { + export const LOCK_PROXY = 'LOCK_PROXY'; + export const TABLE_FOCUSED = 'tableFocused'; + + export function proxify(beakerxInstance: any, kernelInstance): Proxy { + let autotranslationComm = kernelInstance.connectToComm(BEAKER_AUTOTRANSLATION); + autotranslationComm.open(); + + const handler = { + get(obj, prop) { + return prop in obj ? obj[prop] : undefined; + }, + + set(obj, prop, value) { + obj[prop] = value; + + if (prop !== LOCK_PROXY && prop !== TABLE_FOCUSED && !window.beakerx[LOCK_PROXY]) { + autotranslationComm.send({ name: prop, value }); + } + + return true; + } + }; + + return new Proxy(beakerxInstance, handler); + } +} diff --git a/js/_old/lab/src/plugin/codeCells.ts b/js/_old/lab/src/plugin/codeCells.ts new file mode 100644 index 0000000..c69ca8a --- /dev/null +++ b/js/_old/lab/src/plugin/codeCells.ts @@ -0,0 +1,90 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Notebook } from "@jupyterlab/notebook"; +import { JSONArray } from '@phosphor/coreutils'; +import { Cell, CodeCell, CodeCellModel } from '@jupyterlab/cells'; +import beakerx from "./../beakerx"; +import {PageConfig} from "@jupyterlab/coreutils"; + +export function sendJupyterCodeCells( + notebook: Notebook, + filter: string, + url:string +): void { + + const codeCells = getCodeCellsByTag(notebook, filter) + .map((cell: CodeCell): object => ({ + cell_type: cell.model.type, + ...cell.model.toJSON() + }) + ); + + const data: { code_cells: any , url: string } = + { + code_cells: codeCells, + url : url + }; + + let service = new BeakerxRestHandler(); + service.post(data) +} + +export function getCodeCellsByTag(notebook: Notebook, tag: string): Cell[] { + let cells = notebook.widgets || []; + + return cells.filter((cell) => { + const tags: any = cell.model.metadata.get('tags'); + + return ( + cell.model instanceof CodeCellModel && + tags && tags.length && tags.includes(tag) + ); + }); +} + +export class BeakerxRestHandler { + + private api: any; + + constructor() { + this.setApi() + } + + private setApi() { + let baseUrl; + + if (this.api) { + return; + } + + try { + PageConfig.getOption('pageUrl'); + baseUrl = PageConfig.getBaseUrl(); + } catch (e) { + baseUrl = `${window.location.origin}/`; + } + + this.api = new beakerx.BeakerXApi(baseUrl); + } + + public post(data) { + this.api + .restService(data) + .catch((err) => { console.log(err) }); + } + +} \ No newline at end of file diff --git a/js/_old/lab/src/plugin/codeEditor.ts b/js/_old/lab/src/plugin/codeEditor.ts new file mode 100644 index 0000000..81732d7 --- /dev/null +++ b/js/_old/lab/src/plugin/codeEditor.ts @@ -0,0 +1,116 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CodeMirrorEditor } from "@jupyterlab/codemirror"; +import { Cell, CodeCell } from '@jupyterlab/cells'; +import { NotebookPanel } from "@jupyterlab/notebook"; +import CodeMirror = require("codemirror"); + +const LINE_COMMENT_CHAR = '//'; +const LINE_MAGIC_MODE = 'line_magic'; + +const getCodeCellsWithCodeMirrorEditor = (panel: NotebookPanel): CodeCell[] => ( + ((panel.content.widgets || []) as Cell[]) + .filter(cell => (cell instanceof CodeCell)) as CodeCell[]) + .filter(cell => (cell.editor instanceof CodeMirrorEditor)); + +export const registerCommentOutCmd = (panel: NotebookPanel): void => { + getCodeCellsWithCodeMirrorEditor(panel).forEach(setCodeMirrorLineComment); +}; + +const setCodeMirrorLineComment = (cell: CodeCell): void => { + const cmEditor: CodeMirror.Editor = (cell.editor as CodeMirrorEditor).editor; + const doc: CodeMirror.Doc = cmEditor.getDoc(); + const mode = doc.getMode(); + if(mode.lineComment) { + return; + } + CodeMirror.extendMode(mode.name, { 'lineComment': LINE_COMMENT_CHAR }); + mode.lineComment = LINE_COMMENT_CHAR; + doc['mode'] = mode; +}; + +export function extendHighlightModes(panel: NotebookPanel) { + getCodeCellsWithCodeMirrorEditor(panel).forEach(setLineMagicForCell); + CodeMirror.defineInitHook(addLineMagicsOverlay); +} + +function setLineMagicForCell(cell: CodeCell) { + addLineMagicsOverlay((cell.editor).editor); +} + +const lineMagicOverlay = { + startState() { + return { firstMatched: false, inMagicLine: false }; + }, + + token(stream, state) { + if (stream.match(/^%(%classpath|%spark|\w+)/, false)) { + state.inMagicLine = true; + } + + if (state.inMagicLine) { + stream.eat(() => true); + + if (stream.eol()) { + state.inMagicLine = false; + } + + return LINE_MAGIC_MODE; + } + + stream.skipToEnd(); + + return null; + } +}; + +export function autoHighlightLineMagics(editor: CodeMirror.Editor) { + const mode = editor.getOption('mode'); + + if (mode === LINE_MAGIC_MODE) { + return; + } + + const re = /^%(%classpath|%spark|\w+)/; + + editor.getDoc().eachLine(line => { + if (line && line.text.match(re) !== null) { + // Add an overlay mode to recognize the first line as "line magic" instead + // of the mode used for the rest of the cell. + CodeMirror.defineMode(LINE_MAGIC_MODE, (config) => { + return CodeMirror.overlayMode(CodeMirror.getMode(config, mode), lineMagicOverlay); + }); + + editor.setOption('mode', LINE_MAGIC_MODE); + + return false; + } + }); +} + +export function addLineMagicsOverlay(editor: CodeMirror.Editor) { + autoHighlightLineMagics(editor); + + editor.off("focus", autoHighlightLineMagics); + editor.on("focus", autoHighlightLineMagics); + editor.off("change", autoHighlightLineMagics); + editor.on("change", autoHighlightLineMagics); + editor.off("blur", autoHighlightLineMagics); + editor.on("blur", autoHighlightLineMagics); + + editor.refresh(); +} diff --git a/js/_old/lab/src/plugin/comm.ts b/js/_old/lab/src/plugin/comm.ts new file mode 100644 index 0000000..5af53eb --- /dev/null +++ b/js/_old/lab/src/plugin/comm.ts @@ -0,0 +1,121 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DocumentRegistry } from '@jupyterlab/docregistry'; +import { INotebookModel, Notebook, NotebookPanel } from '@jupyterlab/notebook'; +import { showDialog, Dialog, IClientSession } from '@jupyterlab/apputils'; +import { sendJupyterCodeCells, getCodeCellsByTag } from './codeCells'; +import {messageData, messageState} from '../interface/messageData'; +import { Kernel } from "@jupyterlab/services"; +import { CodeCell } from '@jupyterlab/cells'; +import {Autotranslation} from "./autotranslation"; +import LOCK_PROXY = Autotranslation.LOCK_PROXY; + +export const BEAKER_GETCODECELLS = 'beakerx.getcodecells'; +export const BEAKER_AUTOTRANSLATION = 'beakerx.autotranslation'; +export const BEAKER_TAG_RUN = 'beakerx.tag.run'; + +const getMsgHandlers = ( + session: IClientSession, + kernelInstance: Kernel.IKernelConnection, + notebook: Notebook +) => ({ + [BEAKER_GETCODECELLS]: (msg) => { + const state: messageState = msg.content.data.state; + + if (!state.name) { + return; + } + + if(state.name == "CodeCells") { + sendJupyterCodeCells(notebook, JSON.parse(state.value), msg.content.data.url); + } + + window.beakerx[state.name] = JSON.parse(state.value); + }, + + [BEAKER_AUTOTRANSLATION]: (msg) => { + const state: messageState = msg.content.data.state; + + window.beakerx[LOCK_PROXY] = true; + window.beakerx[state.name] = JSON.parse(state.value); + window.beakerx[LOCK_PROXY] = false; + }, + + [BEAKER_TAG_RUN]: (msg) => { + const data: messageData = msg.content.data; + + if(!data.state || !data.state.runByTag) { + return; + } + + const matchedCells = getCodeCellsByTag(notebook, data.state.runByTag); + + if (matchedCells.length === 0) { + showDialog({ + title: 'No cell with the tag !', + body: 'Tag: ' + data.state.runByTag, + buttons: [ Dialog.okButton({ label: 'OK' }) ] + }); + } else { + matchedCells.forEach((cell) => { + cell instanceof CodeCell && CodeCell.execute(cell, session); + }); + } + } +}); + +export const registerCommTargets = async (panel: NotebookPanel, context: DocumentRegistry.IContext): Promise => { + const session = context.session; + const kernelInstance = session.kernel; + const notebook = panel.content; + const msgHandlers = getMsgHandlers(session, kernelInstance, notebook); + + kernelInstance.registerCommTarget(BEAKER_GETCODECELLS, (comm) => { + comm.onMsg = msgHandlers[BEAKER_GETCODECELLS]; + }); + + kernelInstance.registerCommTarget(BEAKER_AUTOTRANSLATION, (comm) => { + comm.onMsg = msgHandlers[BEAKER_AUTOTRANSLATION] + }); + + kernelInstance.registerCommTarget(BEAKER_TAG_RUN, (comm) => { + comm.onMsg = msgHandlers[BEAKER_TAG_RUN] + }); + + let msg = await kernelInstance.requestCommInfo({}); + if (msg.content.status === 'ok') { + assignMsgHandlersToExistingComms(msg.content.comms, kernelInstance, msgHandlers); + } +}; + +const assignMsgHandlersToExistingComms = ( + comms: Object, + kernelInstance: Kernel.IKernelConnection, + msgHandlers: Object +): void => { + let comm; + for (let commId in comms) { + comm = kernelInstance.connectToComm(comms[commId].target_name, commId); + assignMsgHandlerToComm(comm, msgHandlers[comm.targetName]); + } +}; + +const assignMsgHandlerToComm = (comm, handler): void => { + if (handler) { + comm.onMsg = handler; + } +}; diff --git a/js/_old/lab/src/plugin/gistPublish/Modal.ts b/js/_old/lab/src/plugin/gistPublish/Modal.ts new file mode 100644 index 0000000..9f470fa --- /dev/null +++ b/js/_old/lab/src/plugin/gistPublish/Modal.ts @@ -0,0 +1,57 @@ +/* + * Copyright 2018 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Dialog } from '@jupyterlab/apputils'; + +export default class Modal extends Dialog { + submitHandler: Function; + + constructor({ submitHandler, ...options }) { + super(options); + + this.submitHandler = submitHandler; + } + + protected _evtClick(event: MouseEvent): void { + let content = this.node.getElementsByClassName('jp-Dialog-content')[0] as HTMLElement; + + if (!content.contains(event.target as HTMLElement)) { + event.stopPropagation(); + event.preventDefault(); + + return; + } + + for (let buttonNode of this['_buttonNodes']) { + if (buttonNode.contains(event.target as HTMLElement)) { + this.submitHandler && this.submitHandler(event); + break; + } + } + } + + protected _evtKeydown(event: KeyboardEvent): void { + switch (event.keyCode) { + case 13: // Enter. + event.stopPropagation(); + event.preventDefault(); + this.submitHandler && this.submitHandler(event); + break; + default: + super._evtKeydown(event); + } + } +} diff --git a/js/_old/lab/src/plugin/gistPublish/gistPublishModal.ts b/js/_old/lab/src/plugin/gistPublish/gistPublishModal.ts new file mode 100644 index 0000000..cc12dc3 --- /dev/null +++ b/js/_old/lab/src/plugin/gistPublish/gistPublishModal.ts @@ -0,0 +1,138 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Modal from './Modal'; +import { Widget } from '@phosphor/widgets'; +import { ServerConnection } from "@jupyterlab/services"; +import { PageConfig } from "@jupyterlab/coreutils"; +import gistPublishModalTemplate from './modalTemplate'; + +export default class GistPublishModal { + private settingsUrl: string; + private serverSettings: ServerConnection.ISettings; + + constructor() { + this.serverSettings = ServerConnection.makeSettings(); + this.settingsUrl = `${PageConfig.getBaseUrl()}beakerx/settings`; + } + + show(submitCallback: Function): void { + this.getGithubPersonalAccessToken() + .then(personalAccessToken => { + this.create(submitCallback, personalAccessToken); + }); + } + + create(submitCallback, personalAccessToken = ''): Promise { + const bodyWidget = this.createBodyWidget(); + const personalAccessTokenInput = bodyWidget.node.querySelector('input'); + const form = bodyWidget.node.querySelector('form'); + const cancelButton = Modal.cancelButton({ label: 'Cancel' }); + const publishButton = Modal.okButton({ label: 'Publish' }); + const formGroup = bodyWidget.node.querySelector('.form-group'); + const errorNode = this.createErrorIconNode(); + + const submitHandler = (event) => { + event.preventDefault(); + + if (event.target && event.target.innerText === 'CANCEL') { + return modal.reject(); + } + + if (!personalAccessTokenInput.value || !personalAccessTokenInput.checkValidity()) { + personalAccessTokenInput.focus(); + formGroup.classList.add('has-error'); + formGroup.appendChild(errorNode); + + return false; + } + + submitCallback(personalAccessTokenInput.value); + formGroup.contains(errorNode) && formGroup.removeChild(errorNode); + formGroup.classList.remove('has-error'); + + this.storePersonalAccessToken(personalAccessTokenInput.value); + modal.reject(); + }; + + if (personalAccessTokenInput && form) { + personalAccessTokenInput.value = personalAccessToken; + } + + const modal = new Modal({ + submitHandler, + title : 'Publish to a GitHub Gist', + body: bodyWidget, + defaultButton: 1, + focusNodeSelector: 'input', + buttons: [cancelButton, publishButton], + }); + + return modal.launch(); + } + + private createBodyWidget(): Widget { + const modalContent = document.createElement('div'); + + modalContent.innerHTML = gistPublishModalTemplate; + + return new Widget({ node: modalContent }); + } + + private createErrorIconNode() { + const errorNode = document.createElement('span'); + + errorNode.classList.add('fa'); + errorNode.classList.add('fa-remove'); + errorNode.classList.add('form-control-feedback'); + errorNode.style.fontSize = '18px'; + errorNode.style.lineHeight = '25px'; + + return errorNode; + } + + storePersonalAccessToken(githubPersonalAccessToken = ''): Promise { + return this.getStoredSettings() + .then(storedSettings => { + storedSettings.beakerx.githubPersonalAccessToken = githubPersonalAccessToken; + return ServerConnection.makeRequest( + this.settingsUrl, + { + method: 'POST', + body: JSON.stringify({ + ...storedSettings + }) + }, + this.serverSettings + ).catch(reason => { console.log(reason); }) + }); + } + + getGithubPersonalAccessToken(): Promise { + return this.getStoredSettings() + .then(settings => settings.beakerx.githubPersonalAccessToken || ''); + } + + getStoredSettings(): Promise { + return ServerConnection.makeRequest( + this.settingsUrl, + { method: 'GET' }, + this.serverSettings + ) + .then(response => response.json()) + .catch(reason => { console.log(reason); }); + } +} diff --git a/js/_old/lab/src/plugin/gistPublish/index.ts b/js/_old/lab/src/plugin/gistPublish/index.ts new file mode 100644 index 0000000..00ef37a --- /dev/null +++ b/js/_old/lab/src/plugin/gistPublish/index.ts @@ -0,0 +1,114 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NotebookPanel } from "@jupyterlab/notebook"; +import { showDialog, Dialog, ToolbarButton } from '@jupyterlab/apputils'; +import beakerx from "../../beakerx"; +import GistPublishModal from './gistPublishModal'; +import AccessTokenProvider from "../../AccessTokenProvider"; +import { CodeCell, Cell } from "@jupyterlab/cells"; +import {CommandRegistry} from "@phosphor/commands"; + +export function registerFeature(panel: NotebookPanel, commands: CommandRegistry, showPublication: boolean) { + if (showPublication) { + addActionButton(panel, commands); + setupPublisher(panel, commands); + } else { + removeActionButton(panel); + } +} + +function addActionButton(panel: NotebookPanel, commands: CommandRegistry): void { + if (panel.toolbar.isDisposed) { return; } + const action = { + iconClassName: 'bx-PublishIcon fa fa-share-alt', + tooltip: 'Publish...', + onClick: () => openPublishDialog(panel, commands) + }; + + let button = new ToolbarButton(action); + button.id = 'bx-publishButton'; + + panel.toolbar.insertItem(9,'publish', button); +} + +function removeActionButton(panel: NotebookPanel): void { + let iter = panel.toolbar.layout.iter(); + let widget; + while (widget = iter.next()) { + if (widget instanceof ToolbarButton && widget.id == 'bx-publishButton') { + panel.toolbar.layout.removeWidget(widget); + break; + } + } +} + +function setupPublisher(panel: NotebookPanel, commands: CommandRegistry) { + + let options = { + accessTokenProvider: new AccessTokenProvider(), + saveWidgetsStateHandler: saveWidgetsState.bind(undefined, panel, commands), + prepareContentToPublish: (scope) => { + let el = scope.node || scope.element[0]; + let cell: CodeCell; + let cells: CodeCell[] = (panel.content.widgets || []).filter((cell: Cell) => cell instanceof CodeCell); + for(let c of cells) { + if(c.node.contains(el)){ + cell = c; + break; + } + } + + const nbjson = panel.content.model.toJSON(); + nbjson['cells'] = [cell.model.toJSON()]; + return nbjson; + }, + }; + beakerx.GistPublisherUtils.setup(options); +} + +function openPublishDialog(panel: NotebookPanel, commands: CommandRegistry) { + new GistPublishModal() + .show(async (personalAccessToken) => { + await saveWidgetsState(panel, commands); + return doPublish(panel, personalAccessToken) + }); +} + +function showErrorDialog(errorMsg) { + showDialog({ + title : 'Gist publication error', + body : `Uploading gist failed: ${errorMsg}`, + buttons: [ Dialog.okButton({ label: 'OK' }) ] + }); +} + +export async function saveWidgetsState (panel: NotebookPanel, commands: CommandRegistry): Promise { + await commands.execute('docmanager:save'); + console.log("widgets state has been saved"); + return panel.context.contentsModel.name; + +} + +function doPublish(panel: NotebookPanel, personalAccessToken: string|null): void { + beakerx.GistPublisher.doPublish( + personalAccessToken, + panel.context.contentsModel.name, + panel.content.model.toJSON(), + (errorMsg) => showErrorDialog(errorMsg) + ); +} + diff --git a/js/_old/lab/src/plugin/gistPublish/modalTemplate.ts b/js/_old/lab/src/plugin/gistPublish/modalTemplate.ts new file mode 100644 index 0000000..a69af6c --- /dev/null +++ b/js/_old/lab/src/plugin/gistPublish/modalTemplate.ts @@ -0,0 +1,31 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default ` +
+
+
+ + +
+

+ Enter a Personal Access Token to publish the notebook as a gist in your GitHub account.
+ We recommend your Token have only the gist scope.
+ For more information, read the documentation for scopes. +

+
+
+`; diff --git a/js/_old/lab/src/plugin/index.ts b/js/_old/lab/src/plugin/index.ts new file mode 100644 index 0000000..9a04e48 --- /dev/null +++ b/js/_old/lab/src/plugin/index.ts @@ -0,0 +1,97 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {Widget} from '@phosphor/widgets'; +import {DisposableDelegate} from '@phosphor/disposable'; +import {DocumentRegistry} from '@jupyterlab/docregistry'; +import {INotebookModel, NotebookPanel} from '@jupyterlab/notebook'; +import {ILabShell, JupyterFrontEnd} from "@jupyterlab/application"; +import {ISettingRegistry} from "@jupyterlab/coreutils"; +import {registerCommTargets} from './comm'; +import {extendHighlightModes, registerCommentOutCmd} from './codeEditor'; +import {enableInitializationCellsFeature} from './initializationCells'; +import UIOptionFeaturesHelper from "./UIOptionFeaturesHelper"; +import {Autotranslation} from "./autotranslation"; +import beakerx from "./../beakerx"; +import proxify = Autotranslation.proxify; + +function displayHTML(widget: Widget, html: string): void { + if (!widget.node || !html) { + return; + } + + const childElement = document.createElement('pre'); + + childElement.classList.add('jp-RenderedHTML'); + childElement.innerHTML = html; + widget.node.appendChild(childElement); +} + +class BeakerxExtension implements DocumentRegistry.WidgetExtension { + constructor( + private app: JupyterFrontEnd, + private settings: ISettingRegistry, + private labShell: ILabShell + + ) {} + + createNew(panel: NotebookPanel, context: DocumentRegistry.IContext) { + + let app = this.app; + let settings = this.settings; + let labShell = this.labShell; + + Promise.all([panel.session.ready, context.ready]).then(function() { + extendHighlightModes(panel); + enableInitializationCellsFeature(panel); + registerCommentOutCmd(panel); + registerCommTargets(panel, context); + + window.beakerxHolder = window.beakerxHolder || {}; + const plotApiList = beakerx.PlotApi.list(); + const beakerxInstance = { + ...plotApiList, + displayHTML, + prefs: beakerx.bkCoreManager.getBkApp().getBeakerObject().beakerObj.prefs, + }; + window.beakerx = proxify(beakerxInstance, context.session.kernel); + window.beakerxHolder[context.session.kernel.id] = window.beakerx; + + plotApiList.setActiveLabPanel(panel); + labShell.activeChanged.connect((sender, args) => { + if (args.newValue == panel){ + window.beakerx = window.beakerxHolder[panel.context.session.kernel.id]; + plotApiList.setActiveLabPanel(panel); + } + }); + + const originalProcessFn = app.commands.processKeydownEvent; + app.commands.processKeydownEvent = (event) => { + if (window.beakerx.tableFocused) { + return false; + } + + return originalProcessFn.call(app.commands, event); + }; + + new UIOptionFeaturesHelper(app, settings, panel, labShell).registerFeatures(); + }); + + return new DisposableDelegate(() => { }); + } +} + +export default BeakerxExtension; diff --git a/js/_old/lab/src/plugin/initializationCells.ts b/js/_old/lab/src/plugin/initializationCells.ts new file mode 100644 index 0000000..e0c3459 --- /dev/null +++ b/js/_old/lab/src/plugin/initializationCells.ts @@ -0,0 +1,93 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NotebookPanel } from "@jupyterlab/notebook"; +import { Cell, CodeCell } from '@jupyterlab/cells'; +import { showDialog, Dialog } from '@jupyterlab/apputils'; +import { ToolbarButton } from '@jupyterlab/apputils' + +export interface IInitCellsOptions { + run_on_kernel_ready: boolean, + run_untrusted?: boolean +} + +const modName = 'init_cell'; +const logPrefix = `[${modName}]`; + +export function enableInitializationCellsFeature(panel: NotebookPanel): void { + const modOptions = panel.model.metadata[modName]; + const options = { run_on_kernel_ready: true, ...modOptions }; + + registerNotebookInitCellsAction(panel, options); + + panel.session.kernel.ready.then(() => runInitCells(panel, options)); +} + +export function runInitCells(panel: NotebookPanel, options: IInitCellsOptions): void { + const cells: CodeCell[] = getInitCells(panel); + + handleUntrustedKernelInitCells(cells, options); + + if (!canExecuteInitCells(panel, options, cells)) { + return; + } + + console.log(logPrefix, 'running all initialization cells'); + cells.forEach((cell: CodeCell) => CodeCell.execute(cell, panel.session)); + console.log(logPrefix, `finished running ${cells.length} initialization cell${(cells.length !== 1 ? 's' : '')}`); +} + +export function getInitCells(panel: NotebookPanel): CodeCell[] { + const cells = panel.content.widgets || []; + + return cells.filter( + (cell: Cell) => ((cell instanceof CodeCell) && cell.model.metadata.get('init_cell')) + ); +} + +function canExecuteInitCells (panel: NotebookPanel, options: IInitCellsOptions, cells: CodeCell[]) { + const trusted = cells.length && cells[0].model.trusted; + + return ( + options.run_on_kernel_ready && + (trusted || options.run_untrusted) && + panel.session.kernel && + panel.session.kernel.info.status === 'ok' + ); +} + +function handleUntrustedKernelInitCells(cells: CodeCell[], options: IInitCellsOptions) { + if (cells.length && !cells[0].model.trusted && !options.run_untrusted) { + showDialog({ + title: 'Initialization cells in untrusted notebook', + body : 'This notebook is not trusted, so initialization cells will not be automatically run on kernel load. You can still run them manually, though.', + buttons: [ Dialog.okButton({ label: 'OK' }) ] + }); + } +} + +export function registerNotebookInitCellsAction( + panel: NotebookPanel, + options: IInitCellsOptions +): void { + const action = { + iconClassName: 'bx-RunInitializationCellsIcon fa fa-calculator', + tooltip: 'Run all initialization cells', + onClick: () => runInitCells(panel,{ ...options, run_untrusted: true }) + }; + + panel.toolbar.insertItem(9,'run-initialization-cells', new ToolbarButton(action)); +} diff --git a/js/_old/lab/src/plugin/requirejs.ts b/js/_old/lab/src/plugin/requirejs.ts new file mode 100644 index 0000000..d51505b --- /dev/null +++ b/js/_old/lab/src/plugin/requirejs.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +let loader: Promise; + +export default class RequirejsLoader { + + public static load(): Promise { + + if (loader) { + return loader; + } + + loader = new Promise((resolve, reject) => { + + const s = document.createElement('script'); + + s.id = 'bx-requirejs'; + s.src = '//cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.min.js'; + s.onload = (evt) => { + resolve(); + }; + + s.onerror = (evt) => { reject(evt); }; + + document.head.appendChild(s); + }); + + return loader; + } +} diff --git a/js/_old/lab/src/tree.ts b/js/_old/lab/src/tree.ts new file mode 100644 index 0000000..d79930f --- /dev/null +++ b/js/_old/lab/src/tree.ts @@ -0,0 +1,73 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +declare function require(moduleName: string): any; + +import { JupyterFrontEndPlugin, ILayoutRestorer, JupyterFrontEnd} from "@jupyterlab/application"; +import { ICommandPalette, WidgetTracker } from "@jupyterlab/apputils"; +import { JSONExt } from "@phosphor/coreutils"; +import { PageConfig } from "@jupyterlab/coreutils"; + +const BeakerXTreeLib = require("../lib/tree.js").default; +const TreeWidget = BeakerXTreeLib.TreeWidget; + +function activate(app: JupyterFrontEnd, palette: ICommandPalette, restorer: ILayoutRestorer) { + let widget:any; + + const command: string = 'beakerx:tree'; + + app.commands.addCommand(command, { + label: 'BeakerX Options', + execute: () => { + if (!widget) { + let options = { + baseUrl: PageConfig.getBaseUrl(), + isLab: true, + }; + widget = new TreeWidget(options); + widget.update(); + } + if (!tracker.has(widget)) { + tracker.add(widget); + } + + if (!widget.isAttached) { + app.shell.add(widget, "main"); + } else { + widget.update(); + } + + app.shell.activateById(widget.id); + } + }); + + palette.addItem({ command, category: 'BeakerX' }); + let tracker = new WidgetTracker({ namespace: 'beakerx' }); + restorer.restore(tracker, { + command, + args: () => JSONExt.emptyObject, + name: () => 'beakerx-tree' + }); +} + +const tree: JupyterFrontEndPlugin = { + id: 'beakerx_tree', + autoStart: true, + requires: [ICommandPalette, ILayoutRestorer], + activate: activate +}; + +export default tree; diff --git a/js/_old/lab/src/tsconfig.json b/js/_old/lab/src/tsconfig.json new file mode 100644 index 0000000..5627d5e --- /dev/null +++ b/js/_old/lab/src/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "declaration": true, + "declarationDir": "../dist", + "esModuleInterop": true, + "noImplicitAny": false, + "noEmitOnError": true, + "noUnusedLocals": true, + "lib": ["dom", "es5", "es2015"], + "types": ["node"], + "module": "commonjs", + "moduleResolution": "node", + "target": "ES6", + "outDir": "../dist" + } +} diff --git a/js/_old/notebook/src/index.ts b/js/_old/notebook/src/index.ts new file mode 100644 index 0000000..f5166c0 --- /dev/null +++ b/js/_old/notebook/src/index.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Entry point for the notebook bundle containing custom model definitions. +// +// Setup notebook base URL +// +// Some static assets may be required by the custom widget javascript. The base +// url for the notebook is not known at build time and is therefore computed +// dynamically. + +__webpack_public_path__ = document.querySelector('body').getAttribute('data-base-url') + 'nbextensions/beakerx/'; + +// Export widget models and views, and the npm package version number. +export * from './BxHTML'; +export * from './Foldout'; +export * from './HTMLPre'; +export * from './RESTButton'; +export * from './SparkUI'; +export * from './SparkStateProgress'; +export * from './SparkConfiguration'; +export * from './SparkFoldout'; +export * from './TabView'; +export * from './GridView'; +export * from './CyclingDisplayBox'; +export * from './EasyForm'; +export * from './SparkUI2'; +export * from './Plot'; +export * from './Spinner'; +export * from './GistPublisher' +export * from './GistPublisherUtils' + +export const version = require('../package.json').version; +export {SparkUI2Widget} from "./sparkUI2/SparkUI2Widget"; \ No newline at end of file diff --git a/js/_old/notebook/src/shared/fonts/FontAwesome.otf b/js/_old/notebook/src/shared/fonts/FontAwesome.otf new file mode 100644 index 0000000..8b0f54e Binary files /dev/null and b/js/_old/notebook/src/shared/fonts/FontAwesome.otf differ diff --git a/js/_old/notebook/src/shared/fonts/fontawesome-webfont.ttf b/js/_old/notebook/src/shared/fonts/fontawesome-webfont.ttf new file mode 100755 index 0000000..e89738d Binary files /dev/null and b/js/_old/notebook/src/shared/fonts/fontawesome-webfont.ttf differ diff --git a/js/_old/notebook/src/shared/fonts/fontawesome-webfont.woff b/js/_old/notebook/src/shared/fonts/fontawesome-webfont.woff new file mode 100755 index 0000000..8c1748a Binary files /dev/null and b/js/_old/notebook/src/shared/fonts/fontawesome-webfont.woff differ diff --git a/js/_old/notebook/src/shared/fonts/glyphicons-halflings-regular.ttf b/js/_old/notebook/src/shared/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 0000000..1413fc6 Binary files /dev/null and b/js/_old/notebook/src/shared/fonts/glyphicons-halflings-regular.ttf differ diff --git a/js/_old/notebook/src/shared/fonts/glyphicons-halflings-regular.woff b/js/_old/notebook/src/shared/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 0000000..9e61285 Binary files /dev/null and b/js/_old/notebook/src/shared/fonts/glyphicons-halflings-regular.woff differ diff --git a/js/_old/notebook/src/shared/images/down@2x.png b/js/_old/notebook/src/shared/images/down@2x.png new file mode 100644 index 0000000..456ff3a Binary files /dev/null and b/js/_old/notebook/src/shared/images/down@2x.png differ diff --git a/js/_old/notebook/src/shared/images/menu@2x.png b/js/_old/notebook/src/shared/images/menu@2x.png new file mode 100644 index 0000000..d5e33de Binary files /dev/null and b/js/_old/notebook/src/shared/images/menu@2x.png differ diff --git a/js/_old/notebook/src/shared/images/menu_white@2x.png b/js/_old/notebook/src/shared/images/menu_white@2x.png new file mode 100644 index 0000000..cc6b1f7 Binary files /dev/null and b/js/_old/notebook/src/shared/images/menu_white@2x.png differ diff --git a/js/_old/notebook/src/shared/images/sort_asc.png b/js/_old/notebook/src/shared/images/sort_asc.png new file mode 100644 index 0000000..e1ba61a Binary files /dev/null and b/js/_old/notebook/src/shared/images/sort_asc.png differ diff --git a/js/_old/notebook/src/shared/images/sort_asc_disabled.png b/js/_old/notebook/src/shared/images/sort_asc_disabled.png new file mode 100644 index 0000000..fb11dfe Binary files /dev/null and b/js/_old/notebook/src/shared/images/sort_asc_disabled.png differ diff --git a/js/_old/notebook/src/shared/images/sort_asc_white.png b/js/_old/notebook/src/shared/images/sort_asc_white.png new file mode 100644 index 0000000..48ea1a8 Binary files /dev/null and b/js/_old/notebook/src/shared/images/sort_asc_white.png differ diff --git a/js/_old/notebook/src/shared/images/sort_both.png b/js/_old/notebook/src/shared/images/sort_both.png new file mode 100644 index 0000000..af5bc7c Binary files /dev/null and b/js/_old/notebook/src/shared/images/sort_both.png differ diff --git a/js/_old/notebook/src/shared/images/sort_both_gray.png b/js/_old/notebook/src/shared/images/sort_both_gray.png new file mode 100644 index 0000000..7552b0f Binary files /dev/null and b/js/_old/notebook/src/shared/images/sort_both_gray.png differ diff --git a/js/_old/notebook/src/shared/images/sort_desc.png b/js/_old/notebook/src/shared/images/sort_desc.png new file mode 100644 index 0000000..0e156de Binary files /dev/null and b/js/_old/notebook/src/shared/images/sort_desc.png differ diff --git a/js/_old/notebook/src/shared/images/sort_desc_disabled.png b/js/_old/notebook/src/shared/images/sort_desc_disabled.png new file mode 100644 index 0000000..c9fdd8a Binary files /dev/null and b/js/_old/notebook/src/shared/images/sort_desc_disabled.png differ diff --git a/js/_old/notebook/src/shared/images/sort_desc_white.png b/js/_old/notebook/src/shared/images/sort_desc_white.png new file mode 100644 index 0000000..4fb4ab9 Binary files /dev/null and b/js/_old/notebook/src/shared/images/sort_desc_white.png differ diff --git a/js/_old/notebook/src/shared/style/tree-notebook.css b/js/_old/notebook/src/shared/style/tree-notebook.css new file mode 100644 index 0000000..701b7bc --- /dev/null +++ b/js/_old/notebook/src/shared/style/tree-notebook.css @@ -0,0 +1,21 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + /* + this styles are needed in notebook, + but must not be loaded in lab - breaks whole lab UI + */ +@import '~@phosphor/widgets/style/index.css'; \ No newline at end of file diff --git a/js/_old/notebook/src/tree-lab.ts b/js/_old/notebook/src/tree-lab.ts new file mode 100644 index 0000000..31ce448 --- /dev/null +++ b/js/_old/notebook/src/tree-lab.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import TreeWidget from "./tree/TreeWidget"; + +export default { + TreeWidget: TreeWidget, +} diff --git a/js/fonts/lato/Lato-Black.woff b/js/fonts/lato/Lato-Black.woff new file mode 100644 index 0000000..a0ab25e Binary files /dev/null and b/js/fonts/lato/Lato-Black.woff differ diff --git a/js/fonts/lato/Lato-Regular.woff b/js/fonts/lato/Lato-Regular.woff new file mode 100644 index 0000000..ae1307f Binary files /dev/null and b/js/fonts/lato/Lato-Regular.woff differ diff --git a/js/fonts/robotomono/robotomono-bold.ttf b/js/fonts/robotomono/robotomono-bold.ttf new file mode 100755 index 0000000..07ef607 Binary files /dev/null and b/js/fonts/robotomono/robotomono-bold.ttf differ diff --git a/js/fonts/robotomono/robotomono-bold.woff b/js/fonts/robotomono/robotomono-bold.woff new file mode 100644 index 0000000..1a76a58 Binary files /dev/null and b/js/fonts/robotomono/robotomono-bold.woff differ diff --git a/js/fonts/robotomono/robotomono.ttf b/js/fonts/robotomono/robotomono.ttf new file mode 100644 index 0000000..aacaedf Binary files /dev/null and b/js/fonts/robotomono/robotomono.ttf differ diff --git a/js/fonts/robotomono/robotomono.woff b/js/fonts/robotomono/robotomono.woff new file mode 100644 index 0000000..54ebc1d Binary files /dev/null and b/js/fonts/robotomono/robotomono.woff differ diff --git a/js/fonts/robotomono/robotomono.woff2 b/js/fonts/robotomono/robotomono.woff2 new file mode 100644 index 0000000..73ad9a8 Binary files /dev/null and b/js/fonts/robotomono/robotomono.woff2 differ diff --git a/js/package.json b/js/package.json new file mode 100644 index 0000000..4890974 --- /dev/null +++ b/js/package.json @@ -0,0 +1,78 @@ +{ + "name": "beakerx_widgets", + "version": "1.6.0", + "description": "BeakerX: Beaker EasyForms, Magics, Plots and Spark Extension for Jupyter", + "keywords": [ + "jupyter", + "jupyterlab", + "jupyterlab notebook", + "jupyterlab-extension" + ], + "author": "Two Sigma Open Source, LLC", + "main": "src/index.ts", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/twosigma/beakerx_widgets.git" + }, + "scripts": { + "build": "yarn run clean && yarn run build:css && yarn run build:js && webpack", + "build:css": "sass --no-source-map --load-path node_modules sass/beakerx_widgets.scss css/beakerx_widgets.css", + "build:js": "tsc", + "watch:js": "tsc --watch", + "prepublish": "yarn run build", + "test": "karma start --single-run", + "clean": "rimraf lib css dist", + "lint": "eslint src/ --ext .ts" + }, + "devDependencies": { + "@jupyterlab/application": "^1.2.8", + "@types/big.js": "^4.0.5", + "@types/chai": "^4.2.11", + "@types/d3": "^5.7.2", + "@types/expect.js": "^0.3.29", + "@types/jquery": "^3.3.38", + "@types/jqueryui": "^1.12.10", + "@types/mocha": "^7.0.2", + "@types/node": "^13.13.5", + "@types/sinon": "^9.0.0", + "@typescript-eslint/eslint-plugin": "^3.5.0", + "@typescript-eslint/parser": "^3.5.0", + "css-loader": "^3.6.0", + "eslint": "^7.3.1", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-prettier": "^3.1.4", + "file-loader": "^6.0.0", + "prettier": "^2.0.5", + "rimraf": "^3.0.2", + "sass": "^1.26.9", + "sass-loader": "^8.0.2", + "style-loader": "^1.2.1", + "typescript": "^3.8.3", + "url-loader": "^4.1.0", + "webpack": "^4.43.0", + "webpack-cli": "^3.3.11" + }, + "dependencies": { + "@jupyter-widgets/base": "^1.2.5 || ^2.0.2", + "@jupyter-widgets/controls": "^1.5.3", + "@jupyterlab/coreutils": "^3.2.0", + "@phosphor/widgets": "^1.9.3", + "big.js": "^5.2.2", + "d3": "^5.16.0", + "flatpickr": "^4.6.3", + "jquery": "^3.5.1", + "jquery-ui": "^1.12.1", + "jquery-ui.combobox": "^1.0.7", + "moment-timezone": "^0.5.28", + "underscore": "^1.10.2" + }, + "files": [ + "css/", + "dist/", + "lib/" + ], + "jupyterlab": { + "extension": "lib/jupyterlab-plugin" + } +} diff --git a/js/sass/beakerx_widgets.scss b/js/sass/beakerx_widgets.scss new file mode 100644 index 0000000..cd26fc9 --- /dev/null +++ b/js/sass/beakerx_widgets.scss @@ -0,0 +1,10 @@ +@use "partials/beakerx"; +@use "partials/bko-combinedplot"; +@use "partials/bko-plot"; +@use "partials/fontface"; +@use "partials/forms"; +@use "partials/grid-view"; +@use "partials/spark"; +@use "partials/spinner"; +@use "partials/tree"; + diff --git a/js/sass/partials/beakerx.scss b/js/sass/partials/beakerx.scss new file mode 100644 index 0000000..882b87e --- /dev/null +++ b/js/sass/partials/beakerx.scss @@ -0,0 +1,282 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@use "bxvariables" as *; +@use "fontface" as ff; + +//@include ff.fontFace('Roboto Mono', '../fonts/robotomono/robotomono', normal, normal); +//@include ff.fontFace('Roboto Mono', '../fonts/robotomono/robotomono-bold', normal, bold); +//@include ff.fontFace('Lato', '../fonts/lato/Lato-Regular', normal, bold); +//@include ff.fontFace('Lato', '../fonts/lato/Lato-Black', normal, bold); + +.improveFonts .context-menu-root .context-menu-item span { + font-family: "Lato", Helvetica, sans-serif; +} + +.bx-button { + &[class*="icon"] { + font: normal normal normal 14px/1 FontAwesome; + border: 1px solid $bxBorderColor1; + width: auto; + padding: 0 8px; + margin: 2px 4px; + } + + &:before { + display: inline-block; + } + + &.icon-close:before { + content: "\f00d" !important; + } + + &.icon-add:before { + content: "\f067" !important; + } + + &.icon-bg:before { + content: "&" !important; + font-family: "Lato", Helvetica, sans-serif; + font-weight: bold; + } +} + +.bko-focused { + border: 1px solid $bxColorFocus !important; + + &:before { + position: absolute; + display: block; + top: -1px; + left: -1px; + width: 5px; + height: calc(100% + 2px); + content: ''; + background: $bxColorFocus; + } +} + +.text-ellipsis * { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.bko-modal { + font-size: 14px; + line-height: 20px; + position: absolute; + left: 50%; + margin-left: -150px; + z-index: 9999; + width: auto; + -webkit-overflow-scrolling: touch; + outline: 0; + overflow: visible; + animation: 1s fadeIn; + animation-fill-mode: forwards; + + .modal-dialog { + width: 300px; + position: relative; + margin: 10px; + } + + .modal-content { + //border: none; + position: relative; + background-color: $bxBgColor1; + background-clip: padding-box; + border: 1px solid $bxBorderColor1; + border-radius: 3px; + // Remove focus outline from opened modal + outline: 0; + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + } + + .modal-body { + position: relative; + padding: 25px 15px 15px 15px; + } + + // Footer (for actions) + .modal-footer { + padding: 15px; + text-align: right; // right align buttons + border-top: 1px solid $bxBorderColor1; + + .btn { + display: inline-block; + margin-bottom: 0; + font-weight: normal; + text-align: center; + white-space: nowrap; + vertical-align: middle; + touch-action: manipulation; + cursor: pointer; + background-image: none; + border: 1px solid transparent; + padding: 6px 12px; + font-size: 13px; + line-height: 1.42857143; + border-radius: 2px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + + .btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; + } + + // Properly space out buttons + .btn + .btn { + margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs + margin-left: 5px; + } + + // but override that for button groups + .btn-group .btn + .btn { + margin-left: -1px; + } + + // and override it for block buttons as well + .btn-block + .btn-block { + margin-left: 0; + } + + } +} +.jp-Toolbar-button.fa { + line-height: 100%; +} + +.foldout-widget.widget-box { + display: block; + font-size: 14px; + + & > .foldout-label { + display: flex; + flex-direction: row; + align-items: center; + cursor: pointer; + color: #333333; + text-align: left; + line-height: normal; + text-indent: 16px; + border: 1px solid #c5c5c5; + background: #e6e6e6; + border-radius: 3px 3px 0 0; + position: relative; + margin: 2px 0 0 0; + padding: .5em .5em .5em .7em; + font-size: 100%; + width: 100%; + height: 36px; + + &:before { + content: ''; + display: block; + position: absolute; + width: 16px; + height: 16px; + left: .5em; + top: 50%; + margin-top: -9px; + background-position: -32px -16px; + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAADwCAMAAADYSUr5AAABDlBMVEVEREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREQf23IJAAAAWnRSTlMAGf8QMwQIUL+CmS8iVXFAZmAaEzLMDSE8FkJISyAeWiMnMVMshTSHgMNqyM/GOEUcvLi+fKu1pYyqqK0fsin9AZ5RJO8KBgIDj6JilEqgr23fnEdjP29/kWiyI5UtAAAM80lEQVR4Aezb3XLzOgiFYXh8//e8T8MwQeNJ8yXd5T2zF8RiCfm3jWVZvoGUf7x+X+6AfLnEUZRkCZYf7EBQAsgoEFVNilpJ2gFafgkmP9eBoM1R2cEwg0GM6WDqh5ryXur4hxIGPZFD9vTrDbIe8e2QY4FtCsF5DU0dMAygdsCn6EM8d/AQkBzsJUsw+XeuApHIGiz3RmhZlmVZluVP3Anmd92Igbojh4IAg1/z+4CkZqSQn3wjld2AFlC3kM/9St2AHB8eg/yn9ZOnEQ9yIge/Zj8bCMS/AjD3rCzi+X1ATn4W9YMdMJDe+T4gT0sw5F4FlmVZlmVZ/gTiFcBLv++zBQTmHWdxxr3jw/AD5gOcM6DKekhN17SCvmkcjfPx53oMcjg4xslAoYp127GDWoIq3jo+0PWyY5CBuYJz+hCibAM1gBoOxg4yWzS3MCd/e8Xm9HkA5yPcKu+4xmFqQdz7RRwbuoA754CjKl7EV+eDWJZlWZZl6SQZL8EPDwCiMGhADMxqcm8A0WCOqsB5AOpnJVmlETk9QItIs4H9KPPxiRgLKDjPINQPi2TVpg4iRwOS4/hbyOQAY/2aaM5HKP485Ii5g4Dw1GB9jzgPQFefOoyInt/1nt9l9AEPBaLq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"); + } + + .foldout-label-content { + margin: 0; + padding: 0; + height: auto; + display: block; + line-height: 1.2em; + flex-shrink: 0; + } + } + + &.collapsed { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + } + + &.active { + .foldout-label, + .widget-label { + &:before { + background-position: -65px -16px; + } + } + } + + .foldout-preview { + line-height: 1.3em; + flex-shrink: 2; + flex-grow: 1; + text-indent: 0; + transition: opacity 0.1s ease-in-out; + padding: 0 15px; + + .widget-html-content { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + p { + margin: 0; + padding: 0; + } + } + + .foldout-content { + line-height: 1.3em; + padding: 0 2.2em; + border: 1px solid $bxBorderColor1; + border-top: 0; + border-radius: 0 0 3px 3px; + text-overflow: ellipsis; + overflow: hidden; + transition: height 0.3s ease-in-out; + + &:after, + &:before { + content: ''; + height: 10px; + display: block; + width: 100%; + } + } +} + +.bx-dark-theme { + .ui-icon, + .ui-widget-content .ui-icon { + background-image: url("~jquery-ui/themes/base/images/ui-icons_ffffff_256x240.png"); + } +} + +@media (min-width: 1200px) { + .container.toolbar { + width: 100%; + } +} diff --git a/js/sass/partials/bko-combinedplot.scss b/js/sass/partials/bko-combinedplot.scss new file mode 100644 index 0000000..67d222a --- /dev/null +++ b/js/sass/partials/bko-combinedplot.scss @@ -0,0 +1,25 @@ +/* +* Copyright 2014 TWO SIGMA OPEN SOURCE, LLC +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +.combplot-plotcontainer { + background-color: #FEFEFE; + border: none; + position: relative; +} + +.improveFonts .combplot-plotcontainer { + font-family: "Lato", Helvetica, sans-serif; +} diff --git a/js/sass/partials/bko-plot.scss b/js/sass/partials/bko-plot.scss new file mode 100644 index 0000000..8ad590a --- /dev/null +++ b/js/sass/partials/bko-plot.scss @@ -0,0 +1,326 @@ +/* + * Copyright 2014 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.plot-plotcontainer { + background-color: #FEFEFE; + position: relative; + border-collapse: initial; + border: 1px solid transparent; + box-sizing: content-box; +} + +.plot-test, +.plot-cursorlabel, +.plot-constlabel, +.plot-tooltip, +.plot-message-content, +.plot-message-title { + font-family: verdana, helvetica, arial, sans-serif; +} + +.plot-resp { + pointer-events: auto; +} + +.plot-constline, +.plot-line { + fill: none; +} + +.plot-label, +.plot-xylabel, +.plot-legend-label { + line-height: 1.38462; +} +.plot-label { + font-size: 13px; +} +.plot-text { + font-size: 1.0em; +} +.plot-xylabel { + font-size: 15px; + text-anchor: middle; +} + +.plot-cursorlabel, +.plot-constlabel { + font-size: 0.75em; + color: white; + padding: 1px 5px; + pointer-events: none; + position: absolute; + width: auto; + height: auto; + border-radius: 2px; + display: inline-table; +} + +.plot-gridline, +.plot-gridline-base { + stroke-width: 1; + pointer-events: none; +} +.plot-gridline { + stroke: lightgrey; + stroke-width: 1; +} +.plot-gridline-base { + stroke: #333333; + stroke-width: 1; + opacity: .75; +} +.plot-tick { + stroke: #333333; +} + + +.plot-constline, +.plot-constband, +.plot-cursor { + pointer-events: none; +} + +.plot-bar, +.plot-stem { + pointer-events: auto; +} + +.plot-pointrect, +.plot-pointcircle, +.plot-pointdiamond { + /* to be added if needed */ +} + +.plot-locatebox { + fill: #003366; + opacity: 0.2; +} + +.plot-truncatebox { + fill: #FEFEFE; +} + +.plot-title { + text-align: center; + font-weight: bold; + font-size: 1.2em; + margin: 5px; +} + +.plot-legendbox { + width: 10px; + height: 10px; + margin: 0px 5px; + display: inline-block; + vertical-align: baseline; +} + +.plot-legendcheckbox { + margin: 0px !important; + margin-right: 5px; +} + +.plot-legendtext { + pointer-events: none; +} + +.plot-legendlod { + margin: 0px 5px 0px 10px; + display: inline-block; +} +.plot-legendlod ul { + list-style: none; + margin: 0; +} +.plot-legendlod ul li a { + cursor: pointer; +} + +.plot-legendlodtype { + color: #73be3b; + cursor: pointer; + display: inline-block; + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAICAYAAADwdn+XAAAAUUlEQVQoFZ3RSwoAIAhF0XK3rqJahbuVeEERlf0EZ94z0YtIJKLgPkZVk0f3gyBm5liAV6TG6Bpwi/TxBJyQMV4CFrKKTWBErBh328F3sLujDB1vOtcHeTAhAAAAAElFTkSuQmCC') no-repeat center right; + padding-right: 13px; + background-size: 8px; +} +.plot-legendlodtype:hover { + color: #73be3b; + text-decoration: none; +} + +.plot-plotlegendcontainer { + position: relative; +} + +.plot-legend { + position: absolute; + background-color: #FEFEFE; +} + +.plot-legenddraggable { + cursor: pointer; +} + +.plot-legendscrollablecontainer { + border: 1px solid #BBBBBB; + border-collapse: initial; /* override the global table setting*/ + overflow-y: visible; +} + +.plot-legendscrollablecontainer .plot-legenddraggable { + padding: .25em .25em 0 .25em +} +.plot-legendscrollablecontainer .plot-legenddraggable.hasScroll { + padding-right: 23px +} + +.plot-legendline{ + white-space: nowrap; +} + +.plot-legenditeminline{ + display: inline-block; + margin-right: 10px; + position: relative; +} + +.plot-legenditeminrow { +} + +.plot-respdot, +.plot-respstem { + fill: white; + fill-opacity: 0.75; + stroke-width: 1; + border-radius: 2.5px; +} + +.plot-tooltip { + font-size: 0.8em; + position: absolute; + display: inline-table; + border: 1px solid; + background-color: white; + padding: 0.8em; + opacity: 0; + transition: opacity 0.4s; +} + +.plot-tooltip > div { + width: 100%; + overflow: hidden; +} + +.text-line-style { + fill: black; + stroke: black +} + +.fa.fa-times { + float: right; + cursor: pointer; +} + +.plot-message-content { + vertical-align: middle; +} +.plot-message-title { + font-weight: bold; +} +.plot-message { + min-width: 150px; + min-height: 50px; + font-size: 0.8em; + position: absolute; + border: 1px solid gray; + background-color: white; + padding: 0.8em; + display: inline-table; +} + +.plot-lodbox { + stroke: black; + stroke-width: 1; +} + +.plot-lodavg, +.plot-lodavgline { + pointer-events: none; +} + +.plot-lodavgline { + fill: none; + stroke: black; + stroke-width: 1.5; +} +.svg-export { + font-size: 14px; + line-height: 1.38462; + color: #333333; + background-color: white; +} +.nocollapsing-margins:after +{ + content: "\00a0"; /* No-break space character */ + display: block; + overflow: hidden; + height: 0; +} + +.plot-legend-axis { + stroke: #333333; + stroke-width: 1; +} +.plot-legend-tick { + stroke: #333333; +} + +.plot-legend-histogram { + stroke: #333333; + stroke-width: 1; + opacity: 0.5; + fill: #333333; +} +.heatmap-tooltip{ + color: white; + background-color: rgba(0,107,179,0.5); + font-size: 13px; + line-height: 1.38462; + padding: 0.5em; +} + +.svg-export .plot-legendcheckbox { + display: none; +} + +.svg-export { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.svg-export.improveFonts { + font-family: "Lato", Helvetica, sans-serif; +} + +.improveFonts .plot-plotcontainer, +.improveFonts .plot-label, +.improveFonts .plot-xylabel, +.improveFonts .plot-legend-label, +.improveFonts .plot-title, +.improveFonts .heatmap-tooltip { + font-family: "Lato", Helvetica, sans-serif; +} diff --git a/js/sass/partials/bxvariables.scss b/js/sass/partials/bxvariables.scss new file mode 100644 index 0000000..e51c92e --- /dev/null +++ b/js/sass/partials/bxvariables.scss @@ -0,0 +1,29 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +$bxBorderColor1: #cccccc; +$bxBgColor1: #ffffff; +$bxBgColor2: #eeeeee; + +$bxColorFocus: #66bb6a; +$bxColorSuccess: #5cb85c; +$bxColorInfo: #5bc0de; +$bxColorWarning: #f0ad4e; +$bxColorError: #ff0000; +$bxColorDefault: #777777; + +$bxColorLink: #337ab7; +$bxColorHover: #23527c; diff --git a/js/sass/partials/fontface.scss b/js/sass/partials/fontface.scss new file mode 100644 index 0000000..976870b --- /dev/null +++ b/js/sass/partials/fontface.scss @@ -0,0 +1,25 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@mixin fontFace($family, $src, $style: normal, $weight: normal) { + @font-face { + font-family: $family; + src: url('#{$src}.woff') format('woff'), // standards + url('#{$src}.ttf') format('truetype'); // Safari, Android, iOS + font-style: $style; + font-weight: $weight; + } +} diff --git a/js/sass/partials/forms.scss b/js/sass/partials/forms.scss new file mode 100644 index 0000000..4ac2052 --- /dev/null +++ b/js/sass/partials/forms.scss @@ -0,0 +1,137 @@ +$beakerxFont: "Lato", Helvetica, sans-serif; + +.beaker-easyform-container { + font-family: $beakerxFont; + + .widget-inline-hbox { + .widget-label { + min-width: 120px; + max-width: 200px; + margin-right: 15px; + } + } + + .widget-select, + .widget-select-multiple { + width: auto; + max-width: 100%; + min-width: 380px; + } + + .widget-select-multiple, + .widget-select { + > select { + flex: unset; + min-width: 30px; + + &[size] { + overflow: auto; + } + } + } + + .widget-textarea, + .widget-text { + width: auto; + + > [size], + > [cols] { + flex-grow: 0; + } + } + + .widget-radio-box input { + margin-right: 10px; + } + + .datepicker-container { + align-items: stretch; + + .form-control { + width: auto; + border-right: 0; + border-radius: 2px 0 0 2px; + } + } + + .widget-box > .widget-label { + min-width: 120px; + max-width: 200px; + margin-right: 15px; + text-align: right; + } + + .widget-checkbox { + width: auto; + + > .checkbox { + flex: unset; + } + } + + .widget-hbox { + .widget-radio { + width: auto; + } + + .widget-radio-box { + flex-direction: row; + + label { + min-width: 120px; + max-width: 200px; + } + } + } + + .p-Panel { + .widget-checkbox { + .widget-label { + min-width: 0; + } + } + } +} + +.beaker-fieldset { + border: 1px solid #cfcfcf; + padding: 10px; + font-family: $beakerxFont; + overflow: visible !important; + position: relative; + margin-top: 10px !important; + + legend { + border-bottom: none; + margin-bottom: 0; + font-size: 17px; + position: absolute; + top: -0.7em; + background: white; + width: auto; + display: inline-block; + padding: 0 4px; + } + + .widget-inline-hbox { + align-items: center; + } +} + +.easyform-combobox { + position: relative; + display: inline-block; + + .easyform-combobox-toggle { + position: absolute; + top: 0; + bottom: 0; + margin-left: -1px; + padding: 0; + } + + .easyform-combobox-input { + margin: 0; + padding: 0 10px; + } +} diff --git a/js/sass/partials/grid-view.scss b/js/sass/partials/grid-view.scss new file mode 100644 index 0000000..5213858 --- /dev/null +++ b/js/sass/partials/grid-view.scss @@ -0,0 +1,5 @@ +.beaker-grid-view { + .p-Widget { + flex: 1; + } +} \ No newline at end of file diff --git a/js/sass/partials/spark.scss b/js/sass/partials/spark.scss new file mode 100644 index 0000000..79706da --- /dev/null +++ b/js/sass/partials/spark.scss @@ -0,0 +1,159 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@use "bxvariables" as *; +@import "spinner"; + +.bx-spark2-widget { + flex: 1 1 auto; + + .bx-spark-start { + padding: 4px 0; + + .bx-spark-error { + color: #ff0000; + } + } + + .bx-spark-profile-selector { + padding: 4px 0; + } + + .bx-spark-property-container { + .bx-spark-property { + padding: 2px 0; + input[type="text"] { + margin:0 4px 0 0; + line-height: 22px; + } + } + } + + .bx-spark-session { + padding: 4px 0; + + .bx-stats { + display: inline-flex; + width: 133px; + align-items: center; + margin: 0; + + + .bx-label { + flex-grow: 1; + box-sizing: border-box; + display: inline-block; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: normal; + line-height: 16px; + text-align: center; + vertical-align: baseline; + border-radius: .25em; + color: #ffffff; + margin: 0 2px 0 0; + + &.connection { + background-color: $bxColorSuccess; + color: $bxBgColor1; + padding-top:0; + padding-bottom: 0; + &::after { + content: ""; + background-image: url('data:image/png;base64,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'); + background-size: contain; + background-repeat: no-repeat; + background-position: center center; + width: 15px; + height: 15px; + display: inline-block; + vertical-align: baseline; + } + } + &.active { + background-color: $bxColorInfo; + } + &.dead { + background-color: $bxColorError; + } + &.memory { + background-color: $bxColorDefault; + } + } + } + } + + .bx-spark-connect-start { + margin-left:133px; + } + + .bx-spark-connect-stop { + } + + .bx-spark-hive { + margin-left:133px; + + .bx-spark-enable-hive-checkbox { + width: auto; + vertical-align: bottom; + } + } + .bx-spark-save { + margin: 0 4px 0 10px; + } + + .widget-dropdown select { + width: 100%; + } + + .widget-label { + width: 123px; + margin-right: 10px; + text-align: right; + vertical-align: bottom; + display: inline-block; + } + + .widget-button { + border: 1px solid #cccccc; + } + + .bx-button { + margin-top: 0; + margin-bottom: 0; + margin-left: 0; + } + +} + +.bx-label { + box-sizing: border-box; + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + text-align: center; + vertical-align: baseline; + border-radius: .25em; + color: #ffffff; + + &.done { background-color: $bxColorSuccess; } + &.active { background-color: $bxColorInfo; } + &.waiting { background-color: $bxColorWarning; } + &.all { background-color: $bxColorDefault; } + &.error { background-color: $bxColorError; } +} diff --git a/js/sass/partials/spark_old.scss b/js/sass/partials/spark_old.scss new file mode 100644 index 0000000..7d4db0a --- /dev/null +++ b/js/sass/partials/spark_old.scss @@ -0,0 +1,355 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import "bxvariables"; + +.bx-spark-stageGroupPanel { + margin: 8px 0; +} + +.bx-spark-stagePanel { + margin: 8px 0; +} + +.bx-spark-stageGroupPanel .jupyter-button { + overflow: visible; +} + +.bx-spark-state-progress-box { + max-width: 500px; + width: 100%; +} + +.foldout-preview { + & > .bx-spark-stageGroupPanel { + padding: 0 15px; + margin: 0; + } +} + +.spark-foldout-preview { + max-width: 530px; +} + +.bx-panel { + border: 1px solid $bxBorderColor1; + border-radius: 2px; + margin-bottom: 18px; + + .bx-panel-heading { + background-color: $bxBgColor2; + padding: 10px; + border-bottom: 1px solid $bxBorderColor1; + + display: flex; + } + + .bx-panel-body { + padding: 10px; + } +} + +.bx-spark-stageProgressBar { + margin: 0 10px; + display: flex; + align-items: center; + border-radius: .25em; + overflow: hidden; +} + +.bx-progress-bar { + display: inline-block; + height: 18px; + + &.done { background-color: $bxColorSuccess; } + &.active { background-color: $bxColorInfo; } + &.waiting { background-color: $bxColorWarning; } + &.cancelled { background-color: $bxColorError; } +} + +.bx-row { + display: flex; + width: 100%; + .bx-text-right { + text-align: right; + white-space: nowrap; + } + .bx-col-xs-4 { + display: inline-block; + white-space: nowrap; + } + .bx-col-xs-6 { + display: inline-block; + width: 100%; + } +} + +.bx-spark-stageProgressLabels { + margin: 0; +} + +.bx-stats, +.bx-spark-stageProgressLabels { + display: block; + line-height: 100%; + height: 18px; + align-items: center; + + .bx-label { + display: inline-block; + max-height: 100%; + line-height: 150%; + } +} + +.bx-stats { + margin: 4px 0; + height: 20px; + + .label { + font-weight: normal; + } +} + +.bx-status-panel { + padding: 2px 0; + font-size: 14px; + .bx-button { + height: 24px; + padding: 0 6px; + } +} + +//.bx-connection-status { +// background-color: transparent; +// border: 1px solid $bxBorderColor1; +// height: 17px !important; +// padding: 0 4px; +// border-radius: .25em; +// margin: 4px !important; +// font-size: 75% !important; +// line-height: 1 !important; +// +// &.connected { +// border: none; +// background-color: $bxColorSuccess; +// color: $bxBgColor1; +// &::after { +// content: ""; +// background-image: url('data:image/png;base64,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'); +// background-size: contain; +// background-repeat: no-repeat; +// background-position: center center; +// width: 15px; +// height: 15px; +// display: inline-block; +// } +// } +//} + +.widget-spark-ui { + .bx-spark-config { + width: 606px; + max-width: 99%; + height: auto; + + .widget-label { + margin-right: 10px; + width: 292px; + } + + select, + input { + width: 300px; + margin: 2px; + height: 28px; + } + + input, + .widget-label { + flex-grow: 1; + flex-shrink: 1; + } + } + + .bx-config-name input { + text-align: right; + } + + .bx-spark-connect { + margin-left: 306px; + } + + .bx-spark-connect-error { + margin-left: 20px; + } + + .bx-spark-configuration { + .bx-properties-add-label { + width: 298px; + text-align: right; + } + } + + .bx-spark-hive-support { + margin-left: 218px; + input { + text-align: right; + } + } +} + + +.bx-spark-save-button { + margin-left: 10px; + width: 80px; +} + +.bx-toolbar-spark-widget { + display: inline-block; + overflow: hidden; + margin-top: 0; + margin-left: 5px; + max-height: 24px; + vertical-align: middle; + float: right; + + .bx-status-panel > .p-Widget { + margin-top: 2px; + } + .bx-stats { + margin-right: 5px!important; + } + .bx-connection-status { + margin-top: 2px !important; + } +} + +//@keyframes lds-spinner { +// 0% { +// opacity: 1; +// } +// 100% { +// opacity: 0; +// } +//} +//@-webkit-keyframes lds-spinner { +// 0% { +// opacity: 1; +// } +// 100% { +// opacity: 0; +// } +//} +//.lds-spinner { +// position: relative; +// width: 30px !important; +// height: 30px !important; +// -webkit-transform: translate(-15px, -15px) scale(0.15) translate(15px, 15px); +// transform: translate(-15px, -15px) scale(0.15) translate(15px, 15px); +// +// div { +// left: 94px; +// top: 48px; +// position: absolute; +// -webkit-animation: lds-spinner linear 1s infinite; +// animation: lds-spinner linear 1s infinite; +// background: #0055a5; +// width: 12px; +// height: 24px; +// border-radius: 40%; +// -webkit-transform-origin: 6px 52px; +// transform-origin: 6px 52px; +// &:nth-child(1) { +// -webkit-transform: rotate(0deg); +// transform: rotate(0deg); +// -webkit-animation-delay: -0.916666666666667s; +// animation-delay: -0.916666666666667s; +// } +// &:nth-child(2) { +// -webkit-transform: rotate(30deg); +// transform: rotate(30deg); +// -webkit-animation-delay: -0.833333333333333s; +// animation-delay: -0.833333333333333s; +// } +// &:nth-child(3) { +// -webkit-transform: rotate(60deg); +// transform: rotate(60deg); +// -webkit-animation-delay: -0.75s; +// animation-delay: -0.75s; +// } +// &:nth-child(4) { +// -webkit-transform: rotate(90deg); +// transform: rotate(90deg); +// -webkit-animation-delay: -0.666666666666667s; +// animation-delay: -0.666666666666667s; +// } +// &:nth-child(5) { +// -webkit-transform: rotate(120deg); +// transform: rotate(120deg); +// -webkit-animation-delay: -0.583333333333333s; +// animation-delay: -0.583333333333333s; +// } +// &:nth-child(6) { +// -webkit-transform: rotate(150deg); +// transform: rotate(150deg); +// -webkit-animation-delay: -0.5s; +// animation-delay: -0.5s; +// } +// &:nth-child(7) { +// -webkit-transform: rotate(180deg); +// transform: rotate(180deg); +// -webkit-animation-delay: -0.416666666666667s; +// animation-delay: -0.416666666666667s; +// } +// &:nth-child(8) { +// -webkit-transform: rotate(210deg); +// transform: rotate(210deg); +// -webkit-animation-delay: -0.333333333333333s; +// animation-delay: -0.333333333333333s; +// } +// &:nth-child(9) { +// -webkit-transform: rotate(240deg); +// transform: rotate(240deg); +// -webkit-animation-delay: -0.25s; +// animation-delay: -0.25s; +// } +// &:nth-child(10) { +// -webkit-transform: rotate(270deg); +// transform: rotate(270deg); +// -webkit-animation-delay: -0.166666666666667s; +// animation-delay: -0.166666666666667s; +// } +// &:nth-child(11) { +// -webkit-transform: rotate(300deg); +// transform: rotate(300deg); +// -webkit-animation-delay: -0.083333333333333s; +// animation-delay: -0.083333333333AAA333s; +// } +// &:nth-child(12) { +// -webkit-transform: rotate(330deg); +// transform: rotate(330deg); +// -webkit-animation-delay: 0s; +// animation-delay: 0s; +// } +// } +//} + +.bx-spark-profile { + .widget-label { + width: 140px !important + } +} diff --git a/js/sass/partials/spinner.scss b/js/sass/partials/spinner.scss new file mode 100644 index 0000000..3a54561 --- /dev/null +++ b/js/sass/partials/spinner.scss @@ -0,0 +1,48 @@ +$spinner_size: 28px; +$spinner_radius: $spinner_size * .35; +$spinner_width: $spinner_size * .08; +$spinner_height: $spinner_size * .22; +$spinner_color: none; +$spinner_bgcolor: #000000; +$spinner_speed: 1.2s; + +@keyframes lds-spinner { + 0% { + opacity: 1 + } + 100% { + opacity: 0 + } +} + +.lds-spinner { + color: $spinner_color; + display: inline-block; + position: relative; + width: round($spinner_size); + height: round($spinner_size); + vertical-align: bottom; + + div { + transform-origin: round($spinner_size * .5) round($spinner_size * .5); + animation: lds-spinner $spinner_speed linear infinite; + &:after { + content: " "; + display: block; + position: absolute; + top: round($spinner_size * .5 - $spinner_radius - $spinner_height * .5); + left: round($spinner_size * .5 - $spinner_width * .5); + width: round($spinner_width); + height: round($spinner_height); + border-radius: 20%; + background: $spinner_bgcolor; + } + + @for $i from 0 through 11 { + &:nth-child(#{$i + 1}) { + transform: rotate(#{$i * 30deg}); + animation-delay: ($spinner_speed * -1 * (11 - $i) / 12); + } + } + } +} \ No newline at end of file diff --git a/js/sass/partials/tree.scss b/js/sass/partials/tree.scss new file mode 100644 index 0000000..493e6fc --- /dev/null +++ b/js/sass/partials/tree.scss @@ -0,0 +1,238 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@use "bxvariables" as *; + +a { + color: $bxColorLink; + text-decoration: none; + &:focus, &:hover { + color: $bxColorHover; + text-decoration: underline; + } +} + +#beakerx-tree-widget { + + .hidden { + display: none; + } + + &.isLab { + width: 100%; + height: 100%; + overflow: auto; + box-sizing: border-box; + + font-size: 13px; + } + + + * { + box-sizing: border-box; + } + + fieldset { + border: none; + margin: 0; + padding: 0; + } + + label { + font-weight: 400; + } + + .bx-banner-widget { + padding: 15px; + .beakerx_site_link { + display: inline-block; + margin: 0 75px 0 0; + } + svg { + width: 154px; + height: 44px; + transform: translateY(5px); + } + } + + .bx-options-widget { + padding: 15px 0; + display: flex; + .p-TabPanel-stackedPanel { + left: 15px; + right: 15px; + } + } + + .bx-ui-options-widget, + .bx-jvm-options-widget { + left: 10px; + right: 10px; + padding: 15px; + } + + .bx-ui-options-widget .form-check-label { + margin-left: 10px; + } + + .p-TabBar { + min-height: 30px; + max-height: 30px; + } + + .p-TabBar-content { + min-width: 0; + align-items: flex-end; + border-bottom: 1px solid $bxBorderColor1; + padding: 0 15px; + } + + .p-TabBar-tab { + flex: 0 1 auto; + min-height: 26px; + max-height: 26px; + min-width: 35px; + margin-left: -1px; + border: 1px solid transparent; + padding: 4px 15px; + } + + .p-TabBar-tab:first-child { + margin-left: 0; + } + + .p-TabBar-tab.p-mod-current { + background-color: $bxBgColor1; + border-color: $bxBorderColor1; + border-bottom-color: transparent; + transform: translateY(1px); + } + + .p-TabBar-tab:hover:not(.p-mod-current) { + background: $bxBgColor2; + } + + #default_options { + margin: 0 0 18px 0; + + .bx-wrapper { + display: flex; + align-items: stretch; + + label { + margin: 0 6px 0 0; + line-height: 32px; + } + + input { + height: 32px; + padding: 6px 12px; + font-size: 13px; + line-height: 1; + background-color: $bxBgColor1; + border: 1px solid $bxBorderColor1; + border-collapse: collapse; + border-radius: 2px 0 0 2px; + } + + span { + background-color: $bxBgColor2; + border: 1px solid $bxBorderColor1; + line-height: 30px; + padding: 0 12px; + font-size: 13px; + border-radius: 0 2px 2px 0; + border-left-width: 0; + } + } + } + + .bx-panel { + border: 1px solid $bxBorderColor1; + border-radius: 2px; + margin-bottom: 18px; + + .bx-panel-heading { + background-color: $bxBgColor2; + padding:10px; + border-bottom: 1px solid $bxBorderColor1; + + display: flex; + + .bx-btn { + border: 1px solid $bxBorderColor1; + border-radius: 2px; + margin: 0 0 0 6px; + background-color: $bxBgColor1; + padding: 2px 6px; + font-size: 1em; + line-height: 1; + + &:hover { + background-color: $bxBgColor2; + } + } + } + + .bx-panel-body { + padding:10px; + + .bx-form-row { + + &:not(:last-child) { + margin-bottom: 6px; + } + + .bx-input-text { + display: inline-block; + border: 1px solid $bxBorderColor1; + border-radius: 2px; + margin: 0 6px 0 0; + padding: 6px 12px; + font-size: 13px; + line-height: 1.4; + vertical-align: middle; + } + + .bx-btn { + border: 1px solid $bxBorderColor1; + border-radius: 2px; + margin: 0; + background-color: $bxBgColor1; + padding: 6px 12px; + font-size: 13px; + line-height: 1.4; + vertical-align: middle; + height: 32px; + + &:hover { + background-color: $bxBgColor2; + } + + } + + } + } + } + + .bx-sync-indicator-widget { + padding: 15px; + .saving { color: $bxColorWarning; } + .saved { color: $bxColorSuccess; } + .errors-wrapper { color: $bxColorError; } + } + +} diff --git a/js/src/extension.ts b/js/src/extension.ts new file mode 100644 index 0000000..a8e368b --- /dev/null +++ b/js/src/extension.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'flatpickr/dist/flatpickr.css'; +import 'jquery-ui/themes/base/all.css'; +import '@phosphor/widgets/style/index.css'; +import './../css/beakerx_widgets.css'; +/* eslint-disable @typescript-eslint/no-explicit-any */ +if ((window as any).require) { + (window as any).require.config({ + map: { + '*': { + beakerx_widgets: 'nbextensions/beakerx_widgets/index', + 'jupyter-js-widgets': 'nbextensions/jupyter-js-widgets/extension', + '@jupyter-widgets/base': 'nbextensions/jupyter-js-widgets/extension', + '@jupyter-widgets/controls': 'nbextensions/jupyter-js-widgets/extension', + }, + }, + }); +} + +export { load_ipython_extension } from './extension/index'; diff --git a/js/src/extension/UIOptionsHelper.ts b/js/src/extension/UIOptionsHelper.ts new file mode 100644 index 0000000..7dffabc --- /dev/null +++ b/js/src/extension/UIOptionsHelper.ts @@ -0,0 +1,87 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BeakerXApi } from '../utils/api'; + +export function registerFeature(baseUrl: string): void { + if (Jupyter.NotebookList) { + return; + } + + const api = new BeakerXApi(baseUrl); + api + .loadSettings() + .then((data) => { + setupAutoCloseBrackets(data.ui_options.auto_close); + setupWideCells(data.ui_options.wide_cells); + setupImproveFonts(data.ui_options.improve_fonts); + setupShowCatalog(data.ui_options.show_catalog); + setupAutoSave(data.ui_options.auto_save); + }) + .catch((e) => { + console.log(e); + }); +} + +function setupAutoCloseBrackets(autoCloseBrackets: boolean): void { + // new cells + Jupyter.CodeCell.options_default.cm_config.autoCloseBrackets = autoCloseBrackets; + + // existing + const code_cells = Jupyter.notebook.get_cells().filter((cell) => { + return cell.cell_type === 'code'; + }); + + for (const cell of code_cells) { + const cm = cell.code_mirror; + if (cm.getOption('autoCloseBrackets') !== autoCloseBrackets) { + cm.setOption('autoCloseBrackets', autoCloseBrackets); + } + } +} + +function setupWideCells(wideCells: boolean): void { + if (!wideCells) { + return; + } + + const s = document.createElement('style'); + s.innerText = `#notebook_panel .container { width:auto; margin: 0 16px; }`; + + document.body.appendChild(s); +} + +function setupImproveFonts(improveFonts: boolean) { + if (!improveFonts) { + return; + } + + document.body.classList.add('improveFonts'); +} + +function setupShowCatalog(showCatalog: boolean) { + if (!showCatalog) { + return; + } +} + +function setupAutoSave(autoSave: boolean) { + if (autoSave) { + return; + } + + Jupyter.notebook.set_autosave_interval(0); +} diff --git a/js/src/extension/autotranslation.ts b/js/src/extension/autotranslation.ts new file mode 100644 index 0000000..57eda88 --- /dev/null +++ b/js/src/extension/autotranslation.ts @@ -0,0 +1,54 @@ +/* + * Copyright 2018 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BEAKER_AUTOTRANSLATION } from './comm'; +const utils = require('base/js/utils'); + +export class Autotranslation { + static readonly LOCK_PROXY = 'LOCK_PROXY'; + static readonly TABLE_FOCUSED = 'tableFocused'; + + public static proxify(beakerxInstance: any): Record { + function createCommForAT() { + return Jupyter.notebook.kernel.comm_manager.new_comm(BEAKER_AUTOTRANSLATION, null, null, null, utils.uuid()); + } + + let atComm = undefined; + const handler = { + get(obj, prop) { + return prop in obj ? obj[prop] : undefined; + }, + + set(obj, prop, value) { + obj[prop] = value; + if ( + prop !== Autotranslation.LOCK_PROXY && + prop !== Autotranslation.TABLE_FOCUSED && + !window.beakerx[Autotranslation.LOCK_PROXY] + ) { + if (!atComm) { + atComm = createCommForAT(); + } + atComm.send({ name: prop, value }); + } + + return true; + }, + }; + + return new Proxy>(beakerxInstance, handler); + } +} diff --git a/js/src/extension/codeEditor.ts b/js/src/extension/codeEditor.ts new file mode 100644 index 0000000..ce0aafd --- /dev/null +++ b/js/src/extension/codeEditor.ts @@ -0,0 +1,121 @@ +/* + * Copyright 2018 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const LINE_COMMENT_CHAR = '//'; +export const LINE_MAGIC_MODE = 'line_magic'; + +export function extendWithLineComment(Jupyter: any, CodeMirror: any) { + CodeMirror.extendMode('groovy', { lineComment: LINE_COMMENT_CHAR }); + + Jupyter.notebook.get_cells().map(setCodeMirrorLineComment); +} + +function setCodeMirrorLineComment(cell: any) { + if (cell.cell_type !== 'code') { + return; + } + + const cm = cell.code_mirror; + const doc = cm.getDoc(); + const mode = cm.getMode(); + + if (!mode.lineComment) { + mode.lineComment = LINE_COMMENT_CHAR; + doc.mode = mode; + } +} + +export function extendHighlightModes(Jupyter: any) { + Jupyter.CodeCell.options_default.highlight_modes = { + ...Jupyter.CodeCell.options_default.highlight_modes, + magic_python: { reg: ['^%%python'] }, + magic_groovy: { reg: ['^%%groovy'] }, + magic_java: { reg: ['^%%java'] }, + magic_scala: { reg: ['^%%scala'] }, + magic_kotlin: { reg: ['^%%kotlin'] }, + magic_clojure: { reg: ['^%%clojure'] }, + magic_sql: { reg: ['^%%sql'] }, + magic_html: { reg: ['^%%html'] }, + }; + + Jupyter.notebook.get_cells().map(setLineMagicForCell); + CodeMirror.defineInitHook(addLineMagicsOverlay); +} + +function setLineMagicForCell(cell) { + cell.auto_highlight(); + addLineMagicsOverlay(cell.code_mirror); +} + +const lineMagicOverlay = { + startState() { + return { firstMatched: false, inMagicLine: false }; + }, + + token(stream, state) { + if (stream.match(/^%(%classpath|%spark|\w+)/)) { + state.inMagicLine = true; + } + + if (state.inMagicLine) { + stream.eat(() => true); + + if (stream.eol()) { + state.inMagicLine = false; + } + + return LINE_MAGIC_MODE; + } + + stream.skipToEnd(); + + return null; + }, +}; + +export function autoHighlightLineMagics(code_mirror) { + const current_mode = code_mirror.getOption('mode'); + + if (current_mode === LINE_MAGIC_MODE) { + return; + } + + const re = /^%(%classpath|%spark|\w+)/; + + code_mirror.eachLine((line) => { + if (line && line.text.match(re) !== null) { + // Add an overlay mode to recognize the first line as "line magic" instead + // of the mode used for the rest of the cell. + CodeMirror.defineMode(LINE_MAGIC_MODE, (config) => { + return CodeMirror.overlayMode(CodeMirror.getMode(config, current_mode), lineMagicOverlay); + }); + + code_mirror.setOption('mode', LINE_MAGIC_MODE); + + return false; + } + }); +} + +export function addLineMagicsOverlay(code_mirror) { + autoHighlightLineMagics(code_mirror); + code_mirror.off('focus', autoHighlightLineMagics); + code_mirror.on('focus', autoHighlightLineMagics); + code_mirror.off('change', autoHighlightLineMagics); + code_mirror.on('change', autoHighlightLineMagics); + code_mirror.off('blur', autoHighlightLineMagics); + code_mirror.on('blur', autoHighlightLineMagics); +} diff --git a/js/src/extension/comm.ts b/js/src/extension/comm.ts new file mode 100644 index 0000000..2573fc4 --- /dev/null +++ b/js/src/extension/comm.ts @@ -0,0 +1,174 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BeakerXApi } from '../utils/api'; +const dialog = require('base/js/dialog'); +const { Comm } = require('services/kernels/comm'); + +export const BEAKER_GETCODECELLS = 'beakerx_widgets.getcodecells'; +export const BEAKER_GET_URL_ARG = 'beakerx_widgets.geturlarg'; +export const BEAKER_AUTOTRANSLATION = 'beakerx_widgets.autotranslation'; +export const BEAKER_TAG_RUN = 'beakerx_widgets.tag.run'; + +const msgHandlers = { + [BEAKER_GETCODECELLS]: (msg) => { + if (msg.content.data.state.name == 'CodeCells') { + sendJupyterCodeCells(JSON.parse(msg.content.data.state.value), msg.content.data.url); + } + + msgHandlers[BEAKER_AUTOTRANSLATION](msg); + }, + + [BEAKER_GET_URL_ARG]: (msg) => { + if (msg.content.data.state.name == 'URL_ARG') { + sendArgUrl(msg.content.data.url, msg.content.data.type, msg.content.data.state.arg_name); + } + }, + + [BEAKER_AUTOTRANSLATION]: (msg) => { + window.beakerx['LOCK_PROXY'] = true; + window.beakerx[msg.content.data.state.name] = JSON.parse(msg.content.data.state.value); + window.beakerx['LOCK_PROXY'] = false; + }, + + [BEAKER_TAG_RUN]: (msg) => { + if (!msg.content.data.state || !msg.content.data.state.runByTag) { + return; + } + + const notebook = Jupyter.notebook; + const cells = Jupyter.notebook.get_cells(); + const indexList = cells.reduce((acc, cell, index) => { + if (cell._metadata.tags && cell._metadata.tags.includes(msg.content.data.state.runByTag)) { + acc.push(index); + } + + return acc; + }, []); + + if (indexList.length === 0) { + dialog.modal({ + title: 'No cell with the tag !', + body: 'Tag: ' + msg.content.data.state.runByTag, + buttons: { OK: { class: 'btn-primary' } }, + notebook: Jupyter.notebook, + keyboard_manager: Jupyter.keyboard_manager, + }); + } else { + notebook.execute_cells(indexList); + } + }, +}; + +export const registerCommTargets = (kernel: any): void => { + kernel.comm_manager.register_target(BEAKER_GETCODECELLS, function (comm) { + comm.on_msg(msgHandlers[BEAKER_GETCODECELLS]); + }); + + kernel.comm_manager.register_target(BEAKER_AUTOTRANSLATION, function (comm) { + comm.on_msg(msgHandlers[BEAKER_AUTOTRANSLATION]); + }); + + kernel.comm_manager.register_target(BEAKER_TAG_RUN, function (comm) { + comm.on_msg(msgHandlers[BEAKER_TAG_RUN]); + }); + + kernel.comm_manager.register_target(BEAKER_GET_URL_ARG, function (comm) { + comm.on_msg(msgHandlers[BEAKER_GET_URL_ARG]); + }); + + kernel.comm_info(null, (msg) => { + assignMsgHandlersToExistingComms(msg.content.comms, kernel); + }); +}; + +const sendJupyterCodeCells = (filter: string, url: string) => { + const data: { code_cells: any; url: string } = { + code_cells: [], + url: url, + }; + data.code_cells = Jupyter.notebook.get_cells().filter(function (cell) { + if (cell._metadata.tags) { + return cell.cell_type == 'code' && cell._metadata.tags.includes(filter); + } + return false; + }); + const service = new BeakerxRestHandler(); + service.post(data); +}; + +const sendArgUrl = (url: string, type: string, argName: string) => { + const data: { url: string; type: string; argName: string; argValue: string } = { + argName: argName, + argValue: '', + url: url, + type: type, + }; + + const parsedUrl = new URL(window.location.href); + data.argValue = parsedUrl.searchParams.get(argName); + const service = new BeakerxRestHandler(); + service.post(data); +}; + +class BeakerxRestHandler { + private api: BeakerXApi; + + constructor() { + this.setApi(); + } + + private setApi() { + let baseUrl; + + if (this.api) { + return; + } + + try { + const coreutils = require('@jupyterlab/coreutils'); + coreutils.PageConfig.getOption('pageUrl'); + baseUrl = coreutils.PageConfig.getBaseUrl(); + } catch (e) { + baseUrl = `${window.location.origin}/`; + } + + this.api = new BeakerXApi(baseUrl); + } + + public post(data) { + this.api.restService(data).catch((err) => { + console.log(err); + }); + } +} + +const assignMsgHandlersToExistingComms = (comms, kernel) => { + for (const commId in comms) { + const comm = new Comm(comms[commId].target_name, commId); + kernel.comm_manager.register_comm(comm); + + assignMsgHandlerToComm(comm); + } +}; + +const assignMsgHandlerToComm = (comm) => { + const handler = msgHandlers[comm.target_name]; + + if (handler) { + comm.on_msg(handler); + } +}; diff --git a/js/src/extension/gistPublish/gistPublishModal.ts b/js/src/extension/gistPublish/gistPublishModal.ts new file mode 100644 index 0000000..7ed10f1 --- /dev/null +++ b/js/src/extension/gistPublish/gistPublishModal.ts @@ -0,0 +1,136 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { template } from './modalTemplate'; + +/* eslint-disable @typescript-eslint/no-var-requires */ +const dialog = require('base/js/dialog'); +const utils = require('base/js/utils'); +/* eslint-enable @typescript-eslint/no-var-requires */ + +export class GistPublishModal { + private settingsUrl: string; + + constructor() { + this.settingsUrl = `${(Jupyter.notebook_list || Jupyter.notebook).base_url}beakerx/settings`; + } + + show(submitCallback: (personalAccessToken: string) => void): void { + this.getGithubPersonalAccessToken().then((personalAccessToken) => { + this.create(submitCallback, personalAccessToken); + }); + } + + create(submitCallback, personalAccessToken = ''): void { + const modalContent = this.createModalContent(); + const personalAccessTokenInput = modalContent.querySelector('input'); + const form = modalContent.querySelector('form'); + const formGroup = modalContent.querySelector('.form-group'); + const errorNode = this.createErrorIconNode(); + + const submitHandler = (event) => { + const personalAccessToken = personalAccessTokenInput ? personalAccessTokenInput.value : ''; + + event.preventDefault(); + + if (!personalAccessToken || !personalAccessTokenInput.checkValidity()) { + personalAccessTokenInput.focus(); + formGroup.classList.add('has-error'); + formGroup.appendChild(errorNode); + + return false; + } + + submitCallback(personalAccessToken); + formGroup.contains(errorNode) && formGroup.removeChild(errorNode); + formGroup.classList.remove('has-error'); + this.storePersonalAccessToken(personalAccessToken); + modal.modal('hide'); + }; + + if (personalAccessTokenInput && form) { + personalAccessTokenInput.value = personalAccessToken; + } + + const modal = dialog.modal({ + keyboard_manager: Jupyter.notebook.keyboard_manager, + title: 'Publish to a GitHub Gist', + body: modalContent, + default_button: 'Publish', + buttons: { + Publish: { + class: 'btn-primary', + click: submitHandler, + }, + Cancel: {}, + }, + }); + + if (form) { + form.onsubmit = submitHandler; + } + } + + private createModalContent(): HTMLElement { + const modalContent = document.createElement('div'); + + modalContent.innerHTML = template; + + return modalContent; + } + + private createErrorIconNode() { + const errorNode = document.createElement('span'); + + errorNode.classList.add('fa'); + errorNode.classList.add('fa-remove'); + errorNode.classList.add('form-control-feedback'); + errorNode.style.fontSize = '18px'; + errorNode.style.lineHeight = '32px'; + + return errorNode; + } + + storePersonalAccessToken(githubPersonalAccessToken = ''): Promise { + return this.getStoredSettings().then((storedSettings) => { + storedSettings.beakerx.githubPersonalAccessToken = githubPersonalAccessToken; + utils + .ajax(this.settingsUrl, { + type: 'POST', + data: JSON.stringify({ + ...storedSettings, + }), + }) + .fail((reason) => { + console.log(reason); + }); + }); + } + + private getGithubPersonalAccessToken(): Promise { + return this.getStoredSettings().then((settings) => settings.beakerx.githubPersonalAccessToken || ''); + } + + private getStoredSettings(): Promise { + return utils + .ajax(this.settingsUrl, { + method: 'GET', + }) + .fail((reason) => { + console.log(reason); + }); + } +} diff --git a/js/src/extension/gistPublish/index.ts b/js/src/extension/gistPublish/index.ts new file mode 100644 index 0000000..505c766 --- /dev/null +++ b/js/src/extension/gistPublish/index.ts @@ -0,0 +1,114 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import $ from 'jquery'; +import { GistPublishModal } from './gistPublishModal'; +import { GistPublisher, GistPublisherUtils } from '../../plots/publisher'; +import { AccessTokenProvider } from '../../plots/publisher/AccessTokenProvider'; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const dialog = require('base/js/dialog'); + +export function registerFeature(): void { + setupPublisher(); + if (Jupyter.NotebookList) { + return; + } + + Jupyter.toolbar.add_buttons_group([ + { + label: ' ', + id: 'btn_publish', + icon: 'fa-share-alt', + callback: openPublishDialog, + }, + ]); + + $('#btn_publish > span').remove(); + $('#btn_publish').attr({ + title: 'Publish...', + }); + + const publish_menu = $('
  • ').attr('id', 'publish_gist').append($('').attr('href', '#').html('Publish...')); + + publish_menu.insertAfter($('#print_preview')); + publish_menu.click(openPublishDialog); +} + +function setupPublisher() { + const options = { + accessTokenProvider: new AccessTokenProvider(), + saveWidgetsStateHandler: saveWidgetsState, + prepareContentToPublish: (scope) => { + const el = scope.node || scope.element[0]; + let cell; + for (const c of Jupyter.notebook.get_cells()) { + if (c.element[0].contains(el)) { + cell = c; + break; + } + } + + const nbjson = Jupyter.notebook.toJSON(); + nbjson.cells = [cell.toJSON()]; + return nbjson; + }, + }; + GistPublisherUtils.setup(options); +} + +function openPublishDialog(): void { + new GistPublishModal().show((personalAccessToken) => { + saveWidgetsState() + .then(() => { + doPublish(personalAccessToken); + }) + .catch((reason) => console.log(reason)); + }); +} + +function showErrorDialog(errorMsg) { + dialog.modal({ + title: 'Gist publication error', + body: `Uploading gist failed: ${errorMsg}`, + buttons: { + OK: { + class: 'btn-primary', + }, + }, + }); +} + +export function saveWidgetsState(): Promise { + return new Promise((resolve, reject) => { + if (Jupyter.menubar.actions.exists('widgets:save-with-widgets')) { + Jupyter.menubar.actions.call('widgets:save-with-widgets'); + console.log('widgets state has been saved'); + + setTimeout(() => { + resolve(Jupyter.notebook.notebook_name); + }); + } else { + reject('widgets:save-with-widgets actions is not registered'); + } + }); +} + +export function doPublish(personalAccessToken: string): void { + GistPublisher.doPublish(personalAccessToken, Jupyter.notebook.notebook_name, Jupyter.notebook.toJSON(), (errorMsg) => + showErrorDialog(errorMsg), + ); +} diff --git a/js/src/extension/gistPublish/modalTemplate.ts b/js/src/extension/gistPublish/modalTemplate.ts new file mode 100644 index 0000000..747130a --- /dev/null +++ b/js/src/extension/gistPublish/modalTemplate.ts @@ -0,0 +1,31 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const template = ` +
    +
    +
    + + +
    +

    + Enter a Personal Access Token to publish the notebook as a gist in your GitHub account.
    + We recommend your Token have only the gist scope.
    + For more information, read the documentation for scopes. +

    +
    +
    +`; diff --git a/js/src/extension/htmlOutput.ts b/js/src/extension/htmlOutput.ts new file mode 100644 index 0000000..bf24f49 --- /dev/null +++ b/js/src/extension/htmlOutput.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import $ from 'jquery'; + +export function displayHTML(outputArea, html) { + if (html && outputArea && outputArea.element) { + $(outputArea.element).append(html); + } +} diff --git a/js/src/extension/index.ts b/js/src/extension/index.ts new file mode 100644 index 0000000..a951eca --- /dev/null +++ b/js/src/extension/index.ts @@ -0,0 +1,108 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file contains the javascript that is run when the notebook is loaded. +// It contains some requirejs configuration and the `load_ipython_extension` +// which is required for any notebook extension. + +import { extendHighlightModes, extendWithLineComment } from './codeEditor'; +import { registerFeature } from './UIOptionsHelper'; +import { enableInitializationCellsFeature } from './initializationCells'; +import { Autotranslation } from './autotranslation'; +import { installHandler as installKernelHandler } from './kernel'; +import { displayHTML } from './htmlOutput'; +import { bkCoreManager } from '../utils/bk/bkCoreManager'; + +/* eslint-disable @typescript-eslint/no-var-requires */ +const configmod = require('services/config'); +const utils = require('base/js/utils'); +const Jupyter = require('base/js/namespace'); +const events = require('base/js/events'); +const plotApi = require('../plots/plot/_js/plotApi'); +const big = require('big.js'); +const tocUtils = require('./tableOfContents/index'); +/* eslint-enable @typescript-eslint/no-var-requires */ + +window['Big'] = big; + +const base_url = utils.get_body_data('baseUrl'); + +new configmod.ConfigSection('notebook', { base_url: base_url }); + +const MOD_NAME = 'init_cell'; +const log_prefix = `[${MOD_NAME}]`; +let options = { + // updated from server's config & nb metadata + run_on_kernel_ready: true, +}; + +registerFeature(base_url); + +function callback_notebook_loaded() { + enableInitializationCellsFeature(options); + tocUtils.toc_init(); + installKernelHandler(); +} + +function extendWindowObject() { + if (!window) { + return; + } + + const plotApiList = plotApi.list(); + const bkObject = bkCoreManager.getBkApp().getBeakerObject(); + const beakerxInstance = { + ...plotApiList, + displayHTML, + prefs: bkObject.beakerObj.prefs, + }; + + if (!window.beakerx) { + window.beakerx = Autotranslation.proxify(beakerxInstance); + } +} + +function setupNotebook() { + if (Jupyter.NotebookList) { + return; // Notebook not loaded + } + + Jupyter.notebook.config.loaded + .then( + () => { + options = { ...options, ...Jupyter.notebook.config.data[MOD_NAME] }; + }, + (reason) => { + console.warn(log_prefix, 'error loading config:', reason); + }, + ) + .then(() => { + Jupyter.notebook._fully_loaded + ? callback_notebook_loaded() + : events.on('notebook_loaded.Notebook', callback_notebook_loaded); + }) + .catch((reason) => { + console.error(log_prefix, 'unhandled error:', reason); + }); + + extendWithLineComment(Jupyter, CodeMirror); + extendHighlightModes(Jupyter); +} + +export function load_ipython_extension(): void { + extendWindowObject(); + setupNotebook(); +} diff --git a/js/src/extension/initializationCells.ts b/js/src/extension/initializationCells.ts new file mode 100644 index 0000000..c6728d5 --- /dev/null +++ b/js/src/extension/initializationCells.ts @@ -0,0 +1,130 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface IInitCellsOptions { + run_on_kernel_ready: boolean; + run_untrusted?: boolean; +} + +/* eslint-disable @typescript-eslint/no-var-requires */ +const CellToolbar = require('notebook/js/celltoolbar').CellToolbar; +const CodeCell = require('notebook/js/codecell').CodeCell; +const dialog = require('base/js/dialog'); +const events = require('base/js/events'); +/* eslint-enable @typescript-eslint/no-var-requires */ +const modName = 'init_cell'; +const logPrefix = `[${modName}]`; + +export const TOOLBAR_PRESET_NAME = 'Initialization Cell'; + +export const initCellUiCallback = CellToolbar.utils.checkbox_ui_generator( + TOOLBAR_PRESET_NAME, + (cell, value) => { + if (value) { + cell.metadata.init_cell = true; + } else { + delete cell.metadata.init_cell; + } + }, + (cell) => cell.metadata.init_cell, // if init_cell is undefined, it'll be interpreted as false anyway +); + +export function registerCelltoolbarPreset(): void { + if (CellToolbar.list_presets().indexOf(TOOLBAR_PRESET_NAME) > -1) { + return; + } + + CellToolbar.register_callback('init_cell.is_init_cell', initCellUiCallback, 'code'); + CellToolbar.register_preset(TOOLBAR_PRESET_NAME, ['init_cell.is_init_cell'], Jupyter.notebook); +} + +export function enableInitializationCellsFeature(options: IInitCellsOptions) { + const modOptions = Jupyter.notebook.metadata[modName]; + + if (modOptions !== undefined) { + console.log(logPrefix, 'updating options from notebook metadata:', modOptions); + options = { ...options, ...modOptions }; + } + + registerCelltoolbarPreset(); + registerNotebookInitCellsAction(options); + runInitCells(options); + + events.on('kernel_ready.Kernel', () => runInitCells(options)); +} + +export function runInitCells(options: IInitCellsOptions): void { + const cells = getInitCells(); + + handleUntrustedKernelInitCells(cells, options); + + if (!canExecuteInitCells(options)) { + return; + } + + console.log(logPrefix, 'running all initialization cells'); + + let num = 0; + + for (let i = 0; i < cells.length; i++) { + cells[i].execute(); + num++; + } + + console.log(logPrefix, `finished running ${num} initialization cell${num !== 1 ? 's' : ''}`); +} + +export function getInitCells(): any[] { + return Jupyter.notebook.get_cells().filter((cell) => cell instanceof CodeCell && cell.metadata.init_cell === true); +} + +function canExecuteInitCells(options: IInitCellsOptions) { + return ( + options.run_on_kernel_ready && + Jupyter.notebook && + (Jupyter.notebook.trusted || options.run_untrusted) && + Jupyter.notebook.kernel && + Jupyter.notebook.kernel.info_reply.status === 'ok' + ); +} + +function handleUntrustedKernelInitCells(cells, options) { + if (!Jupyter.notebook.trusted && !options.run_untrusted && cells.length) { + dialog.modal({ + title: 'Initialization cells in untrusted notebook', + body: + 'This notebook is not trusted, so initialization cells will not be automatically run on kernel load. You can still run them manually, though.', + buttons: { OK: { class: 'btn-primary' } }, + notebook: Jupyter.notebook, + keyboard_manager: Jupyter.keyboard_manager, + }); + } +} + +export function registerNotebookInitCellsAction(options): void { + const prefix = 'auto'; + const action_name = 'run-initialization-cells'; + const action = { + icon: 'fa-calculator', + help: 'Run all initialization cells', + help_index: 'zz', + handler: () => runInitCells({ ...options, run_untrusted: true }), + }; + const action_full_name = Jupyter.notebook.keyboard_manager.actions.register(action, action_name, prefix); + + // add toolbar button + Jupyter.toolbar.add_buttons_group([action_full_name]); +} diff --git a/js/src/extension/kernel.ts b/js/src/extension/kernel.ts new file mode 100644 index 0000000..57bd297 --- /dev/null +++ b/js/src/extension/kernel.ts @@ -0,0 +1,66 @@ +/* + * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { registerCommTargets } from './comm'; +import { BeakerXApi } from '../utils/api'; + +export function installHandler() { + const kernel = Jupyter.notebook.kernel; + + registerCommTargets(kernel); + + Jupyter.notebook.events.on('kernel_interrupting.Kernel', () => { + interrupt(); + }); +} + +function interrupt() { + if (Jupyter.notebook.kernel.info_reply.url_to_interrupt) { + interruptToKernel(Jupyter.notebook.kernel.info_reply.url_to_interrupt); + } +} + +function interruptToKernel(url_to_interrupt: string) { + new BeakerXInterruptRestHandler().post({ url: url_to_interrupt }); +} + +class BeakerXInterruptRestHandler { + private readonly api: BeakerXApi; + + constructor() { + if (this.api) { + return; + } + + let baseUrl; + + try { + const coreutils = require('@jupyterlab/coreutils'); + coreutils.PageConfig.getOption('pageUrl'); + baseUrl = coreutils.PageConfig.getBaseUrl(); + } catch (e) { + baseUrl = `${window.location.origin}/`; + } + + this.api = new BeakerXApi(baseUrl); + } + + public post(data) { + this.api.restService(data).catch((err) => { + console.log(err); + }); + } +} diff --git a/js/src/extension/tableOfContents/index.js b/js/src/extension/tableOfContents/index.js new file mode 100644 index 0000000..69bff1a --- /dev/null +++ b/js/src/extension/tableOfContents/index.js @@ -0,0 +1,207 @@ +/* + * Copyright 2018 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Adapted from https://gist.github.com/magican/5574556 +// by minrk https://github.com/minrk/ipython_extensions +// See the history of contributions in README.md + +define(['jquery', 'base/js/namespace', 'base/js/events', 'notebook/js/codecell', './toc2'], function ( + $, + Jupyter, + events, + codecell, + toc2, +) { + 'use strict'; + + // imports + var highlight_toc_item = toc2.highlight_toc_item; + var table_of_contents = toc2.table_of_contents; + var toggle_toc = toc2.toggle_toc; + var IPython = Jupyter; + + // extra download as html with toc menu + function addSaveAsWithToc() { + if (parseFloat(Jupyter.version.substr(0, 3)) >= 5.1) { + if ($('#download_html_toc').length === 0) { + /* Add an entry in the download menu */ + var downloadEntry = $('
  • HTML with toc (.html)
  • '); + $('#download_html').after(downloadEntry); + downloadEntry.click(function () { + Jupyter.menubar._nbconvert('html_toc', true); + }); + } + } else { + /* Add a "save a" menu entry for pre 5.1 versions (needs python kernel) */ + if (IPython.notebook.metadata.kernelspec.language === 'python') { + if ($('#save_html_with_toc').length === 0) { + $('#save_checkpoint').after("
  • "); + $('#save_html_with_toc') + .append($('').text('Save as HTML (with toc)').attr('href', '#')) + .on('click', function (evt) { + if (IPython.notebook.metadata.kernelspec.language === 'python') { + var code = "!jupyter nbconvert '" + IPython.notebook.notebook_name + "' --template toc2"; + console.log('[toc2] running:', code); + IPython.notebook.kernel.execute(code); + } else { + alert('Sorry; this only works with a IPython kernel'); + $('#save_html_with_toc').remove(); + } + }); + } + } else { + $('#save_html_with_toc').remove(); + } + } + } + + var toc_button = function (cfg) { + if (!IPython.toolbar) { + events.on('app_initialized.NotebookApp', function (evt) { + toc_button(cfg); + }); + return; + } + if ($('#toc_button').length === 0) { + $( + IPython.toolbar.add_buttons_group([ + Jupyter.keyboard_manager.actions.register( + { + help: 'Table of Contents', + icon: 'fa-list', + handler: function () { + toggle_toc(cfg); + }, + }, + 'toggle-toc', + 'toc2', + ), + ]), + ) + .find('.btn') + .attr('id', 'toc_button'); + } + }; + + function create_additional_css(cfg) { + var sheet = document.createElement('style'); + sheet.innerHTML = + '#toc li > span:hover { background-color: ' + + cfg.colors.hover_highlight + + ' }\n' + + '.toc-item-highlight-select {background-color: ' + + cfg.colors.selected_highlight + + '}\n' + + '.toc-item-highlight-execute {background-color: ' + + cfg.colors.running_highlight + + '}\n' + + '.toc-item-highlight-execute.toc-item-highlight-select {background-color: ' + + cfg.colors.selected_highlight + + '}'; + if (cfg.moveMenuLeft) { + sheet.innerHTML += 'div#menubar-container, div#header-container {\n' + 'width: auto;\n' + 'padding-left: 20px; }'; + } + // Using custom colors + sheet.innerHTML += '#toc-wrapper { background-color: ' + cfg.colors.wrapper_background + '}\n'; + sheet.innerHTML += '#toc a, #navigate_menu a, .toc { color: ' + cfg.colors.navigate_text + '}'; + sheet.innerHTML += '#toc-wrapper .toc-item-num { color: ' + cfg.colors.navigate_num + '}'; + sheet.innerHTML += '.sidebar-wrapper { border-color: ' + cfg.colors.sidebar_border + '}'; + sheet.innerHTML += '.highlight_on_scroll { border-left: solid 4px ' + cfg.colors.on_scroll + '}'; + document.body.appendChild(sheet); + } + + var CodeCell = codecell.CodeCell; + + function patch_CodeCell_get_callbacks() { + var previous_get_callbacks = CodeCell.prototype.get_callbacks; + CodeCell.prototype.get_callbacks = function () { + var callbacks = previous_get_callbacks.apply(this, arguments); + var prev_reply_callback = callbacks.shell.reply; + callbacks.shell.reply = function (msg) { + if (msg.msg_type === 'execute_reply') { + setTimeout(function () { + $('.toc .toc-item-highlight-execute').removeClass('toc-item-highlight-execute'); + rehighlight_running_cells(); // re-highlight running cells + }, 100); + var c = IPython.notebook.get_selected_cell(); + highlight_toc_item( + { + type: 'selected', + }, + { + cell: c, + }, + ); + } + return prev_reply_callback(msg); + }; + return callbacks; + }; + } + + function rehighlight_running_cells() { + $.each( + $('.running'), // re-highlight running cells + function (idx, elt) { + highlight_toc_item( + { + type: 'execute', + }, + $(elt).data(), + ); + }, + ); + } + + var toc_init = function () { + // read configuration, then call toc + IPython.notebook.config.loaded.then(function () { + var cfg = toc2.read_config(); + // create highlights style section in document + create_additional_css(cfg); + // add toc toggle button (now that cfg has loaded) + toc_button(cfg); + // call main function with newly loaded config + table_of_contents(cfg); + // event: render toc for each markdown cell modification + events.on('rendered.MarkdownCell', function (evt, data) { + table_of_contents(cfg); // recompute the toc + rehighlight_running_cells(); // re-highlight running cells + highlight_toc_item(evt, data); // and of course the one currently rendered + }); + }); + + // event: on cell selection, highlight the corresponding item + events.on('select.Cell', highlight_toc_item); + // event: if kernel_ready (kernel change/restart): add/remove a menu item + events.on('kernel_ready.Kernel', function () { + addSaveAsWithToc(); + }); + + // add a save as HTML with toc included + addSaveAsWithToc(); + // Highlight cell on execution + patch_CodeCell_get_callbacks(); + events.on('execute.CodeCell', highlight_toc_item); + console.log('[toc2] Notebook fully loaded -- toc2 initialized '); + }; + + return { + toc_init: toc_init, + toggle_toc: toggle_toc, + table_of_contents: table_of_contents, + }; +}); diff --git a/js/src/extension/tableOfContents/toc.css b/js/src/extension/tableOfContents/toc.css new file mode 100644 index 0000000..57e27d6 --- /dev/null +++ b/js/src/extension/tableOfContents/toc.css @@ -0,0 +1,199 @@ +/* + * Copyright 2018 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + +originally extracted from https://gist.github.com/magican/5574556 + +Most colors defined here are overridden by javascript which adds css based on +values in the server config file notebook.json, which can be edited directly, +or colors can be selected in the nbextensions_configurator +*/ + + + +/*background color for links when you mouse over it */ +#toc-wrapper li > span:hover { + background-color: #DAA520; +} + +#toc a { + color: #333333; /* default - alterable via nbextension-configurator */ + text-decoration: none; +} +#navigate_menu li > span:hover {background-color: #f1f1f1} + + +/* Move menus and tooolbar to the left, following @Kevin-McIsaac suggestion +This is now done in javascript, if the relevant option is selected +div#menubar-container, div#header-container { +width: auto; +padding-left: 20px; +}*/ + +#navigate_menu { + list-style-type: none; + max-width: 800px; + min-width: 100px; + width: 250px; + overflow: auto; +} + + +#navigate_menu a { + list-style-type: none; + color: #333333; /* default - alterable via nbextension-configurator */ + text-decoration: none; +} + +#navigate_menu li { + padding-left: 0px; + clear: both; + list-style-type: none; +} + +#navigate_menu > .toc-item, +#navigate_menu ul { + padding-left: 0px; +} + +.toc { + padding: 0px; + overflow-y: auto; + font-weight: normal; + color: #333333; /* default - alterable via nbextension-configurator */ + white-space: nowrap; + overflow-x: auto; +} + +.text_cell .toc { + margin-top: 1em; +} + +.toc ul.toc-item { + list-style-type: none; + padding: 0; + margin: 0; +} + +#toc-wrapper { + z-index: 90; + position: fixed !important; + display: flex; + flex-direction: column; + overflow: hidden; + padding: 10px; + border-style: solid; + border-width: thin; + background-color: #fff; /* default - alterable via nbextension-configurator */ +} + +#toc-wrapper .toc { + flex-grow: 1; +} + +.float-wrapper { + border-color: rgba(0, 0, 0, 0.38); + border-radius: 5px; + opacity: .8; +} + +.sidebar-wrapper { + top: 0; + bottom: 0; + width: 212px; + border-color: #eeeeee; /* default - alterable via nbextension-configurator */ +} + +.sidebar-wrapper .ui-resizable-se { + display: none; +} + +.sidebar-wrapper .ui-resizable-e { + position: absolute; + top: calc(50% - 8px); +} + +#toc-wrapper.closed { + min-width: 100px; + width: auto; + transition: width; +} +#toc-wrapper:hover{ + opacity: 1; +} +#toc-wrapper .header { + font-size: 18px; + font-weight: bold; +} + +.sidebar-wrapper .hide-btn { + display:none; +} + +#toc-wrapper .hide-btn:before { + content: "\f147"; +} + +#toc-wrapper.closed .hide-btn:before { + content: "\f196"; +} + +#toc-header .fa { + font-size: 14px; + text-decoration: none; +} + +/* on scroll style */ +.highlight_on_scroll { + border-left: solid 4px blue; +} + +.toc-item li { margin:0; padding:0; color:black } +.toc-item li > span { display:block } +.toc-item li > span { padding-left:0em } +.toc-item li li > span { padding-left:1em } +.toc-item li li li > span { padding-left:2em } +.toc-item li li li li > span { padding-left:3em } +.toc-item li li li li li > span { padding-left:4em } +.toc-item li li li li li li > span { padding-left:5em } + + +#toc-wrapper .toc-item-num { + font-family: Georgia, Times New Roman, Times, serif; + color: black; /* default - alterable via nbextension-configurator */ +} + +/* +These colors are now specified in js, after reading the extension's config stored in system +and updated using the nbextension-configurator +.toc-item-highlight-select {background-color: Gold} +.toc-item-highlight-execute {background-color: red} +.toc-item-highlight-execute.toc-item-highlight-select {background-color: Gold} */ + +#toc-header .fa , +.toc-item .fa-fw:first-child { + cursor: pointer; +} + +#toc-header, +.modal-header { + cursor: move; +} + +.tocSkip { + display: none; +} \ No newline at end of file diff --git a/js/src/extension/tableOfContents/toc2.js b/js/src/extension/tableOfContents/toc2.js new file mode 100644 index 0000000..2d4c595 --- /dev/null +++ b/js/src/extension/tableOfContents/toc2.js @@ -0,0 +1,799 @@ +/* + * Copyright 2018 TWO SIGMA OPEN SOURCE, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +define(['jquery'], function ($) { + 'use strict'; + + var IPython; + var events; + var liveNotebook = false; + var all_headers = $('#notebook').find(':header'); + + // default values for system-wide configurable parameters + var default_cfg = { + colors: { + hover_highlight: '#DAA520', + selected_highlight: '#FFD700', + running_highlight: '#FF0000', + wrapper_background: '#FFFFFF', + sidebar_border: '#EEEEEE', + navigate_text: '#333333', + navigate_num: '#000000', + on_scroll: '#2447f0', + }, + collapse_to_match_collapsible_headings: false, + markTocItemOnScroll: true, + moveMenuLeft: true, + navigate_menu: true, + threshold: 4, + widenNotebook: false, + }; + // default values for per-notebook configurable parameters + var metadata_settings = { + nav_menu: {}, + number_sections: false, + sideBar: false, + skip_h1_title: false, + base_numbering: 1, + title_cell: 'Table of Contents', + title_sidebar: 'Contents', + toc_cell: false, + toc_position: {}, + toc_section_display: false, + toc_window_display: false, + }; + $.extend(true, default_cfg, metadata_settings); + + /** + * Read our config from server config & notebook metadata + * This function should only be called when both: + * 1. the notebook (and its metadata) has fully loaded + * AND + * 2. Jupyter.notebook.config.loaded has resolved + */ + var read_config = function () { + var cfg = default_cfg; + // config may be specified at system level or at document level. + // first, update defaults with config loaded from server + $.extend(true, cfg, IPython.notebook.config.data.toc2); + // ensure notebook metadata has toc object, cache old values + var md = IPython.notebook.metadata.toc || {}; + // reset notebook metadata to remove old values + IPython.notebook.metadata.toc = {}; + // then update cfg with any found in current notebook metadata + // and save in nb metadata (then can be modified per document) + Object.keys(metadata_settings).forEach(function (key) { + cfg[key] = IPython.notebook.metadata.toc[key] = (md.hasOwnProperty(key) ? md : cfg)[key]; + }); + return cfg; + }; + + // globally-used status variables: + var rendering_toc_cell = false; + // toc_position default also serves as the defaults for a non-live notebook + var toc_position = { height: 'calc(100% - 180px)', width: '20%', left: '10px', top: '150px' }; + + try { + // this will work in a live notebook because nbextensions & custom.js + // are loaded by/after notebook.js, which requires base/js/namespace + IPython = require('base/js/namespace'); + events = require('base/js/events'); + liveNotebook = true; + } catch (err) { + // We *are* theoretically in a non-live notebook + console.log('[toc2] working in non-live notebook'); //, err); + // in non-live notebook, there's no event structure, so we make our own + if (window.events === undefined) { + var Events = function () {}; + window.events = $([new Events()]); + } + events = window.events; + } + var Jupyter = IPython; + + var setMd = function (key, value) { + if (liveNotebook) { + var md = IPython.notebook.metadata.toc; + if (md === undefined) { + md = IPython.notebook.metadata.toc = {}; + } + var old_val = md[key]; + md[key] = value; + if (typeof _ !== undefined ? !_.isEqual(value, old_val) : old_val != value) { + IPython.notebook.set_dirty(); + } + } + return value; + }; + + function incr_lbl(ary, h_idx) { + //increment heading label w/ h_idx (zero based) + ary[h_idx]++; + for (var j = h_idx + 1; j < ary.length; j++) { + ary[j] = 0; + } + return ary.slice(0, h_idx + 1); + } + + function removeMathJaxPreview(elt) { + elt.children('.anchor-link, .toc-mod-link').remove(); + elt.find("script[type='math/tex']").each(function (i, e) { + $(e).replaceWith('$' + $(e).text() + '$'); + }); + elt.find('span.MathJax_Preview').remove(); + elt.find('span.MathJax').remove(); + return elt; + } + + var callback_toc_link_click = function (evt) { + // workaround for https://github.com/jupyter/notebook/issues/699 + setTimeout(function () { + $.ajax(); + }, 100); + evt.preventDefault(); + var trg_id = $(evt.currentTarget).attr('data-toc-modified-id'); + // use native scrollIntoView method with semi-unique id + // ! browser native click does't follow links on all browsers + document.getElementById(trg_id).scrollIntoView(true); + if (liveNotebook) { + // use native document method as jquery won't cope with characters + // like . in an id + var cell = $(document.getElementById(trg_id)).closest('.cell').data('cell'); + Jupyter.notebook.select(Jupyter.notebook.find_cell_index(cell)); + highlight_toc_item('toc_link_click', { + cell: cell, + }); + } + }; + + var make_link = function (h, toc_mod_id) { + var a = $('').attr({ + href: h.find('.anchor-link').attr('href'), + 'data-toc-modified-id': toc_mod_id, + }); + // get the text *excluding* the link text, whatever it may be + var hclone = h.clone(); + hclone = removeMathJaxPreview(hclone); + a.html(hclone.html()); + a.on('click', callback_toc_link_click); + return a; + }; + + function highlight_toc_item(evt, data) { + if (!data.cell) { + return; + } + var c = $(data.cell.element); + if (c.length < 1) { + return; + } + var trg_id = c.find('.toc-mod-link').attr('id') || c.prevAll().find('.toc-mod-link').eq(-1).attr('id'); + var highlighted_item = $(); + if (trg_id !== undefined) { + highlighted_item = $('.toc a').filter(function (idx, elt) { + return $(elt).attr('data-toc-modified-id') === trg_id; + }); + } + if (evt.type === 'execute') { + // remove the selected class and add execute class + // if the cell is selected again, it will be highligted as selected+running + highlighted_item.removeClass('toc-item-highlight-select').addClass('toc-item-highlight-execute'); + } else { + $('.toc .toc-item-highlight-select').removeClass('toc-item-highlight-select'); + highlighted_item.addClass('toc-item-highlight-select'); + } + } + + var create_navigate_menu = function (callback) { + $('#kernel_menu').parent().after('