Jupyter notebook renderer version 1.5.2
This commit is contained in:
Родитель
bec412eb9d
Коммит
82806a1ec5
|
@ -328,3 +328,9 @@ ASALocalRun/
|
|||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# VSIX package file
|
||||
*.vsix
|
||||
|
||||
# package-lock.json
|
||||
package-lock.json
|
|
@ -1,3 +1,12 @@
|
|||
# Azure Devops Extension: Jupyter Notebook Renderer
|
||||
|
||||
This repo contains source codes to [Jupyter Notebook Renderer Extension for Azure Devops](https://marketplace.visualstudio.com/items?itemName=ms-air-aiagility.ipynb-renderer)
|
||||
|
||||
## Build Locally
|
||||
|
||||
1. Install TFX cross platform command line interface (CLI): `npm i -g tfx-cli`
|
||||
|
||||
2. Run `tfx extension create --manifest-globs vss-extension.json`
|
||||
|
||||
# Contributing
|
||||
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 7.7 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 12 KiB |
|
@ -0,0 +1,48 @@
|
|||
|
||||
THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
|
||||
Do Not Translate or Localize
|
||||
|
||||
Jupyter Notebook Renderer Extension incorporates components from the projects listed below. Microsoft licenses these components to you under Microsoft’s licensing terms for the Jupyter Notebook Renderer Extension. The original copyright notices and the licenses under which Microsoft received such components are set forth below for informational purposes. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise.
|
||||
|
||||
|
||||
1. nbviewer.js (https://github.com/kokes/nbviewer.js)
|
||||
2. prism (https://github.com/PrismJS/prism)
|
||||
|
||||
|
||||
%% nbviewer.js NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
Copyright (c) 2016 Ondrej Kokes
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
=========================================
|
||||
END OF nbviewer.js NOTICES AND INFORMATION
|
||||
|
||||
%% prism NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2012 Lea Verou
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
=========================================
|
||||
END OF prism NOTICES AND INFORMATION
|
|
@ -0,0 +1,35 @@
|
|||
# Jupyter Notebook Renderer on Visual Studio Team Services Extension Change log
|
||||
|
||||
## [1.2.1] (2018.06.29)
|
||||
* Release the initial preview version of Jupyter Notebook Renderer for VSTS
|
||||
|
||||
## [1.2.9] (2018.10.25)
|
||||
* Minor change to look and feel. Make the extension public.
|
||||
|
||||
## [1.3.0] (2018.11.29)
|
||||
* Fix a dependency bug which causes installation failure on TFS 2017
|
||||
|
||||
## [1.3.1] (2019.03.25)
|
||||
* Change the extensioin scopes from code_write to code, as it only needs to read the ipynb files.
|
||||
* Improve the exception handling. Will show friendly message when fails to render
|
||||
* Support text/markdown rendering
|
||||
|
||||
## [1.3.2] (2019.05.24)
|
||||
* Improve the exception handling by removing all the silent failures code paths during rendering. Now all rendering failures result in friendly and actionable error messages in the rendering panel
|
||||
* Explicitly reject nbformat 3 notebooks.
|
||||
|
||||
## [1.3.3] (2019.06.11)
|
||||
* Improve rendering robustness: when a cell output contains multiple formats, as long as one of the format can be rendered, we don't fail the rendering.
|
||||
|
||||
## [1.4.0] (2019.08.19)
|
||||
* Two rendering bug fixes.
|
||||
1. support rendering cell_type == raw
|
||||
2. validate the output data content type before calling Array.join to avoid javascript type error.
|
||||
|
||||
## [1.5.0] (2019.10.31)
|
||||
* Improve rendering robustness: handle the cases when metadata.kernelspec doesn't exist, or metadata.kernelspec.language doesn't exist, or metadata.language_info doesn't exist. All these cases are allowed as per nbformat 4 spec, therefore we will render the notebook in these scenarios.
|
||||
* Improve error reporting: for malformed notebook or required properties are missing (e.g. nbformat, metadata, metadata.languge_info.name etc), repor the exact error to user to reduce confusion.
|
||||
* Update the contact email address in overview page.
|
||||
|
||||
## [1.5.1] (2020.01.15)
|
||||
* Improve rendering robustness: handle the cases output text or html is just a string, even though according to nbformat, it is expected to be an array.
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 268 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 49 KiB |
|
@ -0,0 +1,76 @@
|
|||
**MICROSOFT SOFTWARE LICENSE TERMS**
|
||||
|
||||
---
|
||||
|
||||
**MICROSOFT Jupyter Notebook Renderer on Visual Studio Team Services Extension**
|
||||
|
||||
---
|
||||
|
||||
These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms.
|
||||
|
||||
**IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW.**
|
||||
|
||||
**1. INSTALLATION AND USE RIGHTS.**
|
||||
|
||||
You may install and use any number of copies of the software.
|
||||
|
||||
**2. TERMS FOR SPECIFIC COMPONENTS.**
|
||||
|
||||
**a. Third Party Components.** The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file(s) accompanying the software. Even if such components are governed by other agreements, the disclaimers and the limitations on and exclusions of damages below also apply.
|
||||
|
||||
**3. DATA.**
|
||||
The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the product documentation. The Microsoft privacy statement is located here [https://go.microsoft.com/fwlink/?LinkId=521839](https://go.microsoft.com/fwlink/?LinkId=521839). You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.
|
||||
|
||||
**4. SCOPE OF LICENSE.** The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not
|
||||
* work around any technical limitations in the software;
|
||||
* reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software except, and only to the extent required by third party licensing terms governing the use of certain open source components that may be included in the software;
|
||||
* remove, minimize, block or modify any notices of Microsoft or its suppliers in the software;
|
||||
* use the software in any way that is against the law; or
|
||||
* share, publish, rent or lease the software, or provide the software as
|
||||
a stand-alone hosted as solution for others to use, or transfer the software or this agreement to any third party.
|
||||
|
||||
**5. EXPORT RESTRICTIONS.** You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit [www.microsoft.com/exporting](http://www.microsoft.com/exporting).
|
||||
|
||||
**6. SUPPORT SERVICES.** Because this software is “as is,” we may not provide support services for it.
|
||||
|
||||
**7. ENTIRE AGREEMENT.** This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services.
|
||||
|
||||
**8. APPLICABLE LAW.** If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply.
|
||||
|
||||
**9. CONSUMER RIGHTS; REGIONAL VARIATIONS.** This agreement describes certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. Separate and apart from your relationship with Microsoft, you may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you:
|
||||
|
||||
**a. Australia.** You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights.
|
||||
|
||||
**b. Canada.** If you acquired this software in Canada, you may stop receiving updates by turning off the automatic update feature, disconnecting your device from the Internet (if and when you re-connect to the Internet, however, the software will resume checking for and installing updates), or uninstalling the software. The product documentation, if any, may also specify how to turn off updates for your specific device or software.
|
||||
|
||||
**c. Germany and Austria.**
|
||||
|
||||
**(i)** **Warranty**. The properly licensed software will perform substantially as described in any Microsoft materials that accompany the software. However, Microsoft gives no contractual guarantee in relation to the licensed software.
|
||||
|
||||
**(ii)** **Limitation of Liability**. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in case of death or personal or physical injury, Microsoft is liable according to the statutory law.
|
||||
|
||||
Subject to the foregoing clause (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence.
|
||||
|
||||
**10.DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS-IS.” YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.**
|
||||
|
||||
**11.LIMITATION ON AND EXCLUSION OF DAMAGES. YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES.**
|
||||
|
||||
This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.
|
||||
|
||||
It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages.
|
||||
|
||||
**Please note: As this software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French.**
|
||||
|
||||
**Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français.**
|
||||
|
||||
**EXONÉRATION DE GARANTIE.** Le logiciel visé par une licence est offert « tel quel ». Toute utilisation de ce logiciel est à votre seule risque et péril. Microsoft n’accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualité marchande, d’adéquation à un usage particulier et d’absence de contrefaçon sont exclues.
|
||||
|
||||
**LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES.** Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices.
|
||||
|
||||
Cette limitation concerne :
|
||||
* tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et
|
||||
* les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur.
|
||||
|
||||
Elle s’applique également, même si Microsoft connaissait ou devrait connaître l’éventualité d’un tel dommage. Si votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l’exclusion ci-dessus ne s’appliquera pas à votre égard.
|
||||
|
||||
**EFFET JURIDIQUE.** Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas.
|
|
@ -0,0 +1,47 @@
|
|||
## Jupyter Notebook Renderer
|
||||
Render Jupyter Notebook inline.
|
||||
|
||||
## How to Use
|
||||
### Install this extension to your Azure DevOps account
|
||||
|
||||
### Choose any Jupyter Notebook (.ipynb) file in your repo. The preview pane will show the rendered content of Jupyter Notebook.
|
||||
|
||||
![Preview Notebook](marketplace/images/Preview.png)
|
||||
|
||||
## Known Issues
|
||||
* Any Jupyter Notebook (*.ipynb) file bigger than 5MB will be truncated and cannot be rendered successfully
|
||||
|
||||
* Jupyter Notebook format lower than 4 (nbformat field in the ipynb file) is not supported. You can use nbconvert tool to convert the older format Jupyter Notebook to nbformat 4 or higher first.
|
||||
|
||||
`
|
||||
jupyter nbconvert --to=notebook --inplace --nbformat=4 <your-notebook-filename>
|
||||
`
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Team Services
|
||||
|
||||
## Contributors
|
||||
|
||||
Microsoft "Cloud AI Infra" team: https://repos.opensource.microsoft.com/Microsoft/teams/cloud-ai-infra
|
||||
|
||||
## Feedback
|
||||
|
||||
We need your feedback! Here are some of the ways to connect with us:
|
||||
|
||||
- Add a review below
|
||||
- Send us an [email](mailto://aml_ado_support@microsoft.com).
|
||||
|
||||
## Important Note
|
||||
Version 1.3.1, published on 3/25/2019, contains a fix which downgrades the required scope from code_write to code. As a result, if you have pervious version of this extension installed, please **authorize** the request of scope change, as shown in the screen shot below. This actually makes it *safer* to run this extension!
|
||||
|
||||
![Authorize Scope Change](marketplace/images/change_scope_auth.png)
|
||||
|
||||
--------
|
||||
|
||||
|
||||
-------
|
||||
[View Notices](marketplace/ThirdPartyNotices.txt) for third party software included in this extension.
|
||||
|
||||
-------
|
||||
> Microsoft DevLabs is an outlet for experiments from Microsoft, experiments that represent some of the latest ideas around developer tools. Solutions in this category are designed for broad usage, and you are encouraged to use and provide feedback on them; however, these extensions are not supported nor are any commitments made as to their longevity.
|
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Notebook Rendering</title>
|
||||
|
||||
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism.min.css'/>
|
||||
<script src='https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.6/marked.min.js'></script>
|
||||
<script src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/prism.min.js' data-manual></script>
|
||||
<script src='https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/components/prism-python.min.js' data-manual></script>
|
||||
|
||||
<script src="lib/VSS.SDK.min.js"></script>
|
||||
<script src="scripts/nbv.js"></script>
|
||||
<script src="scripts/nbrender.js"></script>
|
||||
</head>
|
||||
<body style="overflow-y: scroll">
|
||||
<div id="render-content-display">
|
||||
<link rel="stylesheet" href="css/styles.css">
|
||||
<link rel="stylesheet" href="css/notebook.css">
|
||||
<nav id="menubar" class="navbar navbar-default navbar-fixed-top" data-spy="affix">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="https://marketplace.visualstudio.com/items?itemName=ms-air-aiagility.ipynb-renderer" target="_blank">
|
||||
<img src="images/vsts_extension_logo.png" title="Vote for our app!" height="60">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a class="active" href="http://jupyter.org">JUPYTER</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container container-main">
|
||||
<div id="notebook-display"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "ipynb-renderer",
|
||||
"version": "1.3.1",
|
||||
"description": "Jupyter Notebook Renderer",
|
||||
"main": "nbrender.html",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"Jupyter",
|
||||
"Notebook",
|
||||
"ipynb",
|
||||
"render",
|
||||
"preview"
|
||||
],
|
||||
"author": "ms-air-aiagility",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vss-web-extension-sdk": "^5.141.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
var notebookRenderer = (function () {
|
||||
"use strict";
|
||||
return {
|
||||
renderContent: function(rawContent, options) {
|
||||
|
||||
var render_ux = document.getElementById("notebook-display");
|
||||
|
||||
var validFormedJson = false;
|
||||
var jsonPayload;
|
||||
try
|
||||
{
|
||||
jsonPayload = JSON.parse(rawContent);
|
||||
validFormedJson = true;
|
||||
nbv.render(jsonPayload, render_ux);
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
console.log(err);
|
||||
|
||||
var errMsg;
|
||||
if (validFormedJson)
|
||||
{
|
||||
// rendering failure
|
||||
errMsg = "We cannot render this notebook. The detailed reason is: '" + err + "'. Please check the \"Known Issues\" from <a href='https://marketplace.visualstudio.com/items?itemName=ms-air-aiagility.ipynb-renderer' target='_blank'>Jupyter Notebook Renderer Extension Page</a>. If it's a new issue, please <a href=\"mailto:aml_ado_support@microsoft.com\" target='_blank'>contact us</a> by providing the detailed error message above. We will respond and look for the fix. Thanks for helping us to improve.";
|
||||
}
|
||||
else
|
||||
{
|
||||
// parsing failure
|
||||
errMsg = "Failed to parse the notebook file as JSON. The detailed reason is: '" + err + "'. Please check the \"Known Issues\" from <a href='https://marketplace.visualstudio.com/items?itemName=ms-air-aiagility.ipynb-renderer' target='_blank'>Jupyter Notebook Renderer Extension Page</a>. If it's a new issue, please <a href=\"mailto:aml_ado_support@microsoft.com\" target='_blank'>contact us</a> by providing the detailed error message above. We will respond and look for the fix. Thanks for helping us to improve.";
|
||||
}
|
||||
|
||||
nbv.render_error(errMsg, render_ux);
|
||||
}
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
VSS.init({
|
||||
usePlatformScripts: true,
|
||||
usePlatformStyles: true,
|
||||
explicitNotifyLoaded: true
|
||||
});
|
||||
|
||||
VSS.ready(function () {
|
||||
VSS.register("ipynb_renderer", function (context) {
|
||||
return notebookRenderer;
|
||||
});
|
||||
|
||||
VSS.notifyLoadSucceeded();
|
||||
});
|
|
@ -0,0 +1,449 @@
|
|||
var nbv = (function() {
|
||||
"use strict";
|
||||
|
||||
var d = document;
|
||||
var st = {}; // settings
|
||||
|
||||
function render_ipynb(obj, target, settings) {
|
||||
if (!window.marked || !window.Prism) {
|
||||
var errMsg_NoLib = "Expecting libraries marked.js and Prism.js to be present. Please report the rendering issue at <a href='https://marketplace.visualstudio.com/items?itemName=ms-air-aiagility.ipynb-renderer' target='_blank'>Jupyter Notebook Renderer Extension Page</a>.";
|
||||
throw(errMsg_NoLib);
|
||||
}
|
||||
st = settings || {};
|
||||
|
||||
// validate nbformat property
|
||||
st.nbformat = obj.nbformat;
|
||||
if (typeof st.nbformat == 'undefined')
|
||||
{
|
||||
var errMsg_NoNbformat = "The notebook file doesn't have nbformat property. Please define it. This extension can only render notebook whose nbformat is 4 or above.";
|
||||
throw(errMsg_NoNbformat);
|
||||
}
|
||||
else if (st.nbformat < 4)
|
||||
{
|
||||
var errMsg_LowFormat = "This extension can only render notebook whose nbformat is 4 or above. If you have an older version of notebook, please use \"jupyter nbconvert --to=notebook --inplace --nbformat=4\" to convert it first.";
|
||||
throw(errMsg_LowFormat);
|
||||
}
|
||||
|
||||
// NOTE: nbformat 4 schema is specified at:
|
||||
// https://github.com/jupyter/nbformat/blob/11903688167d21af96c92f7f5bf0634ab51819f1/nbformat/v4/nbformat.v4.schema.json
|
||||
// validate metadata property
|
||||
if (typeof obj.metadata == 'undefined')
|
||||
{
|
||||
var errMsg_NoNbMetadata = "Malformed notebook file: it doesn't have metadata property.";
|
||||
throw(errMsg_NoNbMetadata);
|
||||
}
|
||||
|
||||
// figure out notebook language
|
||||
// first choice: if launage_info exists, use the required property name
|
||||
// second choice: if kernelspec exists, try to use the optional property language
|
||||
// final choice: use python as default language
|
||||
var langInfo = obj.metadata.language_info;
|
||||
if (typeof langInfo != 'undefined')
|
||||
{
|
||||
if (typeof langInfo.name == 'undefined')
|
||||
{
|
||||
var errMsg_NoLanuageInfoName = "Malformed notebook file: it doesn't have required property metadata.language_info.name.";
|
||||
throw(errMsg_NoLanuageInfoName);
|
||||
}
|
||||
|
||||
st.lang = langInfo.name;
|
||||
st.pygments_lexer = langInfo.pygments_lexer || langInfo.name;
|
||||
}
|
||||
else
|
||||
{
|
||||
st.lang = (obj.metadata.kernelspec && obj.metadata.kernelspec.language) || "python";
|
||||
}
|
||||
|
||||
// wipe all inner elements of our target
|
||||
while (target.firstChild) {
|
||||
target.removeChild(target.firstChild);
|
||||
}
|
||||
var t = d.createElement('div');
|
||||
t.setAttribute('id', 'notebook-container');
|
||||
target.appendChild(t);
|
||||
st.target = t;
|
||||
|
||||
// v4 has cells directly in the object, v3 had a list of
|
||||
// worksheets, each with a list of cells
|
||||
var cells = obj.cells || function() {
|
||||
var ret = [];
|
||||
for (var j=0; j < obj.worksheets.length; j++) {
|
||||
ret = ret.concat(obj.worksheets[j].cells);
|
||||
}
|
||||
return ret;
|
||||
}();
|
||||
|
||||
for (var j=0; j < cells.length; j++) {
|
||||
var tc = cells[j];
|
||||
|
||||
var cell = d.createElement('div'); // empty div as a fallback
|
||||
|
||||
switch (tc.cell_type) {
|
||||
case 'code':
|
||||
cell = handle_code(tc);
|
||||
break;
|
||||
case 'markdown':
|
||||
cell = handle_mdown_or_raw(tc, true);
|
||||
break;
|
||||
case 'raw':
|
||||
cell = handle_mdown_or_raw(tc, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw('Unsupported cell type: ' + tc.cell_type);
|
||||
}
|
||||
t.appendChild(cell);
|
||||
}
|
||||
}
|
||||
|
||||
function excount(cell, tin) {
|
||||
var cc = d.createElement('div');
|
||||
cc.setAttribute('class',
|
||||
tin? 'prompt input_prompt': 'prompt output_prompt');
|
||||
cc.innerHTML = (tin ? 'In': 'Out') + ' ' + '[' +
|
||||
((!cell.execution_count && !cell.prompt_number) ? ' ' :
|
||||
(cell.execution_count || cell.prompt_number)) + ']:';
|
||||
return cc;
|
||||
}
|
||||
|
||||
// receives cell, outputs DOM
|
||||
function handle_code(cell) {
|
||||
// ignore the cell which has no source, no output and no execution_count
|
||||
if (!cell.execution_count && (cell.source.length == 0) && (cell.outputs.length == 0)) {
|
||||
console.log('ignore the empty code cell');
|
||||
return d.createElement('div');
|
||||
}
|
||||
|
||||
// container for the code cell
|
||||
var ccDiv = d.createElement('div');
|
||||
ccDiv.setAttribute('class', 'cell border-box-sizing code_cell rendered');
|
||||
|
||||
// container for the input part
|
||||
var inputDiv = d.createElement('div');
|
||||
inputDiv.setAttribute('class', 'input');
|
||||
ccDiv.appendChild(inputDiv);
|
||||
|
||||
// container for execution count
|
||||
inputDiv.appendChild(excount(cell, true));
|
||||
|
||||
// container for inner_cell
|
||||
var innerCellDiv = d.createElement('div');
|
||||
innerCellDiv.setAttribute('class', 'inner_cell');
|
||||
inputDiv.appendChild(innerCellDiv);
|
||||
|
||||
// container for input_area
|
||||
var inputAreaDiv = d.createElement('div');
|
||||
inputAreaDiv.setAttribute('class', 'input_area');
|
||||
innerCellDiv.appendChild(inputAreaDiv);
|
||||
|
||||
// container for the code
|
||||
var codeDiv = d.createElement('div');
|
||||
codeDiv.setAttribute('class', 'highlight hl_' + st.pygments_lexer);
|
||||
inputAreaDiv.appendChild(codeDiv);
|
||||
|
||||
var pre = d.createElement('pre');
|
||||
var code = d.createElement('code');
|
||||
code.setAttribute('class', 'language-' + (!cell.language ? st.lang: cell.language) );
|
||||
// no need to join on '\n' - newlines are in the code already
|
||||
// .source for v4, .input for v3
|
||||
var raw_source = (cell.source || cell.input)
|
||||
code.textContent = get_data_content(raw_source);
|
||||
pre.appendChild(code);
|
||||
codeDiv.appendChild(pre);
|
||||
Prism.highlightElement(code);
|
||||
|
||||
// outputs now
|
||||
|
||||
// container for output wrapper
|
||||
var owDiv = d.createElement('div');
|
||||
owDiv.setAttribute('class', 'output_wrapper');
|
||||
ccDiv.appendChild(owDiv);
|
||||
|
||||
// container for output
|
||||
var outDiv = d.createElement('div');
|
||||
outDiv.setAttribute('class', 'output');
|
||||
owDiv.appendChild(outDiv);
|
||||
|
||||
for (var j=0; j < cell.outputs.length; j++) {
|
||||
// container for output area
|
||||
var outAreaDiv = d.createElement('div');
|
||||
outAreaDiv.setAttribute('class', 'output_area');
|
||||
outDiv.appendChild(outAreaDiv);
|
||||
|
||||
var dt = cell.outputs[j];
|
||||
if (dt.output_type == 'execute_result') {
|
||||
outAreaDiv.appendChild(excount(dt, false));
|
||||
}
|
||||
else {
|
||||
var outPromptDiv = d.createElement('div');
|
||||
outPromptDiv.setAttribute('class', 'prompt');
|
||||
outAreaDiv.appendChild(outPromptDiv);
|
||||
}
|
||||
|
||||
switch (dt.output_type) {
|
||||
case 'execute_result':
|
||||
outAreaDiv.appendChild(handle_cell_output(dt, true));
|
||||
break;
|
||||
case 'stream':
|
||||
outAreaDiv.appendChild(handle_stream_output(dt));
|
||||
break;
|
||||
case 'pyerr': // v3
|
||||
case 'error': // v4
|
||||
outAreaDiv.appendChild(handle_error_cell(dt));
|
||||
break;
|
||||
case 'display_data':
|
||||
if (st.nbformat > 3) {
|
||||
outAreaDiv.appendChild(handle_cell_output(dt, false));
|
||||
break;
|
||||
}
|
||||
// if 3, fall through to pyout
|
||||
case 'pyout':
|
||||
// legacy (v3)
|
||||
outAreaDiv.appendChild(handle_pyout(dt));
|
||||
break;
|
||||
default:
|
||||
throw('Not supported output_type: ' +
|
||||
cell.outputs[j].output_type);
|
||||
}
|
||||
}
|
||||
|
||||
return ccDiv;
|
||||
}
|
||||
|
||||
function handle_cell_output(dt, texe_result) {
|
||||
var el = d.createElement('div');
|
||||
el.setAttribute('class', 'output_text output_subarea' + (texe_result? ' output_execute_result': ''));
|
||||
|
||||
// individual outputs
|
||||
var hasValidFormat = false;
|
||||
var invalidFormat = null;
|
||||
|
||||
var fmts = Object.keys(dt.data);
|
||||
for (var j=0; j < fmts.length; j++) {
|
||||
var fmt = fmts[j];
|
||||
var dm = d.createElement('div');
|
||||
switch (fmt) {
|
||||
case 'text/plain':
|
||||
// text/plain might be just a fallback here
|
||||
// fixed #10
|
||||
if (fmts.includes('text/html') || fmts.includes('text/markdown'))
|
||||
continue;
|
||||
|
||||
dm = d.createElement('pre');
|
||||
dm.style.margin = 0;
|
||||
dm.textContent = get_data_content(dt.data[fmt]);
|
||||
hasValidFormat = true;
|
||||
break;
|
||||
|
||||
case 'text/html':
|
||||
dm.innerHTML = get_data_content(dt.data[fmt]);
|
||||
|
||||
// we may have generated some HTML tables we need to style
|
||||
var dfs = dm.getElementsByClassName('dataframe');
|
||||
for (var k=0; k < dfs.length; k++) {
|
||||
dfs[k].setAttribute('style', [
|
||||
'border-collapse: collapse',
|
||||
'text-align: left'
|
||||
// 'margin-top: 1em'
|
||||
].join(';'));
|
||||
|
||||
// let's style individual cells as well
|
||||
var cl = dfs[k].querySelectorAll('td, th');
|
||||
for (var l=0; l < cl.length; l++) {
|
||||
cl[l].style.padding = '3px';
|
||||
}
|
||||
}
|
||||
|
||||
hasValidFormat = true;
|
||||
break;
|
||||
|
||||
case 'text/markdown':
|
||||
dm.innerHTML = marked(get_data_content(dt.data[fmt]));
|
||||
hasValidFormat = true;
|
||||
break;
|
||||
|
||||
case 'image/svg+xml':
|
||||
dm.innerHTML = get_data_content(dt.data[fmt]);
|
||||
hasValidFormat = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (fmt.startsWith('image/')) {
|
||||
dm = d.createElement('img');
|
||||
dm.setAttribute('src', 'data:' + fmt + ';base64,' + get_data_content(dt.data[fmt]));
|
||||
|
||||
// use width and height attributes supplied in metadata
|
||||
if (fmt in dt.metadata) {
|
||||
var metadata = dt.metadata[fmt];
|
||||
if ('width' in metadata) {
|
||||
dm.setAttribute('width', metadata.width);
|
||||
}
|
||||
if ('height' in metadata) {
|
||||
dm.setAttribute('height', metadata.height);
|
||||
}
|
||||
}
|
||||
|
||||
hasValidFormat = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// this is an unexpected format. Instead of directly throwing the error, we
|
||||
// make it more robust by only throwing the error if this is the ONLY format
|
||||
// for the current cell output
|
||||
invalidFormat = fmt;
|
||||
|
||||
} // end of switch
|
||||
|
||||
el.appendChild(dm);
|
||||
} // end of for loop
|
||||
|
||||
if (!hasValidFormat)
|
||||
{
|
||||
if (invalidFormat)
|
||||
{
|
||||
throw('unexpected format: ' + invalidFormat);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw('missing format for the cell output');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function get_data_content(format_data)
|
||||
{
|
||||
if (Array.isArray(format_data))
|
||||
{
|
||||
return format_data.join('');
|
||||
}
|
||||
else if (typeof format_data == 'string')
|
||||
{
|
||||
return format_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return format_data.toString();
|
||||
}
|
||||
}
|
||||
|
||||
function handle_error_cell(dt) {
|
||||
var el = d.createElement('div');
|
||||
el.setAttribute('class', 'output_subarea output_text output_error');
|
||||
|
||||
var cn = d.createElement('pre');
|
||||
var txt = dt.traceback.join('\n');
|
||||
cn.textContent = txt;
|
||||
|
||||
el.appendChild(cn);
|
||||
return el;
|
||||
}
|
||||
|
||||
function handle_stream_output(dt) {
|
||||
// name in v4, stream in v3
|
||||
var outt = dt.name || dt.stream; // v4 || v3; contains 'stdout' or 'stderr'
|
||||
|
||||
if (!dt.hasOwnProperty('text'))
|
||||
throw('data for stream missing');
|
||||
|
||||
var el = d.createElement('div');
|
||||
el.setAttribute('class',
|
||||
'output_subarea output_stream output_text ' + (outt == 'stderr' ? 'output_stderr': 'output_stdout'));
|
||||
|
||||
var cn = d.createElement('pre');
|
||||
cn.textContent = get_data_content(dt.text);
|
||||
el.appendChild(cn);
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function handle_mdown_or_raw(cell, isMarkdown) {
|
||||
// container for the markdown or raw cell
|
||||
var mdDiv = d.createElement('div');
|
||||
mdDiv.setAttribute('class', 'cell border-box-sizing text_cell rendered');
|
||||
|
||||
// container for the empty input_prompt
|
||||
var inPromptDiv = d.createElement('div');
|
||||
inPromptDiv.setAttribute('class', 'prompt input_prompt');
|
||||
mdDiv.appendChild(inPromptDiv);
|
||||
|
||||
// container for inner cell
|
||||
var innerCellDiv = d.createElement('div');
|
||||
innerCellDiv.setAttribute('class', 'inner_cell');
|
||||
mdDiv.appendChild(innerCellDiv);
|
||||
|
||||
// container for the markdown or raw content
|
||||
var mdcontentDiv = d.createElement('div');
|
||||
mdcontentDiv.setAttribute('class', 'text_cell_render border-box-sizing rendered_html');
|
||||
mdcontentDiv.innerHTML =
|
||||
isMarkdown ? marked(get_data_content(cell.source)) : get_data_content(cell.source);
|
||||
innerCellDiv.appendChild(mdcontentDiv);
|
||||
|
||||
return mdDiv;
|
||||
}
|
||||
|
||||
// handling legacy notebooks (v3)
|
||||
function handle_pyout(cell) {
|
||||
var el = d.createElement('div');
|
||||
|
||||
if (cell.hasOwnProperty('prompt_number'))
|
||||
el.appendChild(excount(cell, false));
|
||||
|
||||
// var ks = Object.keys(cell)
|
||||
// text, png, jpeg, html -- supported
|
||||
// svg, latex, javascript, json, pdf, metadata -- not yet
|
||||
Object.keys(cell).forEach(function(k) {
|
||||
if (['output_type', 'prompt_number'].indexOf(k) > -1)
|
||||
return;
|
||||
var p = d.createElement('span'); // if errs
|
||||
switch (k) {
|
||||
case 'text':
|
||||
p = d.createElement('pre');
|
||||
p.style.margin = 0;
|
||||
p.textContent = get_data_content(cell[k]);
|
||||
break;
|
||||
case 'html':
|
||||
p = d.createElement('div');
|
||||
// guessing here, haven't seen a v3 HTML element
|
||||
p.innerHTML = get_data_content(cell[k]);
|
||||
break;
|
||||
case 'png':
|
||||
case 'jpeg':
|
||||
p = d.createElement('img');
|
||||
p.setAttribute('src', 'data:' + k + ';base64,' + cell[k]);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw('unsupported pyout format: ' + k);
|
||||
}
|
||||
el.appendChild(p);
|
||||
});
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function sanitise(el) {
|
||||
return el.toLowerCase().replace(/\W+/g, '-');
|
||||
}
|
||||
|
||||
function render_error(message, target) {
|
||||
// wipe all inner elements of our target
|
||||
while (target.firstChild) {
|
||||
target.removeChild(target.firstChild);
|
||||
}
|
||||
|
||||
var t = d.createElement('div');
|
||||
t.setAttribute('class', 'alert alert-danger');
|
||||
t.innerHTML = message;
|
||||
target.appendChild(t);
|
||||
}
|
||||
|
||||
return {
|
||||
render: render_ipynb,
|
||||
render_error: render_error
|
||||
};
|
||||
|
||||
})();
|
|
@ -0,0 +1,95 @@
|
|||
{
|
||||
"manifestVersion": 1,
|
||||
"id": "ipynb-renderer",
|
||||
"version": "1.5.2",
|
||||
"name": "Jupyter Notebook Renderer",
|
||||
"description": "Jupyter Notebook Renderer",
|
||||
"publisher": "ms-air-aiagility",
|
||||
"targets": [
|
||||
{
|
||||
"id": "Microsoft.VisualStudio.Services"
|
||||
}
|
||||
],
|
||||
"icons": {
|
||||
"default": "images/logo.png"
|
||||
},
|
||||
"galleryFlags": [
|
||||
"Public"
|
||||
],
|
||||
"contributions": [
|
||||
{
|
||||
"id": "ipynb_renderer",
|
||||
"type": "ms.vss-code-web.content-renderer",
|
||||
"description": "The collection of registered content renderers which each display file content for particular file types",
|
||||
"targets": [
|
||||
"ms.vss-code-web.content-renderer-collection"
|
||||
],
|
||||
"properties": {
|
||||
"uri": "nbrender.html",
|
||||
"fileExtensions": [
|
||||
"ipynb"
|
||||
],
|
||||
"mimeTypes": [
|
||||
"text/html"
|
||||
],
|
||||
"defaultBehavior": "showRenderedContent",
|
||||
"registeredObjectId": "ipynb_renderer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"scopes": [
|
||||
"vso.code"
|
||||
],
|
||||
"categories": [
|
||||
"Code"
|
||||
],
|
||||
"tags": [
|
||||
"Jupyter",
|
||||
"Notebook",
|
||||
"ipynb",
|
||||
"render",
|
||||
"preview"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"uri": "https://github.com/Azure/ipynb-renderer"
|
||||
},
|
||||
"content": {
|
||||
"details": {
|
||||
"path": "marketplace/overview.md"
|
||||
},
|
||||
"changelog": {
|
||||
"path": "marketplace/changelog.md"
|
||||
},
|
||||
"license": {
|
||||
"path": "marketplace/license.md"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
"path": "nbrender.html",
|
||||
"addressable": true
|
||||
},
|
||||
{
|
||||
"path": "css",
|
||||
"addressable": true
|
||||
},
|
||||
{
|
||||
"path": "scripts",
|
||||
"addressable": true
|
||||
},
|
||||
{
|
||||
"path": "node_modules/vss-web-extension-sdk/lib",
|
||||
"addressable": true,
|
||||
"packagePath": "lib"
|
||||
},
|
||||
{
|
||||
"path": "images",
|
||||
"addressable": true
|
||||
},
|
||||
{
|
||||
"path": "marketplace",
|
||||
"addressable": true
|
||||
}
|
||||
]
|
||||
}
|
Загрузка…
Ссылка в новой задаче