diff --git a/package.json b/package.json --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "license": "AGPL-3.0-or-later", "dependencies": { "admin-lte": "^3.0.0-alpha", + "ansi_up": "^4.0.3", "bootstrap": "^4.3.1", "bootstrap-year-calendar-bs4": "^1.0.0", "clipboard": "^2.0.4", @@ -28,6 +29,7 @@ "iframe-resizer": "^4.1.1", "jquery": "^3.4.0", "js-cookie": "^2.2.0", + "notebookjs": "^0.4.2", "object-fit-images": "^3.2.4", "octicons": "^8.5.0", "open-iconic": "^1.1.1", @@ -80,6 +82,7 @@ "robotstxt-webpack-plugin": "^5.0.0", "sass-loader": "^7.1.0", "schema-utils": "^1.0.0", + "script-loader": "^0.7.2", "spdx-expression-parse": "^3.0.0", "style-loader": "^0.23.1", "stylelint": "^10.0.0", diff --git a/swh/web/assets/config/.eslintrc b/swh/web/assets/config/.eslintrc --- a/swh/web/assets/config/.eslintrc +++ b/swh/web/assets/config/.eslintrc @@ -40,7 +40,9 @@ "__STATIC__": false, "Image": false, "Cookies": false, - "grecaptcha": false + "grecaptcha": false, + "nb": false, + "MathJax": false }, "rules": { diff --git a/swh/web/assets/config/bootstrap-pre-customize.scss b/swh/web/assets/config/bootstrap-pre-customize.scss --- a/swh/web/assets/config/bootstrap-pre-customize.scss +++ b/swh/web/assets/config/bootstrap-pre-customize.scss @@ -9,14 +9,14 @@ // global text colors and fonts $body-color: rgba(0, 0, 0, 0.55); -$font-family-sans-serif: "Alegreya Sans", sans-serif; +$font-family-sans-serif: "Alegreya Sans", sans-serif !important; $link-color: rgba(0, 0, 0, 0.75); $code-color: #c7254e; // headings $headings-line-height: 1.1; $headings-color: #e20026; -$headings-font-family: "Alegreya Sans", sans-serif; +$headings-font-family: "Alegreya Sans", sans-serif !important; // remove the ugly box shadow from bootstrap 4.x $input-btn-focus-width: 0; diff --git a/swh/web/assets/config/webpack-plugins/generate-weblabels-webpack-plugin/index.js b/swh/web/assets/config/webpack-plugins/generate-weblabels-webpack-plugin/index.js --- a/swh/web/assets/config/webpack-plugins/generate-weblabels-webpack-plugin/index.js +++ b/swh/web/assets/config/webpack-plugins/generate-weblabels-webpack-plugin/index.js @@ -99,6 +99,12 @@ } } + // remove webpack loader call if any + let loaderEndPos = srcFilePath.indexOf('!'); + if (loaderEndPos !== -1) { + srcFilePath = srcFilePath.slice(loaderEndPos + 1); + } + // iterate on all chunks containing the module mod.chunks.forEach(chunk => { @@ -182,7 +188,11 @@ scriptSrcData['licenses'].forEach(license => { license['copy_url'] = licenseCopyUrl; }); - scriptSrcData['src_url'] = stats.publicPath + path.join(this.weblabelsDirName, scriptSrc['id']); + if (!scriptSrc['path'].startsWith('http:') && !scriptSrc['path'].startsWith('https:')) { + scriptSrcData['src_url'] = stats.publicPath + path.join(this.weblabelsDirName, scriptSrc['id']); + } else { + scriptSrcData['src_url'] = scriptSrc['path']; + } this.chunkJsAssetToSrcFiles[script].push(scriptSrcData); this.copyFileToOutputPath(scriptSrc['path']); } @@ -341,7 +351,9 @@ } copyFileToOutputPath(srcFilePath, ext = '') { - if (this.copiedFiles.has(srcFilePath)) { + if (this.copiedFiles.has(srcFilePath) || + srcFilePath.startsWith('http:') || + srcFilePath.startsWith('https:')) { return; } let destPath = this.cleanupPath(srcFilePath); diff --git a/swh/web/assets/config/webpack.config.development.js b/swh/web/assets/config/webpack.config.development.js --- a/swh/web/assets/config/webpack.config.development.js +++ b/swh/web/assets/config/webpack.config.development.js @@ -378,7 +378,130 @@ 'licenseFilePath': './node_modules/pdfjs-dist/LICENSE' } - ] + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js': [ + { + 'id': 'MathJax.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/MathJax.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/config/TeX-AMS_HTML.js': [ + { + 'id': 'TeX-AMS_HTML.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/config/TeX-AMS_HTML.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/extensions/MathMenu.js': [ + { + 'id': 'MathMenu.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/extensions/MathMenu.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/jax/element/mml/optable/BasicLatin.js': [ + { + 'id': 'BasicLatin.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/jax/element/mml/optable/BasicLatin.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/jax/output/HTML-CSS/jax.js': [ + { + 'id': 'jax.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/jax/output/HTML-CSS/jax.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/jax/output/HTML-CSS/fonts/TeX/fontdata.js': [ + { + 'id': 'fontdata.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/jax/output/HTML-CSS/fonts/TeX/fontdata.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/Main.js': [ + { + 'id': 'Main.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/Main.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/BBBold.js': [ + { + 'id': 'BBBold.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/BBBold.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/GeneralPunctuation.js': [ + { + 'id': 'GeneralPunctuation.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/GeneralPunctuation.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/jax/output/HTML-CSS/autoload/mtable.js': [ + { + 'id': 'mtable.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/jax/output/HTML-CSS/autoload/mtable.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/MiscTechnical.js': [ + { + 'id': 'MiscTechnical.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/MiscTechnical.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/jax/output/HTML-CSS/fonts/TeX/Typewriter/Regular/Main.js': [ + { + 'id': 'Main.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/jax/output/HTML-CSS/fonts/TeX/Typewriter/Regular/Main.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/jax/element/mml/optable/MathOperators.js': [ + { + 'id': 'MathOperators.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/jax/element/mml/optable/MathOperators.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/jax/output/HTML-CSS/autoload/multiline.js': [ + { + 'id': 'multiline.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/jax/output/HTML-CSS/autoload/multiline.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + } + ], + 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/MathOperators.js': [ + { + 'id': 'MathOperators.js', + 'path': 'https://raw.githubusercontent.com/mathjax/MathJax/2.7.5/unpacked/jax/output/HTML-CSS/fonts/TeX/AMS/Regular/MathOperators.js', + 'spdxLicenseExpression': 'Apache-2.0', + 'licenseFilePath': '' + } + ], } }) ], diff --git a/swh/web/assets/src/bundles/webapp/index.js b/swh/web/assets/src/bundles/webapp/index.js --- a/swh/web/assets/src/bundles/webapp/index.js +++ b/swh/web/assets/src/bundles/webapp/index.js @@ -1,5 +1,5 @@ /** - * Copyright (C) 2018 The Software Heritage developers + * Copyright (C) 2018-2019 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information @@ -21,3 +21,5 @@ export * from './code-highlighting'; export * from './readme-rendering'; export * from './pdf-rendering'; +export * from './notebook-rendering'; +export * from './xss-filtering'; diff --git a/swh/web/assets/src/bundles/webapp/notebook-rendering.js b/swh/web/assets/src/bundles/webapp/notebook-rendering.js new file mode 100644 --- /dev/null +++ b/swh/web/assets/src/bundles/webapp/notebook-rendering.js @@ -0,0 +1,152 @@ +/** + * Copyright (C) 2019 The Software Heritage developers + * See the AUTHORS file at the top-level directory of this distribution + * License: GNU Affero General Public License version 3, or any later version + * See top-level LICENSE file for more information + */ + +/* eslint-disable */ + +import 'script-loader!notebookjs'; +import AnsiUp from 'ansi_up'; +import './notebook.css'; + +const ansiup = new AnsiUp(); + +function escapeLaTeX(text) { + + let blockMath = /\$\$(.+?)\$\$|\\\\\[(.+?)\\\\\]/msg; + let inlineMath = /\$(.+?)\$|\\\\\((.+?)\\\\\)/g; + let latexEnvironment = /\\begin\{([a-z]*\*?)\}(.+?)\\end\{\1\}/msg; + + let mathTextFound = []; + let bm; + while ((bm = blockMath.exec(text)) !== null) { + mathTextFound.push(bm[1]); + } + + let im; + while ((im = inlineMath.exec(text)) !== null) { + mathTextFound.push(im[1]); + } + + let le; + while ((le = latexEnvironment.exec(text)) !== null) { + mathTextFound.push(le[1]); + } + + for (let mathText of mathTextFound) { + // showdown will remove line breaks in LaTex array and + // some escaping sequences when converting md to html. + // So we use the following escaping hacks to keep them in the html + // output and avoid MathJax typesetting errors. + let escapedText = mathText.replace('\\\\', '\\\\\\\\'); + for (let specialLaTexChar of ['{', '}', '#', '%', '&', '_']) { + escapedText = escapedText.replace(new RegExp(`\\\\${specialLaTexChar}`, 'g'), + `\\\\${specialLaTexChar}`); + } + + // some html escaping is also needed + escapedText = escapedText.replace(//g, '>'); + + if (mathText !== escapedText) { + text = text.replace(mathText, escapedText); + } + } + + return text; +} + +export async function renderNotebook(nbJsonUrl, domElt) { + + let showdown = await import(/* webpackChunkName: "showdown" */ 'utils/showdown'); + + await import(/* webpackChunkName: "highlightjs" */ 'utils/highlightjs'); + + function renderMarkdown(text) { + let converter = new showdown.Converter({ + tables: true + }); + + // some LaTeX escaping is required to get correct math typesetting + text = escapeLaTeX(text); + + // hack to prevent showdown to replace _ characters + // by html em tags as it will break some math typesetting + // (setting the literalMidWordUnderscores option is not + // enough as iy only works for _ characters contained in words) + text = text.replace(/_/g, '{@}underscore{@}'); + + // render markdown + let rendered = converter.makeHtml(text); + + // restore underscores in rendered HTML + rendered = rendered.replace(/{@}underscore{@}/g, '_'); + + return rendered; + } + + function highlightCode(text, preElt, codeElt, lang) { + if (lang) { + return hljs.highlight(lang, text).value; + } else { + return text; + } + } + + function renderAnsi(text) { + return ansiup.ansi_to_html(text); + } + + nb.markdown = renderMarkdown; + nb.highlighter = highlightCode; + nb.ansi = renderAnsi; + + function initMathJax() { + + // same config as in nbviewer + window.MathJax = { + TeX: { + equationNumbers: { + autoNumber: 'AMS', + useLabelIds: true + } + }, + tex2jax: { + inlineMath: [ ['$', '$'], ['\\(', '\\)'] ], + displayMath: [ ['$$', '$$'], ['\\[', '\\]'] ], + processEscapes: true, + processEnvironments: true + }, + displayAlign: 'center', + 'HTML-CSS': { + styles: {'.MathJax_Display': {'margin': 0}}, + linebreaks: { automatic: true } + } + }; + + // MathJax is not easily webpackable in its current version + // (https://github.com/mathjax/MathJax/issues/1629) + // and is quite a monster regarding the number of files to distribute. + // So we will load it through a CDN for commodity of use here. + let head = document.getElementsByTagName('head')[0]; + let script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS_HTML'; + head.appendChild(script); + } + + fetch(nbJsonUrl) + .then(response => response.json()) + .then(nbJson => { + // parse the notebook + let notebook = nb.parse(nbJson); + // render it to HTML and apply XSS filtering + let rendered = swh.webapp.filterXSS(notebook.render()); + // insert rendered notebook in the DOM + $(domElt).append(rendered); + // load MathJax library for math typesetting + initMathJax(); + }); +} diff --git a/swh/web/assets/src/bundles/webapp/notebook.css b/swh/web/assets/src/bundles/webapp/notebook.css new file mode 100644 --- /dev/null +++ b/swh/web/assets/src/bundles/webapp/notebook.css @@ -0,0 +1,119 @@ +/** + * Copyright (C) 2019 The Software Heritage developers + * See the AUTHORS file at the top-level directory of this distribution + * License: GNU Affero General Public License version 3, or any later version + * See top-level LICENSE file for more information + */ + +.card { + overflow-x: visible; +} + +.nb-notebook { + line-height: 1.5; +} + +.nb-stdout, +.nb-stderr { + white-space: pre-wrap; + margin: 1em 0; + padding: 0.1em 0.5em; +} + +.nb-stderr { + background-color: #faa; +} + +.nb-cell + .nb-cell { + margin-top: 0.5em; +} + +.nb-output table { + border: 1px solid #000; + border-collapse: collapse; +} + +.nb-output th { + font-weight: bold; +} + +.nb-output th, +.nb-output td { + border: 1px solid #000; + padding: 0.25em; + text-align: left; + vertical-align: middle; + border-collapse: collapse; +} + +.nb-cell { + position: relative; +} + +.nb-raw-cell { + white-space: pre-wrap; + background-color: #f5f2f0; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + padding: 1em; + margin: 0.5em 0; +} + +.nb-input { + border: 1px solid #cfcfcf; + border-radius: 2px; + background: #f7f7f7; + margin: 0.4em; + padding: 0; +} + +.nb-notebook pre { + margin: 0.4em !important; + border: none; + padding: 0; + background-color: transparent; +} + +.nb-output { + min-height: 1em; + width: 100%; + overflow-x: auto; + border-right: 1px dotted #ccc; +} + +.nb-output img { + max-width: 100%; +} + +.nb-output::before, +.nb-input::before { + position: absolute; + font-family: monospace; + color: #999; + left: -7.5em; + width: 7em; + text-align: right; + font-size: large; +} + +.nb-input::before { + content: "In [" attr(data-prompt-number) "]:"; + color: #303f9f; +} + +.nb-output::before { + content: "Out [" attr(data-prompt-number) "]:"; + color: #d84315; +} + +.nb-notebook div[style="max-height:1000px;max-width:1500px;overflow:auto;"] { + max-height: none !important; +} + +.nb-latex-output .MathJax_Display { + text-align: left !important; + padding-left: 0.5rem; +} + +.nb-markdown-cell { + margin: 0.4em; +} diff --git a/swh/web/assets/src/bundles/webapp/readme-rendering.js b/swh/web/assets/src/bundles/webapp/readme-rendering.js --- a/swh/web/assets/src/bundles/webapp/readme-rendering.js +++ b/swh/web/assets/src/bundles/webapp/readme-rendering.js @@ -5,23 +5,8 @@ * See top-level LICENSE file for more information */ -import DOMPurify from 'dompurify'; - import {handleFetchError} from 'utils/functions'; -DOMPurify.addHook('uponSanitizeAttribute', function(node, data) { - if (node.nodeName === 'IMG' && data.attrName === 'src') { - // remove leading slash from image src to fix rendering - if (data.attrValue.startsWith('/')) { - data.attrValue = data.attrValue.slice(1); - } - } -}); - -export function filterXSS(html) { - return DOMPurify.sanitize(html); -} - export async function renderMarkdown(domElt, markdownDocUrl) { let showdown = await import(/* webpackChunkName: "showdown" */ 'utils/showdown'); @@ -33,7 +18,7 @@ .then(response => response.text()) .then(data => { $(domElt).addClass('swh-showdown'); - $(domElt).html(filterXSS(converter.makeHtml(data))); + $(domElt).html(swh.webapp.filterXSS(converter.makeHtml(data))); }) .catch(() => { $(domElt).text('Readme bytes are not available'); @@ -50,7 +35,7 @@ let orgDocument = parser.parse(orgDocData, {toc: false}); let orgHTMLDocument = orgDocument.convert(org.ConverterHTML, {}); $(domElt).addClass('swh-org'); - $(domElt).html(filterXSS(orgHTMLDocument.toString())); + $(domElt).html(swh.webapp.filterXSS(orgHTMLDocument.toString())); // remove toc and section numbers to get consistent // with other readme renderings $('.swh-org ul').first().remove(); diff --git a/swh/web/assets/src/bundles/webapp/webapp-utils.js b/swh/web/assets/src/bundles/webapp/webapp-utils.js --- a/swh/web/assets/src/bundles/webapp/webapp-utils.js +++ b/swh/web/assets/src/bundles/webapp/webapp-utils.js @@ -183,3 +183,13 @@ export function isReCaptchaActivated() { return reCaptchaActivated; } + +let browsedSwhObjectMetadata = {}; + +export function setBrowsedSwhObjectMetadata(metadata) { + browsedSwhObjectMetadata = metadata; +} + +export function getBrowsedSwhObjectMetadata() { + return browsedSwhObjectMetadata; +} diff --git a/swh/web/assets/src/bundles/webapp/xss-filtering.js b/swh/web/assets/src/bundles/webapp/xss-filtering.js new file mode 100644 --- /dev/null +++ b/swh/web/assets/src/bundles/webapp/xss-filtering.js @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2019 The Software Heritage developers + * See the AUTHORS file at the top-level directory of this distribution + * License: GNU Affero General Public License version 3, or any later version + * See top-level LICENSE file for more information + */ + +import DOMPurify from 'dompurify'; + +// we register a hook when performing XSS filtering in order to +// possibly replace a relative image url with the one for getting +// the image bytes from the archive content +DOMPurify.addHook('uponSanitizeAttribute', function(node, data) { + if (node.nodeName === 'IMG' && data.attrName === 'src') { + + // image url does not need any processing here + if (data.attrValue.startsWith('data:image') || + data.attrValue.startsWith('http:') || + data.attrValue.startsWith('https:')) { + return; + } + + // get currently browsed swh content object metadata + let swhObjectMetadata = swh.webapp.getBrowsedSwhObjectMetadata(); + + // the swh content is provided without any useful context + // to get the image checksums from the web api + if (!swhObjectMetadata.hasOwnProperty('directory')) { + return; + } + + // reconstruct relative path for image + let filePath = data.attrValue; + if (filePath.startsWith('/')) { + filePath = filePath.slice(1); + } + + // query internal endpoint to possibly get the image data url + let url = Urls.browse_directory_data_url(swhObjectMetadata.directory, + filePath); + fetch(url) + .then(response => response.json()) + .then(fileData => { + // image file can be loaded from the archive content + if (fileData.hasOwnProperty('data_url')) { + // get the image bytes from the archive + $(`img[src="${data.attrValue}"]`).attr('src', fileData['data_url']); + } + }); + } +}); + +export function filterXSS(html) { + return DOMPurify.sanitize(html); +} diff --git a/swh/web/browse/views/directory.py b/swh/web/browse/views/directory.py --- a/swh/web/browse/views/directory.py +++ b/swh/web/browse/views/directory.py @@ -3,8 +3,10 @@ # License: GNU Affero General Public License version 3, or any later version # See top-level LICENSE file for more information +import json -from django.shortcuts import render, redirect +from django.http import HttpResponse +from django.shortcuts import render from django.template.defaultfilters import filesizeformat from swh.web.common import service @@ -35,14 +37,6 @@ try: if path: dir_info = service.lookup_directory_with_path(sha1_git, path) - # some readme files can reference assets reachable from the - # browsed directory, handle that special case in order to - # correctly displayed them - if dir_info and dir_info['type'] == 'file': - file_raw_url = reverse( - 'browse-content-raw', - url_args={'query_string': dir_info['checksums']['sha1']}) - return redirect(file_raw_url) sha1_git = dir_info['target'] dirs, files = get_directory_entries(sha1_git) @@ -159,3 +153,25 @@ 'vault_cooking': vault_cooking, 'show_actions_menu': True, 'swh_ids': swh_ids}) + + +@browse_route(r'directory/data/url/(?P[0-9a-f]+)/(?P.+)/', + view_name='browse-directory-data-url', + checksum_args=['sha1_git']) +def _directory_browse_data_url(request, sha1_git, path): + """ + Internal endpoint returning data url from a specific file path + relative to a root directory. + """ + data = {} + try: + dir_info = service.lookup_directory_with_path(sha1_git, path) + if dir_info['type'] == 'file': + sha1_git = dir_info['target'] + data['data_url'] = reverse( + 'browse-content-raw', + url_args={'query_string': 'sha1_git:%s' % sha1_git} + ) + except Exception: + pass + return HttpResponse(json.dumps(data), content_type='application/json') diff --git a/swh/web/browse/views/revision.py b/swh/web/browse/views/revision.py --- a/swh/web/browse/views/revision.py +++ b/swh/web/browse/views/revision.py @@ -8,7 +8,7 @@ import textwrap from django.http import HttpResponse -from django.shortcuts import render, redirect +from django.shortcuts import render from django.template.defaultfilters import filesizeformat from django.utils.html import escape from django.utils.safestring import mark_safe @@ -266,18 +266,6 @@ """ try: revision = service.lookup_revision(sha1_git) - # some readme files can reference assets reachable from the - # browsed directory, handle that special case in order to - # correctly displayed them - if extra_path: - dir_info = \ - service.lookup_directory_with_path(revision['directory'], - extra_path) - if dir_info and dir_info['type'] == 'file': - file_raw_url = reverse( - 'browse-content-raw', - url_args={'query_string': dir_info['checksums']['sha1']}) - return redirect(file_raw_url) origin_info = None snapshot_context = None origin_type = request.GET.get('origin_type', None) diff --git a/swh/web/browse/views/utils/snapshot_context.py b/swh/web/browse/views/utils/snapshot_context.py --- a/swh/web/browse/views/utils/snapshot_context.py +++ b/swh/web/browse/views/utils/snapshot_context.py @@ -8,7 +8,7 @@ # Its purpose is to factorize code for the views reachable from the # /origin/.* and /snapshot/.* endpoints. -from django.shortcuts import render, redirect +from django.shortcuts import render from django.template.defaultfilters import filesizeformat from swh.model.identifiers import snapshot_identifier @@ -241,14 +241,6 @@ sha1_git = root_sha1_git if root_sha1_git and path: dir_info = service.lookup_directory_with_path(root_sha1_git, path) - # some readme files can reference assets reachable from the - # browsed directory, handle that special case in order to - # correctly displayed them - if dir_info and dir_info['type'] == 'file': - file_raw_url = reverse( - 'browse-content-raw', - url_args={'query_string': dir_info['checksums']['sha1']}) - return redirect(file_raw_url) sha1_git = dir_info['target'] dirs = [] diff --git a/swh/web/config.py b/swh/web/config.py --- a/swh/web/config.py +++ b/swh/web/config.py @@ -37,7 +37,7 @@ 'port': ('int', 5004), 'secret_key': ('string', 'development key'), # do not display code highlighting for content > 1MB - 'content_display_max_size': ('int', 1024 * 1024), + 'content_display_max_size': ('int', 5 * 1024 * 1024), 'snapshot_content_max_size': ('int', 1000), 'throttling': ('dict', { 'cache_uri': None, # production: memcached as cache (127.0.0.1:11211) diff --git a/swh/web/templates/includes/content-display.html b/swh/web/templates/includes/content-display.html --- a/swh/web/templates/includes/content-display.html +++ b/swh/web/templates/includes/content-display.html @@ -21,6 +21,9 @@ Content is too large to be displayed (size is greater than {{ max_content_size|filesizeformat }}). {% elif "inode/x-empty" == mimetype %} File is empty + {% elif swh_object_metadata.filename and swh_object_metadata.filename|default:""|slice:"-5:" == "ipynb" %} +
+
{% elif "text/" in mimetype %}
{{ content }}
@@ -47,6 +50,8 @@ diff --git a/swh/web/tests/browse/views/test_content.py b/swh/web/tests/browse/views/test_content.py --- a/swh/web/tests/browse/views/test_content.py +++ b/swh/web/tests/browse/views/test_content.py @@ -308,7 +308,7 @@ 'blake2s256': ('38702b7168c7785bfe748b51b45d9856070ba90' 'f9dc6d90f2ea75d4356411ffe') }, - 'length': 3000000, + 'length': 30000000, 'raw_data': None, 'mimetype': 'text/plain', 'encoding': 'us-ascii', diff --git a/yarn.lock b/yarn.lock --- a/yarn.lock +++ b/yarn.lock @@ -881,6 +881,11 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== +abab@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f" + integrity sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w== + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -899,12 +904,25 @@ resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948" integrity sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw== +acorn-globals@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.1.tgz#deb149c59276657ebd40ba2ba849ddd529763ccf" + integrity sha512-gJSiKY8dBIjV/0jagZIFBdVMtfQyA5QHCvAT48H2q8REQoW8Fs5AOjqBql1LgSXgrMWdevcE+8cdZ33NtVbIBA== + dependencies: + acorn "^6.0.1" + acorn-walk "^6.0.1" + acorn-jsx@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg== -acorn@^6.0.5, acorn@^6.0.7: +acorn-walk@^6.0.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913" + integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw== + +acorn@^6.0.1, acorn@^6.0.4, acorn@^6.0.5, acorn@^6.0.7: version "6.1.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== @@ -1019,6 +1037,11 @@ dependencies: color-convert "^1.9.0" +ansi_up@>=1.1.3, ansi_up@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/ansi_up/-/ansi_up-4.0.3.tgz#7cc6683304c71f4dcd83725b7e8d313089e369a7" + integrity sha512-ub+xzfB7Mkikx6u0/iHx97Cvs5SJRNjh3yoQpuRaAvC9L2t8ZghlqG6jXIQ7XkbtQyHJU/EP2AnPvwcib0vu6A== + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1067,6 +1090,11 @@ resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= + array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -1165,6 +1193,11 @@ resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI= +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== + async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -1401,6 +1434,11 @@ resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= +browser-process-hrtime@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" + integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== + browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" @@ -2389,6 +2427,18 @@ dependencies: css-tree "1.0.0-alpha.29" +cssom@0.3.x, cssom@^0.3.4: + version "0.3.6" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.6.tgz#f85206cee04efa841f3c5982a74ba96ab20d65ad" + integrity sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A== + +cssstyle@^1.1.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.2.2.tgz#427ea4d585b18624f6fdbf9de7a2a1a3ba713077" + integrity sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow== + dependencies: + cssom "0.3.x" + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -2663,6 +2713,15 @@ dependencies: assert-plus "^1.0.0" +data-urls@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" + integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== + dependencies: + abab "^2.0.0" + whatwg-mimetype "^2.2.0" + whatwg-url "^7.0.0" + datatables.net-bs4@^1.10.16, datatables.net-bs4@^1.10.19: version "1.10.19" resolved "https://registry.yarnpkg.com/datatables.net-bs4/-/datatables.net-bs4-1.10.19.tgz#0608dff22008cf3c7b8a68b1bc702ed255b404fb" @@ -2903,6 +2962,13 @@ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== +domexception@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" + integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== + dependencies: + webidl-conversions "^4.0.2" + domhandler@^2.3.0: version "2.4.2" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" @@ -3103,6 +3169,18 @@ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escodegen@^1.11.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.1.tgz#c485ff8d6b4cdb89e27f4a856e91f118401ca510" + integrity sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw== + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + eslint-import-resolver-node@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" @@ -3254,6 +3332,11 @@ acorn-jsx "^5.0.0" eslint-visitor-keys "^1.0.0" +esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= + esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -3273,7 +3356,7 @@ dependencies: estraverse "^4.1.0" -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= @@ -4152,6 +4235,13 @@ resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== +html-encoding-sniffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" + integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== + dependencies: + whatwg-encoding "^1.0.1" + html-entities@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" @@ -4227,7 +4317,7 @@ resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -iconv-lite@0.4, iconv-lite@^0.4.24, iconv-lite@^0.4.4: +iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -4905,6 +4995,38 @@ resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +jsdom@>=11.6.2: + version "14.0.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-14.0.0.tgz#c7f1441ebcc57902d08d5fb2f6ba2baf746da7c6" + integrity sha512-/VkyPmdtbwqpJSkwDx3YyJ3U1oawYNB/h5z8vTUZGAzjtu2OHTeFRfnJqyMHsJ5Cyes23trOmvUpM1GfHH1leA== + dependencies: + abab "^2.0.0" + acorn "^6.0.4" + acorn-globals "^4.3.0" + array-equal "^1.0.0" + cssom "^0.3.4" + cssstyle "^1.1.1" + data-urls "^1.1.0" + domexception "^1.0.1" + escodegen "^1.11.0" + html-encoding-sniffer "^1.0.2" + nwsapi "^2.0.9" + parse5 "5.1.0" + pn "^1.1.0" + request "^2.88.0" + request-promise-native "^1.0.5" + saxes "^3.1.5" + symbol-tree "^3.2.2" + tough-cookie "^2.5.0" + w3c-hr-time "^1.0.1" + w3c-xmlserializer "^1.0.1" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^7.0.0" + ws "^6.1.2" + xml-name-validator "^3.0.0" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -5166,6 +5288,11 @@ resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" integrity sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ== +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + lodash.tail@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664" @@ -5292,6 +5419,11 @@ resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.2.tgz#c78db948fa879903a41bce522e3b96f801c63786" integrity sha512-NcWuJFHDA8V3wkDgR/j4+gZx+YQwstPgfQDV8ndUeWWzta3dnDTBxpVzqS9lkmJAuV5YX35lmyojl6HO5JXAgw== +marked@>=0.3.3: + version "0.6.2" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.6.2.tgz#c574be8b545a8b48641456ca1dbe0e37b6dccc1a" + integrity sha512-LqxwVH3P/rqKX4EKGz7+c2G9r98WeM/SW34ybhgNGhUQNKtf1GmmSkJ6cDGJ/t6tiyae49qRkpyTw2B9HOrgUA== + mathml-tag-names@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.0.tgz#490b70e062ee24636536e3d9481e333733d00f2c" @@ -5864,6 +5996,15 @@ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== +notebookjs@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/notebookjs/-/notebookjs-0.4.2.tgz#3a89e77afadf9b0b0dbe3aa5a472e3cfa20e052e" + integrity sha512-zgq9nREL6R4JQPEr3Dt7AklUbt/bYqZuI4cs7l3BulSySJtr9ItMi7A8cXDZl5NEc4Ulo/KQX3ienmNCI/6ciw== + dependencies: + ansi_up ">=1.1.3" + jsdom ">=11.6.2" + marked ">=0.3.3" + npm-bundled@^1.0.1: version "1.0.6" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" @@ -5911,6 +6052,11 @@ resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= +nwsapi@^2.0.9: + version "2.1.3" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.1.3.tgz#25f3a5cec26c654f7376df6659cdf84b99df9558" + integrity sha512-RowAaJGEgYXEZfQ7tvvdtAQUKPyTR6T6wNu0fwlNsGQYr/h3yQc6oI8WnVZh3Y/Sylwc+dtAlvPqfFZjhTyk3A== + oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" @@ -6040,7 +6186,7 @@ cssnano "^4.1.0" last-call-webpack-plugin "^3.0.0" -optionator@^0.8.2: +optionator@^0.8.1, optionator@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= @@ -6245,6 +6391,11 @@ resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= +parse5@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" + integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== + parseurl@~1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" @@ -6402,6 +6553,11 @@ dependencies: find-up "^3.0.0" +pn@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" + integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== + popper.js@^1.14.3, popper.js@^1.15.0: version "1.15.0" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.15.0.tgz#5560b99bbad7647e9faa475c6b8056621f5a4ff2" @@ -6918,7 +7074,7 @@ resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.24: +psl@^1.1.24, psl@^1.1.28: version "1.1.31" resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== @@ -6975,7 +7131,7 @@ resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -punycode@^2.1.0: +punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== @@ -7049,6 +7205,11 @@ iconv-lite "0.4.23" unpipe "1.0.0" +raw-loader@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" + integrity sha1-DD0L6u2KAclm2Xh793goElKpeao= + rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -7320,6 +7481,22 @@ resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= +request-promise-core@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346" + integrity sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== + dependencies: + lodash "^4.17.11" + +request-promise-native@^1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.7.tgz#a49868a624bdea5069f1251d0a836e0d89aa2c59" + integrity sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w== + dependencies: + request-promise-core "1.1.2" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + request@^2.83.0, request@^2.87.0, request@^2.88.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" @@ -7548,6 +7725,13 @@ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +saxes@^3.1.5: + version "3.1.9" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.9.tgz#c1c197cd54956d88c09f960254b999e192d7058b" + integrity sha512-FZeKhJglhJHk7eWG5YM0z46VHmI3KJpMBAQm3xa9meDvd+wevB5GuBB0wc0exPInZiBBHqi00DbS8AcvCGCFMw== + dependencies: + xmlchars "^1.3.1" + schema-utils@^0.4.0: version "0.4.7" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" @@ -7565,6 +7749,13 @@ ajv-errors "^1.0.0" ajv-keywords "^3.1.0" +script-loader@^0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/script-loader/-/script-loader-0.7.2.tgz#2016db6f86f25f5cf56da38915d83378bb166ba7" + integrity sha512-UMNLEvgOAQuzK8ji8qIscM3GIrRCWN6MmMXGD4SD5l6cSycgGsCo0tX5xRnfQcoghqct0tjHjcykgI1PyBE2aA== + dependencies: + raw-loader "~0.5.1" + scss-tokenizer@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" @@ -7997,6 +8188,11 @@ dependencies: readable-stream "^2.0.1" +stealthy-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= + stream-browserify@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" @@ -8293,6 +8489,11 @@ unquote "~1.1.1" util.promisify "~1.0.0" +symbol-tree@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" + integrity sha1-rifbOPZgp64uHDt9G8KQgZuFGeY= + table@^5.2.3: version "5.2.3" resolved "https://registry.yarnpkg.com/table/-/table-5.2.3.tgz#cde0cc6eb06751c009efab27e8c820ca5b67b7f2" @@ -8461,6 +8662,14 @@ dependencies: nopt "~1.0.10" +tough-cookie@^2.3.3, tough-cookie@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" @@ -8469,6 +8678,13 @@ psl "^1.1.24" punycode "^1.4.1" +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -8895,6 +9111,22 @@ dependencies: indexof "0.0.1" +w3c-hr-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" + integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= + dependencies: + browser-process-hrtime "^0.1.2" + +w3c-xmlserializer@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" + integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== + dependencies: + domexception "^1.0.1" + webidl-conversions "^4.0.2" + xml-name-validator "^3.0.0" + watchpack@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" @@ -8916,6 +9148,11 @@ dependencies: minimalistic-assert "^1.0.0" +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + webpack-bundle-tracker@^0.4.2-beta: version "0.4.2-beta" resolved "https://registry.yarnpkg.com/webpack-bundle-tracker/-/webpack-bundle-tracker-0.4.2-beta.tgz#263c3a3db8722aaab0083763154e5f025e47825d" @@ -9047,11 +9284,32 @@ resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + whatwg-fetch@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== +whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd" + integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + which-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" @@ -9132,6 +9390,13 @@ dependencies: mkdirp "^0.5.1" +ws@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + dependencies: + async-limiter "~1.0.0" + x-is-string@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" @@ -9142,6 +9407,16 @@ resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xmlchars@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-1.3.1.tgz#1dda035f833dbb4f86a0c28eaa6ca769214793cf" + integrity sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw== + xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"