diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 1ca7809193..c3b2fa83aa 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -34,6 +34,7 @@ expressly granted herein, whether by implication, estoppel or otherwise. jquery-ui: https://github.com/jquery/jquery-ui jquery.event.drag: https://github.com/devongovett/jquery.event.drag jschardet: https://github.com/aadsm/jschardet + JupyterLab: https://github.com/jupyterlab/jupyterlab make-error: https://github.com/JsCommunity/make-error minimist: https://github.com/substack/minimist moment: https://github.com/moment/moment @@ -1166,6 +1167,43 @@ That's all there is to it! ========================================= END OF jschardet NOTICES AND INFORMATION +%% JupyterLab NOTICES AND INFORMATION BEGIN HERE +Copyright (c) 2015 Project Jupyter Contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Semver File License +=================== + +The semver.py file is from https://github.com/podhmo/python-semver +which is licensed under the "MIT" license. See the semver.py file for details. + +END OF JupyterLab NOTICES AND INFORMATION + %% make-error NOTICES AND INFORMATION BEGIN HERE ========================================= ISC © Julien Fontanet diff --git a/build/yarn.lock b/build/yarn.lock index 7e5ce01e6b..c92047d496 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -46,6 +46,7 @@ "@types/minimatch@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/node@*": version "8.0.51" diff --git a/extensions/azurecore/yarn.lock b/extensions/azurecore/yarn.lock index 2ebfde1b74..4483da3cf8 100644 --- a/extensions/azurecore/yarn.lock +++ b/extensions/azurecore/yarn.lock @@ -10,6 +10,7 @@ "@types/node@^8.0.24": version "8.10.36" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.36.tgz#eac05d576fbcd0b4ea3c912dc58c20475c08d9e4" + integrity sha512-SL6KhfM7PTqiFmbCW3eVNwVBZ+88Mrzbuvn9olPsfv43mbiWaFY+nRcz/TGGku0/lc2FepdMbImdMY1JrQ+zbw== "@types/node@^8.0.47": version "8.10.30" @@ -44,14 +45,17 @@ ajv@^5.3.0: ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= asn1@0.1.11: version "0.1.11" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.1.11.tgz#559be18376d08a4ec4dbe80877d27818639b2df7" + integrity sha1-VZvhg3bQik7E2+gId9J4GGObLfc= asn1@~0.2.3: version "0.2.4" @@ -68,6 +72,7 @@ assert-plus@1.0.0, assert-plus@^1.0.0: assert-plus@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.1.5.tgz#ee74009413002d84cec7219c6ac811812e723160" + integrity sha1-7nQAlBMALYTOxyGcasgRgS5yMWA= async@2.6.0: version "2.6.0" @@ -79,6 +84,7 @@ async@2.6.0: async@>=0.6.0, async@^2.0.1: version "2.6.1" resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" + integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== dependencies: lodash "^4.17.10" @@ -90,6 +96,7 @@ asynckit@^0.4.0: aws-sign2@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.5.0.tgz#c57103f7a17fc037f02d7c2e64b602ea223f7d63" + integrity sha1-xXED96F/wDfwLXwuZLYC6iI/fWM= aws-sign2@~0.7.0: version "0.7.0" @@ -132,16 +139,19 @@ bcrypt-pbkdf@^1.0.0: bl@~1.0.0: version "1.0.3" resolved "http://registry.npmjs.org/bl/-/bl-1.0.3.tgz#fc5421a28fd4226036c3b3891a66a25bc64d226e" + integrity sha1-/FQhoo/UImA2w7OJGmaiW8ZNIm4= dependencies: readable-stream "~2.0.5" bluebird@^2.9.30: version "2.11.0" resolved "http://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" + integrity sha1-U0uQM8AiyVecVro7Plpcqvu2UOE= boom@2.x.x: version "2.10.1" resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + integrity sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8= dependencies: hoek "2.x.x" @@ -166,6 +176,7 @@ buffer-equal-constant-time@1.0.1: caseless@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + integrity sha1-cVuW6phBWTzDMGeSP17GDr2k99c= caseless@~0.12.0: version "0.12.0" @@ -175,6 +186,7 @@ caseless@~0.12.0: chalk@^1.0.0: version "1.1.3" resolved "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= dependencies: ansi-styles "^2.2.1" escape-string-regexp "^1.0.2" @@ -202,6 +214,7 @@ combined-stream@1.0.6: combined-stream@^1.0.5, combined-stream@~1.0.1, combined-stream@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" + integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== dependencies: delayed-stream "~1.0.0" @@ -213,6 +226,7 @@ commander@2.15.1: commander@^2.8.1: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== concat-map@0.0.1: version "0.0.1" @@ -222,16 +236,19 @@ concat-map@0.0.1: core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + integrity sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g= dependencies: boom "2.x.x" ctype@0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/ctype/-/ctype-0.5.3.tgz#82c18c2461f74114ef16c135224ad0b9144ca12f" + integrity sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8= dashdash@^1.12.0: version "1.14.1" @@ -285,10 +302,12 @@ ecdsa-sig-formatter@1.0.10: escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= extend@~3.0.0, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== extsprintf@1.3.0: version "1.3.0" @@ -313,10 +332,12 @@ fast-json-stable-stringify@^2.0.0: forever-agent@~0.6.0, forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@~1.0.0-rc1: version "1.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-1.0.1.tgz#ae315db9a4907fa065502304a66d7733475ee37c" + integrity sha1-rjFduaSQf6BlUCMEpm13M0de43w= dependencies: async "^2.0.1" combined-stream "^1.0.5" @@ -339,12 +360,14 @@ fs.realpath@^1.0.0: generate-function@^2.0.0: version "2.3.1" resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" + integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== dependencies: is-property "^1.0.2" generate-object-property@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + integrity sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA= dependencies: is-property "^1.0.0" @@ -380,6 +403,7 @@ har-schema@^2.0.0: har-validator@^1.6.1: version "1.8.0" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-1.8.0.tgz#d83842b0eb4c435960aeb108a067a3aa94c0eeb2" + integrity sha1-2DhCsOtMQ1lgrrEIoGejqpTA7rI= dependencies: bluebird "^2.9.30" chalk "^1.0.0" @@ -397,6 +421,7 @@ har-validator@~5.1.0: has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= dependencies: ansi-regex "^2.0.0" @@ -408,6 +433,7 @@ has-flag@^3.0.0: hawk@~3.1.0: version "3.1.3" resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + integrity sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ= dependencies: boom "2.x.x" cryptiles "2.x.x" @@ -422,10 +448,12 @@ he@1.1.1: hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + integrity sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0= http-signature@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-0.11.0.tgz#1796cf67a001ad5cd6849dca0991485f09089fe6" + integrity sha1-F5bPZ6ABrVzWhJ3KCZFIXwkIn+Y= dependencies: asn1 "0.1.11" assert-plus "^0.1.5" @@ -451,6 +479,7 @@ inflight@^1.0.4: inherits@2, inherits@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= is-buffer@^1.1.6: version "1.1.6" @@ -460,10 +489,12 @@ is-buffer@^1.1.6: is-my-ip-valid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" + integrity sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ== is-my-json-valid@^2.12.0: version "2.19.0" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz#8fd6e40363cd06b963fa877d444bfb5eddc62175" + integrity sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q== dependencies: generate-function "^2.0.0" generate-object-property "^1.1.0" @@ -474,6 +505,7 @@ is-my-json-valid@^2.12.0: is-property@^1.0.0, is-property@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= is-stream@^1.1.0: version "1.1.0" @@ -488,10 +520,12 @@ is-typedarray@~1.0.0: isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isstream@~0.1.1, isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= jsbn@~0.1.0: version "0.1.1" @@ -511,10 +545,12 @@ json-schema@0.2.3: json-stringify-safe@~5.0.0, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= jsonpointer@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + integrity sha1-T9kss04OnbPInIYi7PUfm5eMbLk= jsprim@^1.2.2: version "1.4.1" @@ -556,6 +592,7 @@ mime-db@~1.36.0: mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.2: version "2.1.20" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19" + integrity sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A== dependencies: mime-db "~1.36.0" @@ -634,10 +671,12 @@ ms@2.0.0: node-uuid@~1.4.0: version "1.4.8" resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907" + integrity sha1-sEDrCSOWivq/jTL7HxfxFn/auQc= oauth-sign@~0.8.0: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM= oauth-sign@~0.9.0: version "0.9.0" @@ -669,6 +708,7 @@ postinstall-build@^5.0.1: process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= psl@^1.1.24: version "1.1.29" @@ -683,6 +723,7 @@ punycode@^1.4.1: qs@~5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/qs/-/qs-5.1.0.tgz#4d932e5c7ea411cca76a312d39a606200fd50cd9" + integrity sha1-TZMuXH6kEcynajEtOaYGIA/VDNk= qs@~6.5.2: version "6.5.2" @@ -692,6 +733,7 @@ qs@~6.5.2: readable-stream@~2.0.5: version "2.0.6" resolved "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" + integrity sha1-j5A0HmilPMySh4jaz80Rs265t44= dependencies: core-util-is "~1.0.0" inherits "~2.0.1" @@ -703,6 +745,7 @@ readable-stream@~2.0.5: request@2.63.0: version "2.63.0" resolved "http://registry.npmjs.org/request/-/request-2.63.0.tgz#c83e7c3485e5d9bf9b146318429bc48f1253d8be" + integrity sha1-yD58NIXl2b+bFGMYQpvEjxJT2L4= dependencies: aws-sign2 "~0.5.0" bl "~1.0.0" @@ -807,6 +850,7 @@ should@^13.2.1: sntp@1.x.x: version "1.0.9" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + integrity sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg= dependencies: hoek "2.x.x" @@ -829,14 +873,17 @@ sshpk@^1.7.0: string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= stringstream@~0.0.4: version "0.0.6" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.6.tgz#7880225b0d4ad10e30927d167a1d6f2fd3b33a72" + integrity sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA== strip-ansi@^3.0.0: version "3.0.1" resolved "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" @@ -850,6 +897,7 @@ supports-color@5.4.0: supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= through@^2.3.8: version "2.3.8" @@ -859,6 +907,7 @@ through@^2.3.8: tough-cookie@>=0.12.0, tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== dependencies: psl "^1.1.24" punycode "^1.4.1" @@ -873,6 +922,7 @@ tunnel-agent@^0.6.0: tunnel-agent@~0.4.0: version "0.4.3" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + integrity sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us= tunnel@0.0.5: version "0.0.5" @@ -901,6 +951,7 @@ typemoq@^2.1.0: util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2: version "3.3.2" @@ -934,7 +985,9 @@ wrappy@1: xpath.js@~1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/xpath.js/-/xpath.js-1.1.0.tgz#3816a44ed4bb352091083d002a383dd5104a5ff1" + integrity sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ== xtend@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= diff --git a/extensions/import/yarn.lock b/extensions/import/yarn.lock index 3dc0f8a91b..b1e352edce 100644 --- a/extensions/import/yarn.lock +++ b/extensions/import/yarn.lock @@ -12,6 +12,7 @@ agent-base@4, agent-base@^4.1.0: applicationinsights@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.0.1.tgz#53446b830fe8d5d619eee2a278b31d3d25030927" + integrity sha1-U0Rrgw/o1dYZ7uKieLMdPSUDCSc= dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" @@ -143,10 +144,12 @@ decompress@^4.2.0: diagnostic-channel-publishers@0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" + integrity sha1-ji1geottef6IC1SLxYzGvrKIxPM= diagnostic-channel@0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17" + integrity sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc= dependencies: semver "^5.3.0" @@ -366,6 +369,7 @@ seek-bzip@^1.0.5: semver@^5.3.0: version "5.6.0" resolved "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" + integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== "service-downloader@github:anthonydresser/service-downloader#0.1.5": version "0.1.5" @@ -438,6 +442,7 @@ util-deprecate@~1.0.1: vscode-extension-telemetry@0.0.18: version "0.0.18" resolved "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.18.tgz#602ba20d8c71453aa34533a291e7638f6e5c0327" + integrity sha512-Vw3Sr+dZwl+c6PlsUwrTtCOJkgrmvS3OUVDQGcmpXWAgq9xGq6as0K4pUx+aGqTjzLAESmWSrs6HlJm6J6Khcg== dependencies: applicationinsights "1.0.1" @@ -492,3 +497,4 @@ yauzl@^2.4.2: zone.js@0.7.6: version "0.7.6" resolved "https://registry.npmjs.org/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" + integrity sha1-+7w50+AmHQmG8boGMG6zrrDSIAk= diff --git a/package.json b/package.json index 3abb6216c7..1a05cc19af 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,10 @@ "@angular/router": "~4.1.3", "@angular/upgrade": "~4.1.3", "@types/chart.js": "^2.7.31", + "@types/htmlparser2": "^3.7.31", "angular2-grid": "2.0.6", "angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.4.6", + "ansi_up": "^3.0.0", "applicationinsights": "0.18.0", "chart.js": "^2.6.0", "fast-plist": "0.1.2", @@ -64,6 +66,7 @@ "pretty-data": "^0.40.0", "reflect-metadata": "^0.1.8", "rxjs": "5.4.0", + "sanitize-html": "^1.19.1", "semver": "^5.5.0", "slickgrid": "github:anthonydresser/SlickGrid#2.3.28", "spdlog": "0.7.1", @@ -85,6 +88,7 @@ "@types/keytar": "4.0.1", "@types/minimist": "1.2.0", "@types/mocha": "2.2.39", + "@types/sanitize-html": "^1.18.2", "@types/semver": "5.3.30", "@types/sinon": "1.16.34", "@types/winreg": "^1.2.30", diff --git a/src/sql/parts/notebook/cellViews/code.component.ts b/src/sql/parts/notebook/cellViews/code.component.ts index 462798818c..9b3c270ae7 100644 --- a/src/sql/parts/notebook/cellViews/code.component.ts +++ b/src/sql/parts/notebook/cellViews/code.component.ts @@ -8,8 +8,6 @@ import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetecto import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service'; import { AngularDisposable } from 'sql/base/common/lifecycle'; -import { ComponentBase } from 'sql/parts/modelComponents/componentBase'; -import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces'; import { QueryTextEditor } from 'sql/parts/modelComponents/queryTextEditor'; import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; diff --git a/src/sql/parts/notebook/cellViews/codeCell.component.html b/src/sql/parts/notebook/cellViews/codeCell.component.html index b7f97fed40..1351e5ef2f 100644 --- a/src/sql/parts/notebook/cellViews/codeCell.component.html +++ b/src/sql/parts/notebook/cellViews/codeCell.component.html @@ -8,7 +8,8 @@
-
- Place Holder for output area +
+ +
-
+ \ No newline at end of file diff --git a/src/sql/parts/notebook/cellViews/codeCell.component.ts b/src/sql/parts/notebook/cellViews/codeCell.component.ts index 5e02d19dc7..c15f3cc42d 100644 --- a/src/sql/parts/notebook/cellViews/codeCell.component.ts +++ b/src/sql/parts/notebook/cellViews/codeCell.component.ts @@ -21,10 +21,8 @@ export const CODE_SELECTOR: string = 'code-cell-component'; templateUrl: decodeURI(require.toUrl('./codeCell.component.html')) }) export class CodeCellComponent extends CellView implements OnInit { - @ViewChild('output', { read: ElementRef }) private output: ElementRef; @Input() cellModel: ICellModel; constructor( - @Inject(forwardRef(() => CommonServiceInterface)) private _bootstrapService: CommonServiceInterface, @Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef, @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService ) { @@ -42,7 +40,5 @@ export class CodeCellComponent extends CellView implements OnInit { } private updateTheme(theme: IColorTheme): void { - let outputElement = this.output.nativeElement; - outputElement.style.borderTopColor = theme.getColor(themeColors.SIDE_BAR_BACKGROUND, true).toString(); } } diff --git a/src/sql/parts/notebook/cellViews/interfaces.ts b/src/sql/parts/notebook/cellViews/interfaces.ts index dcf46d6887..1f188b6663 100644 --- a/src/sql/parts/notebook/cellViews/interfaces.ts +++ b/src/sql/parts/notebook/cellViews/interfaces.ts @@ -13,3 +13,5 @@ export abstract class CellView extends AngularDisposable implements OnDestroy { public abstract layout(): void; } + + diff --git a/src/sql/parts/notebook/cellViews/output.component.html b/src/sql/parts/notebook/cellViews/output.component.html new file mode 100644 index 0000000000..f05a51ae65 --- /dev/null +++ b/src/sql/parts/notebook/cellViews/output.component.html @@ -0,0 +1,11 @@ + +
+
+
+
+
diff --git a/src/sql/parts/notebook/cellViews/output.component.ts b/src/sql/parts/notebook/cellViews/output.component.ts new file mode 100644 index 0000000000..c3f8e9d4fe --- /dev/null +++ b/src/sql/parts/notebook/cellViews/output.component.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ +import 'vs/css!./code'; + +import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, Output, EventEmitter } from '@angular/core'; +import { AngularDisposable } from 'sql/base/common/lifecycle'; +import { nb } from 'sqlops'; +import { INotebookService } from 'sql/services/notebook/notebookService'; +import { MimeModel } from 'sql/parts/notebook/outputs/common/mimemodel'; +import * as outputProcessor from '../outputs/common/outputProcessor'; +import { RenderMimeRegistry } from 'sql/parts/notebook/outputs/registry'; +import 'vs/css!sql/parts/notebook/outputs/style/index'; + +export const OUTPUT_SELECTOR: string = 'output-component'; + +@Component({ + selector: OUTPUT_SELECTOR, + templateUrl: decodeURI(require.toUrl('./output.component.html')) +}) +export class OutputComponent extends AngularDisposable implements OnInit { + @ViewChild('output', { read: ElementRef }) private outputElement: ElementRef; + @Input() cellOutput: nb.ICellOutput; + private readonly _minimumHeight = 30; + registry: RenderMimeRegistry; + trusted: boolean = false; + + + constructor( + @Inject(INotebookService) private _notebookService: INotebookService + ) { + super(); + this.registry = _notebookService.getMimeRegistry(); + } + + ngOnInit() { + let node = this.outputElement.nativeElement; + let output = this.cellOutput; + let options = outputProcessor.getBundleOptions({ value: output, trusted: this.trusted }); + // TODO handle safe/unsafe mapping + this.createRenderedMimetype(options, node); + } + + public layout(): void { + } + + protected createRenderedMimetype(options: MimeModel.IOptions, node: HTMLElement): void { + let mimeType = this.registry.preferredMimeType( + options.data, + options.trusted ? 'any' : 'ensure' + ); + if (mimeType) { + let output = this.registry.createRenderer(mimeType); + output.node = node; + let model = new MimeModel(options); + output.renderModel(model).catch(error => { + // Manually append error message to output + output.node.innerHTML = `
Javascript Error: ${error.message}
`; + // Remove mime-type-specific CSS classes + output.node.className = 'p-Widget jp-RenderedText'; + output.node.setAttribute( + 'data-mime-type', + 'application/vnd.jupyter.stderr' + ); + }); + //this.setState({ node: node }); + } else { + // TODO Localize + node.innerHTML = + `No ${options.trusted ? '' : '(safe) '}renderer could be ` + + 'found for output. It has the following MIME types: ' + + Object.keys(options.data).join(', '); + //this.setState({ node: node }); + } + } +} diff --git a/src/sql/parts/notebook/cellViews/outputArea.component.html b/src/sql/parts/notebook/cellViews/outputArea.component.html new file mode 100644 index 0000000000..15c0fe1f71 --- /dev/null +++ b/src/sql/parts/notebook/cellViews/outputArea.component.html @@ -0,0 +1,12 @@ + +
+
+ + +
+
\ No newline at end of file diff --git a/src/sql/parts/notebook/cellViews/outputArea.component.ts b/src/sql/parts/notebook/cellViews/outputArea.component.ts new file mode 100644 index 0000000000..461de0da21 --- /dev/null +++ b/src/sql/parts/notebook/cellViews/outputArea.component.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the Source EULA. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ +import 'vs/css!./code'; +import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, Output, EventEmitter } from '@angular/core'; +import { AngularDisposable } from 'sql/base/common/lifecycle'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ICellModel } from 'sql/parts/notebook/models/modelInterfaces'; + +export const OUTPUT_AREA_SELECTOR: string = 'output-area-component'; + +@Component({ + selector: OUTPUT_AREA_SELECTOR, + templateUrl: decodeURI(require.toUrl('./outputArea.component.html')) +}) +export class OutputAreaComponent extends AngularDisposable implements OnInit { + @Input() cellModel: ICellModel; + + private readonly _minimumHeight = 30; + + constructor( + @Inject(IModeService) private _modeService: IModeService + ) { + super(); + } + ngOnInit(): void { + + } +} diff --git a/src/sql/parts/notebook/models/modelInterfaces.ts b/src/sql/parts/notebook/models/modelInterfaces.ts index ddd75c4179..50caeb3e76 100644 --- a/src/sql/parts/notebook/models/modelInterfaces.ts +++ b/src/sql/parts/notebook/models/modelInterfaces.ts @@ -338,6 +338,7 @@ export interface ICellModel { cellType: CellType; trustedMode: boolean; active: boolean; + readonly outputs: ReadonlyArray; equals(cellModel: ICellModel): boolean; toJSON(): nb.ICell; } diff --git a/src/sql/parts/notebook/notebook.module.ts b/src/sql/parts/notebook/notebook.module.ts index 2c5964b99d..d60a84edbe 100644 --- a/src/sql/parts/notebook/notebook.module.ts +++ b/src/sql/parts/notebook/notebook.module.ts @@ -26,6 +26,8 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { CodeComponent } from 'sql/parts/notebook/cellViews/code.component'; import { CodeCellComponent } from 'sql/parts/notebook/cellViews/codeCell.component'; import { TextCellComponent } from 'sql/parts/notebook/cellViews/textCell.component'; +import { OutputAreaComponent } from 'sql/parts/notebook/cellViews/outputArea.component'; +import { OutputComponent } from 'sql/parts/notebook/cellViews/output.component'; import LoadingSpinner from 'sql/parts/modelComponents/loadingSpinner.component'; export const NotebookModule = (params, selector: string, instantiationService: IInstantiationService): any => { @@ -40,7 +42,9 @@ export const NotebookModule = (params, selector: string, instantiationService: I CodeCellComponent, TextCellComponent, NotebookComponent, - ComponentHostDirective + ComponentHostDirective, + OutputAreaComponent, + OutputComponent ], entryComponents: [NotebookComponent], imports: [ diff --git a/src/sql/parts/notebook/outputs/common/jsonext.ts b/src/sql/parts/notebook/outputs/common/jsonext.ts new file mode 100644 index 0000000000..95de8717f1 --- /dev/null +++ b/src/sql/parts/notebook/outputs/common/jsonext.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +/** + * A type alias for a JSON primitive. + */ +export declare type JSONPrimitive = boolean | number | string | null; +/** + * A type alias for a JSON value. + */ +export declare type JSONValue = JSONPrimitive | JSONObject | JSONArray; +/** + * A type definition for a JSON object. + */ +export interface JSONObject { + [key: string]: JSONValue; +} +/** + * A type definition for a JSON array. + */ +export interface JSONArray extends Array { +} +/** + * A type definition for a readonly JSON object. + */ +export interface ReadonlyJSONObject { + readonly [key: string]: ReadonlyJSONValue; +} +/** + * A type definition for a readonly JSON array. + */ +export interface ReadonlyJSONArray extends ReadonlyArray { +} +/** + * A type alias for a readonly JSON value. + */ +export declare type ReadonlyJSONValue = JSONPrimitive | ReadonlyJSONObject | ReadonlyJSONArray; +/** + * Test whether a JSON value is a primitive. + * + * @param value - The JSON value of interest. + * + * @returns `true` if the value is a primitive,`false` otherwise. + */ +export function isPrimitive(value: any): boolean { + return ( + value === null || + typeof value === 'boolean' || + typeof value === 'number' || + typeof value === 'string' + ); +} diff --git a/src/sql/parts/notebook/outputs/common/mimemodel.ts b/src/sql/parts/notebook/outputs/common/mimemodel.ts new file mode 100644 index 0000000000..3b88c489d7 --- /dev/null +++ b/src/sql/parts/notebook/outputs/common/mimemodel.ts @@ -0,0 +1,87 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ +import { IRenderMime } from './renderMimeInterfaces'; +import { ReadonlyJSONObject } from './jsonext'; + +/** + * The default mime model implementation. + */ +export class MimeModel implements IRenderMime.IMimeModel { + /** + * Construct a new mime model. + */ + constructor(options: MimeModel.IOptions = {}) { + this.trusted = !!options.trusted; + this._data = options.data || {}; + this._metadata = options.metadata || {}; + this._callback = options.callback; + } + + /** + * Whether the model is trusted. + */ + readonly trusted: boolean; + + /** + * The data associated with the model. + */ + get data(): ReadonlyJSONObject { + return this._data; + } + + /** + * The metadata associated with the model. + */ + get metadata(): ReadonlyJSONObject { + return this._metadata; + } + + /** + * Set the data associated with the model. + * + * #### Notes + * Depending on the implementation of the mime model, + * this call may or may not have deferred effects, + */ + setData(options: IRenderMime.ISetDataOptions): void { + this._data = options.data || this._data; + this._metadata = options.metadata || this._metadata; + this._callback(options); + } + + private _callback: (options: IRenderMime.ISetDataOptions) => void; + private _data: ReadonlyJSONObject; + private _metadata: ReadonlyJSONObject; +} + +/** + * The namespace for MimeModel class statics. + */ +export namespace MimeModel { + /** + * The options used to create a mime model. + */ + export interface IOptions { + /** + * Whether the model is trusted. Defaults to `false`. + */ + trusted?: boolean; + + /** + * A callback function for when the data changes. + */ + callback?: (options: IRenderMime.ISetDataOptions) => void; + + /** + * The initial mime data. + */ + data?: ReadonlyJSONObject; + + /** + * The initial mime metadata. + */ + metadata?: ReadonlyJSONObject; + } +} \ No newline at end of file diff --git a/src/sql/parts/notebook/outputs/common/nbformat.ts b/src/sql/parts/notebook/outputs/common/nbformat.ts new file mode 100644 index 0000000000..12ebc816cb --- /dev/null +++ b/src/sql/parts/notebook/outputs/common/nbformat.ts @@ -0,0 +1,494 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +// Notebook format interfaces +// https://nbformat.readthedocs.io/en/latest/format_description.html +// https://github.com/jupyter/nbformat/blob/master/nbformat/v4/nbformat.v4.schema.json + + +import { JSONObject } from './jsonext'; +import { nb } from 'sqlops'; + +/** + * A namespace for nbformat interfaces. + */ +export namespace nbformat { + /** + * The major version of the notebook format. + */ + export const MAJOR_VERSION: number = 4; + + /** + * The minor version of the notebook format. + */ + export const MINOR_VERSION: number = 2; + + /** + * The kernelspec metadata. + */ + export interface IKernelspecMetadata extends JSONObject { + name: string; + display_name: string; + } + + /** + * The language info metatda + */ + export interface ILanguageInfoMetadata extends JSONObject { + name: string; + codemirror_mode?: string | JSONObject; + file_extension?: string; + mimetype?: string; + pygments_lexer?: string; + } + + /** + * The default metadata for the notebook. + */ + export interface INotebookMetadata extends JSONObject { + kernelspec?: IKernelspecMetadata; + language_info?: ILanguageInfoMetadata; + orig_nbformat: number; + } + + /** + * The notebook content. + */ + export interface INotebookContent { + metadata: INotebookMetadata; + nbformat_minor: number; + nbformat: number; + cells: ICell[]; + } + + /** + * A multiline string. + */ + export type MultilineString = string | string[]; + + /** + * A mime-type keyed dictionary of data. + */ + export interface IMimeBundle extends JSONObject { + [key: string]: MultilineString | JSONObject; + } + + /** + * Media attachments (e.g. inline images). + */ + export interface IAttachments { + [key: string]: IMimeBundle; + } + + /** + * The code cell's prompt number. Will be null if the cell has not been run. + */ + export type ExecutionCount = number | null; + + /** + * Cell output metadata. + */ + export type OutputMetadata = JSONObject; + + /** + * Validate a mime type/value pair. + * + * @param type - The mimetype name. + * + * @param value - The value associated with the type. + * + * @returns Whether the type/value pair are valid. + */ + export function validateMimeValue( + type: string, + value: MultilineString | JSONObject + ): boolean { + // Check if "application/json" or "application/foo+json" + const jsonTest = /^application\/(.*?)+\+json$/; + const isJSONType = type === 'application/json' || jsonTest.test(type); + + let isString = (x: any) => { + return Object.prototype.toString.call(x) === '[object String]'; + }; + + // If it is an array, make sure if is not a JSON type and it is an + // array of strings. + if (Array.isArray(value)) { + if (isJSONType) { + return false; + } + let valid = true; + (value as string[]).forEach(v => { + if (!isString(v)) { + valid = false; + } + }); + return valid; + } + + // If it is a string, make sure we are not a JSON type. + if (isString(value)) { + return !isJSONType; + } + + // It is not a string, make sure it is a JSON type. + if (!isJSONType) { + return false; + } + + // It is a JSON type, make sure it is a valid JSON object. + // return JSONExt.isObject(value); + return true; + } + + /** + * Cell-level metadata. + */ + export interface IBaseCellMetadata extends JSONObject { + /** + * Whether the cell is trusted. + * + * #### Notes + * This is not strictly part of the nbformat spec, but it is added by + * the contents manager. + * + * See https://jupyter-notebook.readthedocs.io/en/latest/security.html. + */ + trusted: boolean; + + /** + * The cell's name. If present, must be a non-empty string. + */ + name: string; + + /** + * The cell's tags. Tags must be unique, and must not contain commas. + */ + tags: string[]; + } + + /** + * The base cell interface. + */ + export interface IBaseCell { + /** + * String identifying the type of cell. + */ + cell_type: string; + + /** + * Contents of the cell, represented as an array of lines. + */ + source: MultilineString; + + /** + * Cell-level metadata. + */ + metadata: Partial; + } + + /** + * Metadata for the raw cell. + */ + export interface IRawCellMetadata extends IBaseCellMetadata { + /** + * Raw cell metadata format for nbconvert. + */ + format: string; + } + + /** + * A raw cell. + */ + export interface IRawCell extends IBaseCell { + /** + * String identifying the type of cell. + */ + cell_type: 'raw'; + + /** + * Cell-level metadata. + */ + metadata: Partial; + + /** + * Cell attachments. + */ + attachments?: IAttachments; + } + + /** + * A markdown cell. + */ + export interface IMarkdownCell extends IBaseCell { + /** + * String identifying the type of cell. + */ + cell_type: 'markdown'; + + /** + * Cell attachments. + */ + attachments?: IAttachments; + } + + /** + * Metadata for a code cell. + */ + export interface ICodeCellMetadata extends IBaseCellMetadata { + /** + * Whether the cell is collapsed/expanded. + */ + collapsed: boolean; + + /** + * Whether the cell's output is scrolled, unscrolled, or autoscrolled. + */ + scrolled: boolean | 'auto'; + } + + /** + * A code cell. + */ + export interface ICodeCell extends IBaseCell { + /** + * String identifying the type of cell. + */ + cell_type: 'code'; + + /** + * Cell-level metadata. + */ + metadata: Partial; + + /** + * Execution, display, or stream outputs. + */ + outputs: IOutput[]; + + /** + * The code cell's prompt number. Will be null if the cell has not been run. + */ + execution_count: ExecutionCount; + } + + /** + * An unrecognized cell. + */ + export interface IUnrecognizedCell extends IBaseCell { } + + /** + * A cell union type. + */ + export type ICell = IRawCell | IMarkdownCell | ICodeCell | IUnrecognizedCell; + + /** + * Test whether a cell is a raw cell. + */ + export function isRaw(cell: ICell): cell is IRawCell { + return cell.cell_type === 'raw'; + } + + /** + * Test whether a cell is a markdown cell. + */ + export function isMarkdown(cell: ICell): cell is IMarkdownCell { + return cell.cell_type === 'markdown'; + } + + /** + * Test whether a cell is a code cell. + */ + export function isCode(cell: ICell): cell is ICodeCell { + return cell.cell_type === 'code'; + } + + /** + * A union metadata type. + */ + export type ICellMetadata = + | IBaseCellMetadata + | IRawCellMetadata + | ICodeCellMetadata; + + /** + * The valid output types. + */ + export type OutputType = + | 'execute_result' + | 'display_data' + | 'stream' + | 'error' + | 'update_display_data'; + + + /** + * Result of executing a code cell. + */ + export interface IExecuteResult extends nb.ICellOutput { + /** + * Type of cell output. + */ + output_type: 'execute_result'; + + /** + * A result's prompt number. + */ + execution_count: ExecutionCount; + + /** + * A mime-type keyed dictionary of data. + */ + data: IMimeBundle; + + /** + * Cell output metadata. + */ + metadata: OutputMetadata; + } + + /** + * Data displayed as a result of code cell execution. + */ + export interface IDisplayData extends nb.ICellOutput { + /** + * Type of cell output. + */ + output_type: 'display_data'; + + /** + * A mime-type keyed dictionary of data. + */ + data: IMimeBundle; + + /** + * Cell output metadata. + */ + metadata: OutputMetadata; + } + + /** + * Data displayed as an update to existing display data. + */ + export interface IDisplayUpdate extends nb.ICellOutput { + /** + * Type of cell output. + */ + output_type: 'update_display_data'; + + /** + * A mime-type keyed dictionary of data. + */ + data: IMimeBundle; + + /** + * Cell output metadata. + */ + metadata: OutputMetadata; + } + + /** + * Stream output from a code cell. + */ + export interface IStream extends nb.ICellOutput { + /** + * Type of cell output. + */ + output_type: 'stream'; + + /** + * The name of the stream. + */ + name: StreamType; + + /** + * The stream's text output. + */ + text: MultilineString; + } + + /** + * An alias for a stream type. + */ + export type StreamType = 'stdout' | 'stderr'; + + /** + * Output of an error that occurred during code cell execution. + */ + export interface IError extends nb.ICellOutput { + /** + * Type of cell output. + */ + output_type: 'error'; + + /** + * The name of the error. + */ + ename: string; + + /** + * The value, or message, of the error. + */ + evalue: string; + + /** + * The error's traceback. + */ + traceback: string[]; + } + + /** + * Unrecognized output. + */ + export interface IUnrecognizedOutput extends nb.ICellOutput { } + + /** + * Test whether an output is an execute result. + */ + export function isExecuteResult(output: IOutput): output is IExecuteResult { + return output.output_type === 'execute_result'; + } + + /** + * Test whether an output is from display data. + */ + export function isDisplayData(output: IOutput): output is IDisplayData { + return output.output_type === 'display_data'; + } + + /** + * Test whether an output is from updated display data. + */ + export function isDisplayUpdate(output: IOutput): output is IDisplayUpdate { + return output.output_type === 'update_display_data'; + } + + /** + * Test whether an output is from a stream. + */ + export function isStream(output: IOutput): output is IStream { + return output.output_type === 'stream'; + } + + /** + * Test whether an output is from a stream. + */ + export function isError(output: IOutput): output is IError { + return output.output_type === 'error'; + } + + /** + * An output union type. + */ + export type IOutput = + | IUnrecognizedOutput + | IExecuteResult + | IDisplayData + | IStream + | IError; +} + +export interface ICellOutputWithIdAndTrust extends nb.ICellOutput { + id: number; + trusted: boolean; +} diff --git a/src/sql/parts/notebook/outputs/common/outputProcessor.ts b/src/sql/parts/notebook/outputs/common/outputProcessor.ts new file mode 100644 index 0000000000..11ad262d25 --- /dev/null +++ b/src/sql/parts/notebook/outputs/common/outputProcessor.ts @@ -0,0 +1,110 @@ + +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ + +import { JSONObject } from './jsonext'; +import { MimeModel } from './mimemodel'; +import * as JSONExt from './jsonext'; +import { nbformat } from './nbformat'; +import { nb } from 'sqlops'; + +/** + * A multiline string. + */ +export type MultilineString = string | string[]; + +/** + * A mime-type keyed dictionary of data. + */ +export interface IMimeBundle extends JSONObject { + [key: string]: MultilineString | JSONObject; +} + +/** + * Get the data from a notebook output. + */ +export function getData(output: nb.ICellOutput): JSONObject { + let bundle: IMimeBundle = {}; + if ( + nbformat.isExecuteResult(output) || + nbformat.isDisplayData(output) || + nbformat.isDisplayUpdate(output) + ) { + bundle = (output as nbformat.IExecuteResult).data; + } else if (nbformat.isStream(output)) { + if (output.name === 'stderr') { + bundle['application/vnd.jupyter.stderr'] = output.text; + } else { + bundle['application/vnd.jupyter.stdout'] = output.text; + } + } else if (nbformat.isError(output)) { + let traceback = output.traceback.join('\n'); + bundle['application/vnd.jupyter.stderr'] = + traceback || `${output.ename}: ${output.evalue}`; + } + return convertBundle(bundle); +} + +/** + * Get the metadata from an output message. + */ +export function getMetadata(output: nbformat.IOutput): JSONObject { + let value: JSONObject = Object.create(null); + if (nbformat.isExecuteResult(output) || nbformat.isDisplayData(output)) { + for (let key in output.metadata) { + value[key] = extract(output.metadata, key); + } + } + return value; +} + +/** + * Get the bundle options given output model options. + */ +export function getBundleOptions( + options: IOutputModelOptions +): MimeModel.IOptions { + let data = getData(options.value); + let metadata = getMetadata(options.value); + let trusted = !!options.trusted; + return { data, metadata, trusted }; +} + +/** + * Extract a value from a JSONObject. + */ +export function extract(value: JSONObject, key: string): {} { + let item = value[key]; + if (JSONExt.isPrimitive(item)) { + return item; + } + return JSON.parse(JSON.stringify(item)); +} + +/** + * Convert a mime bundle to mime data. + */ +function convertBundle(bundle: nbformat.IMimeBundle): JSONObject { + let map: JSONObject = Object.create(null); + for (let mimeType in bundle) { + map[mimeType] = extract(bundle, mimeType); + } + return map; +} + +/** + * The options used to create a notebook output model. + */ +export interface IOutputModelOptions { + /** + * The raw output value. + */ + value: nbformat.IOutput; + + /** + * Whether the output is trusted. The default is false. + */ + trusted?: boolean; +} \ No newline at end of file diff --git a/src/sql/parts/notebook/outputs/common/renderMimeInterfaces.ts b/src/sql/parts/notebook/outputs/common/renderMimeInterfaces.ts new file mode 100644 index 0000000000..3585a0d112 --- /dev/null +++ b/src/sql/parts/notebook/outputs/common/renderMimeInterfaces.ts @@ -0,0 +1,360 @@ + +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ +import { ReadonlyJSONObject } from './jsonext'; + +/** + * A namespace for rendermime associated interfaces. + */ +export namespace IRenderMime { + /** + * A model for mime data. + */ + export interface IMimeModel { + /** + * Whether the data in the model is trusted. + */ + readonly trusted: boolean; + + /** + * The data associated with the model. + */ + readonly data: ReadonlyJSONObject; + + /** + * The metadata associated with the model. + */ + readonly metadata: ReadonlyJSONObject; + + /** + * Set the data associated with the model. + * + * #### Notes + * Calling this function may trigger an asynchronous operation + * that could cause the renderer to be rendered with a new model + * containing the new data. + */ + setData(options: ISetDataOptions): void; + } + + /** + * The options used to update a mime model. + */ + export interface ISetDataOptions { + /** + * The new data object. + */ + data?: ReadonlyJSONObject; + + /** + * The new metadata object. + */ + metadata?: ReadonlyJSONObject; + } + + + /** + * The options used to initialize a document widget factory. + * + * This interface is intended to be used by mime renderer extensions + * to define a document opener that uses its renderer factory. + */ + export interface IDocumentWidgetFactoryOptions { + /** + * The name of the widget to display in dialogs. + */ + readonly name: string; + + /** + * The name of the document model type. + */ + readonly modelName?: string; + + /** + * The primary file type of the widget. + */ + readonly primaryFileType: string; + + /** + * The file types the widget can view. + */ + readonly fileTypes: ReadonlyArray; + + /** + * The file types for which the factory should be the default. + */ + readonly defaultFor?: ReadonlyArray; + + /** + * The file types for which the factory should be the default for rendering, + * if that is different than the default factory (which may be for editing) + * If undefined, then it will fall back on the default file type. + */ + readonly defaultRendered?: ReadonlyArray; + } + + /** + * A file type to associate with the renderer. + */ + export interface IFileType { + /** + * The name of the file type. + */ + readonly name: string; + + /** + * The mime types associated the file type. + */ + readonly mimeTypes: ReadonlyArray; + + /** + * The extensions of the file type (e.g. `".txt"`). Can be a compound + * extension (e.g. `".table.json`). + */ + readonly extensions: ReadonlyArray; + + /** + * An optional display name for the file type. + */ + readonly displayName?: string; + + /** + * An optional pattern for a file name (e.g. `^Dockerfile$`). + */ + readonly pattern?: string; + + /** + * The icon class name for the file type. + */ + readonly iconClass?: string; + + /** + * The icon label for the file type. + */ + readonly iconLabel?: string; + + /** + * The file format for the file type ('text', 'base64', or 'json'). + */ + readonly fileFormat?: string; + } + + /** + * An interface for using a RenderMime.IRenderer for output and read-only documents. + */ + export interface IExtension { + /** + * The ID of the extension. + * + * #### Notes + * The convention for extension IDs in JupyterLab is the full NPM package + * name followed by a colon and a unique string token, e.g. + * `'@jupyterlab/apputils-extension:settings'` or `'foo-extension:bar'`. + */ + readonly id: string; + + /** + * A renderer factory to be registered to render the MIME type. + */ + readonly rendererFactory: IRendererFactory; + + /** + * The rank passed to `RenderMime.addFactory`. If not given, + * defaults to the `defaultRank` of the factory. + */ + readonly rank?: number; + + /** + * The timeout after user activity to re-render the data. + */ + readonly renderTimeout?: number; + + /** + * Preferred data type from the model. Defaults to `string`. + */ + readonly dataType?: 'string' | 'json'; + + /** + * The options used to open a document with the renderer factory. + */ + readonly documentWidgetFactoryOptions?: + | IDocumentWidgetFactoryOptions + | ReadonlyArray; + + /** + * The optional file type associated with the extension. + */ + readonly fileTypes?: ReadonlyArray; + } + + /** + * The interface for a module that exports an extension or extensions as + * the default value. + */ + export interface IExtensionModule { + /** + * The default export. + */ + readonly default: IExtension | ReadonlyArray; + } + + /** + * A widget which displays the contents of a mime model. + */ + export interface IRenderer { + /** + * Render a mime model. + * + * @param model - The mime model to render. + * + * @returns A promise which resolves when rendering is complete. + * + * #### Notes + * This method may be called multiple times during the lifetime + * of the widget to update it if and when new data is available. + */ + renderModel(model: IRenderMime.IMimeModel): Promise; + + /** + * Node to be updated by the renderer + */ + node: HTMLElement; + } + + /** + * The interface for a renderer factory. + */ + export interface IRendererFactory { + /** + * Whether the factory is a "safe" factory. + * + * #### Notes + * A "safe" factory produces renderer widgets which can render + * untrusted model data in a usable way. *All* renderers must + * handle untrusted data safely, but some may simply failover + * with a "Run cell to view output" message. A "safe" renderer + * is an indication that its sanitized output will be useful. + */ + readonly safe: boolean; + + /** + * The mime types handled by this factory. + */ + readonly mimeTypes: ReadonlyArray; + + /** + * The default rank of the factory. If not given, defaults to 100. + */ + readonly defaultRank?: number; + + /** + * Create a renderer which displays the mime data. + * + * @param options - The options used to render the data. + */ + createRenderer(options: IRendererOptions): IRenderer; + } + + /** + * The options used to create a renderer. + */ + export interface IRendererOptions { + /** + * The preferred mimeType to render. + */ + mimeType: string; + + /** + * The html sanitizer. + */ + sanitizer: ISanitizer; + + /** + * An optional url resolver. + */ + resolver: IResolver | null; + + /** + * An optional link handler. + */ + linkHandler: ILinkHandler | null; + + /** + * The LaTeX typesetter. + */ + latexTypesetter: ILatexTypesetter | null; + } + + /** + * An object that handles html sanitization. + */ + export interface ISanitizer { + /** + * Sanitize an HTML string. + */ + sanitize(dirty: string): string; + } + + /** + * An object that handles links on a node. + */ + export interface ILinkHandler { + /** + * Add the link handler to the node. + * + * @param node: the node for which to handle the link. + * + * @param path: the path to open when the link is clicked. + * + * @param id: an optional element id to scroll to when the path is opened. + */ + handleLink(node: HTMLElement, path: string, id?: string): void; + } + + /** + * An object that resolves relative URLs. + */ + export interface IResolver { + /** + * Resolve a relative url to a correct server path. + */ + resolveUrl(url: string): Promise; + + /** + * Get the download url of a given absolute server path. + */ + getDownloadUrl(path: string): Promise; + + /** + * Whether the URL should be handled by the resolver + * or not. + * + * #### Notes + * This is similar to the `isLocal` check in `URLExt`, + * but can also perform additional checks on whether the + * resolver should handle a given URL. + */ + isLocal?: (url: string) => boolean; + } + + /** + * The interface for a LaTeX typesetter. + */ + export interface ILatexTypesetter { + /** + * Typeset a DOM element. + * + * @param element - the DOM element to typeset. The typesetting may + * happen synchronously or asynchronously. + * + * #### Notes + * The application-wide rendermime object has a settable + * `latexTypesetter` property which is used wherever LaTeX + * typesetting is required. Extensions wishing to provide their + * own typesetter may replace that on the global `lab.rendermime`. + */ + typeset(element: HTMLElement): void; + } +} diff --git a/src/sql/parts/notebook/outputs/common/url.ts b/src/sql/parts/notebook/outputs/common/url.ts new file mode 100644 index 0000000000..5386ee1daa --- /dev/null +++ b/src/sql/parts/notebook/outputs/common/url.ts @@ -0,0 +1,184 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. + +import { JSONObject } from './jsonext'; +import URI from 'vs/base/common/uri'; + +/** + * The namespace for URL-related functions. + */ +export namespace URLExt { + /** + * Normalize a url. + */ + export function normalize(url: string): string { + return URI.parse(url).toString(); + } + + /** + * Join a sequence of url components and normalizes as in node `path.join`. + * + * @param parts - The url components. + * + * @returns the joined url. + */ + export function join(...parts: string[]): string { + parts = parts || []; + + // Isolate the top element. + const top = parts[0] || ''; + + // Check whether protocol shorthand is being used. + const shorthand = top.indexOf('//') === 0; + + // Parse the top element into a header collection. + const header = top.match(/(\w+)(:)(\/\/)?/); + const protocol = header && header[1]; + const colon = protocol && header[2]; + const slashes = colon && header[3]; + + // Construct the URL prefix. + const prefix = shorthand + ? '//' + : [protocol, colon, slashes].filter(str => str).join(''); + + // Construct the URL body omitting the prefix of the top value. + const body = [top.indexOf(prefix) === 0 ? top.replace(prefix, '') : top] + // Filter out top value if empty. + .filter(str => str) + // Remove leading slashes in all subsequent URL body elements. + .concat(parts.slice(1).map(str => str.replace(/^\//, ''))) + .join('/') + // Replace multiple slashes with one. + .replace(/\/+/g, '/'); + + return prefix + body; + } + + /** + * Encode the components of a multi-segment url. + * + * @param url - The url to encode. + * + * @returns the encoded url. + * + * #### Notes + * Preserves the `'/'` separators. + * Should not include the base url, since all parts are escaped. + */ + export function encodeParts(url: string): string { + return join(...url.split('/').map(encodeURIComponent)); + } + + /** + * Return a serialized object string suitable for a query. + * + * @param object - The source object. + * + * @returns an encoded url query. + * + * #### Notes + * Modified version of [stackoverflow](http://stackoverflow.com/a/30707423). + */ + export function objectToQueryString(value: JSONObject): string { + const keys = Object.keys(value); + + if (!keys.length) { + return ''; + } + + return ( + '?' + + keys + .map(key => { + const content = encodeURIComponent(String(value[key])); + + return key + (content ? '=' + content : ''); + }) + .join('&') + ); + } + + /** + * Return a parsed object that represents the values in a query string. + */ + export function queryStringToObject( + value: string + ): { [key: string]: string } { + return value + .replace(/^\?/, '') + .split('&') + .reduce( + (acc, val) => { + const [key, value] = val.split('='); + + acc[key] = decodeURIComponent(value || ''); + + return acc; + }, + {} as { [key: string]: string } + ); + } + + /** + * Test whether the url is a local url. + * + * #### Notes + * This function returns `false` for any fully qualified url, including + * `data:`, `file:`, and `//` protocol URLs. + */ + export function isLocal(url: string): boolean { + // If if doesn't have a scheme such as file: or http:// it's local + return !!URI.parse(url).scheme; + } + + /** + * The interface for a URL object + */ + export interface IUrl { + /** + * The full URL string that was parsed with both the protocol and host + * components converted to lower-case. + */ + href?: string; + + /** + * Identifies the URL's lower-cased protocol scheme. + */ + protocol?: string; + + /** + * The full lower-cased host portion of the URL, including the port if + * specified. + */ + host?: string; + + /** + * The lower-cased host name portion of the host component without the + * port included. + */ + hostname?: string; + + /** + * The numeric port portion of the host component. + */ + port?: string; + + /** + * The entire path section of the URL. + */ + pathname?: string; + + /** + * The "fragment" portion of the URL including the leading ASCII hash + * `(#)` character + */ + hash?: string; + + /** + * The search element, including leading question mark (`'?'`), if any, + * of the URL. + */ + search?: string; + } +} diff --git a/src/sql/parts/notebook/outputs/factories.ts b/src/sql/parts/notebook/outputs/factories.ts new file mode 100644 index 0000000000..a34d1cffa1 --- /dev/null +++ b/src/sql/parts/notebook/outputs/factories.ts @@ -0,0 +1,94 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ + +import * as widgets from './widgets'; +import { IRenderMime } from './common/renderMimeInterfaces'; + +/** + * A mime renderer factory for raw html. + */ +export const htmlRendererFactory: IRenderMime.IRendererFactory = { + safe: true, + mimeTypes: ['text/html'], + defaultRank: 50, + createRenderer: options => new widgets.RenderedHTML(options) +}; + +/** + * A mime renderer factory for images. + */ +export const imageRendererFactory: IRenderMime.IRendererFactory = { + safe: true, + mimeTypes: ['image/bmp', 'image/png', 'image/jpeg', 'image/gif'], + defaultRank: 90, + createRenderer: options => new widgets.RenderedImage(options) +}; + +// /** +// * A mime renderer factory for LaTeX. +// */ +// export const latexRendererFactory: IRenderMime.IRendererFactory = { +// safe: true, +// mimeTypes: ['text/latex'], +// defaultRank: 70, +// createRenderer: options => new widgets.RenderedLatex(options) +// }; + +// /** +// * A mime renderer factory for Markdown. +// */ +// export const markdownRendererFactory: IRenderMime.IRendererFactory = { +// safe: true, +// mimeTypes: ['text/markdown'], +// defaultRank: 60, +// createRenderer: options => new widgets.RenderedMarkdown(options) +// }; + +/** + * A mime renderer factory for svg. + */ +export const svgRendererFactory: IRenderMime.IRendererFactory = { + safe: false, + mimeTypes: ['image/svg+xml'], + defaultRank: 80, + createRenderer: options => new widgets.RenderedSVG(options) +}; + +/** + * A mime renderer factory for plain and jupyter console text data. + */ +export const textRendererFactory: IRenderMime.IRendererFactory = { + safe: true, + mimeTypes: [ + 'text/plain', + 'application/vnd.jupyter.stdout', + 'application/vnd.jupyter.stderr' + ], + defaultRank: 120, + createRenderer: options => new widgets.RenderedText(options) +}; + +/** + * A placeholder factory for deprecated rendered JavaScript. + */ +export const javaScriptRendererFactory: IRenderMime.IRendererFactory = { + safe: false, + mimeTypes: ['text/javascript', 'application/javascript'], + defaultRank: 110, + createRenderer: options => new widgets.RenderedJavaScript(options) +}; + +/** + * The standard factories provided by the rendermime package. + */ +export const standardRendererFactories: ReadonlyArray = [ + htmlRendererFactory, + // markdownRendererFactory, + // latexRendererFactory, + svgRendererFactory, + imageRendererFactory, + javaScriptRendererFactory, + textRendererFactory +]; diff --git a/src/sql/parts/notebook/outputs/registry.ts b/src/sql/parts/notebook/outputs/registry.ts new file mode 100644 index 0000000000..b35004e696 --- /dev/null +++ b/src/sql/parts/notebook/outputs/registry.ts @@ -0,0 +1,352 @@ + +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ +import { IRenderMime } from './common/renderMimeInterfaces'; +import { MimeModel } from './common/mimemodel'; +import { ReadonlyJSONObject } from './common/jsonext'; +import { defaultSanitizer } from './sanitizer'; + +/** + * An object which manages mime renderer factories. + * + * This object is used to render mime models using registered mime + * renderers, selecting the preferred mime renderer to render the + * model into a widget. + * + * #### Notes + * This class is not intended to be subclassed. + */ +export class RenderMimeRegistry { + /** + * Construct a new rendermime. + * + * @param options - The options for initializing the instance. + */ + constructor(options: RenderMimeRegistry.IOptions = {}) { + // Parse the options. + this.resolver = options.resolver || null; + this.linkHandler = options.linkHandler || null; + this.latexTypesetter = options.latexTypesetter || null; + this.sanitizer = options.sanitizer || defaultSanitizer; + + // Add the initial factories. + if (options.initialFactories) { + for (let factory of options.initialFactories) { + this.addFactory(factory); + } + } + } + + /** + * The sanitizer used by the rendermime instance. + */ + readonly sanitizer: IRenderMime.ISanitizer; + + /** + * The object used to resolve relative urls for the rendermime instance. + */ + readonly resolver: IRenderMime.IResolver | null; + + /** + * The object used to handle path opening links. + */ + readonly linkHandler: IRenderMime.ILinkHandler | null; + + /** + * The LaTeX typesetter for the rendermime. + */ + readonly latexTypesetter: IRenderMime.ILatexTypesetter | null; + + /** + * The ordered list of mimeTypes. + */ + get mimeTypes(): ReadonlyArray { + return this._types || (this._types = Private.sortedTypes(this._ranks)); + } + + /** + * Find the preferred mime type for a mime bundle. + * + * @param bundle - The bundle of mime data. + * + * @param safe - How to consider safe/unsafe factories. If 'ensure', + * it will only consider safe factories. If 'any', any factory will be + * considered. If 'prefer', unsafe factories will be considered, but + * only after the safe options have been exhausted. + * + * @returns The preferred mime type from the available factories, + * or `undefined` if the mime type cannot be rendered. + */ + preferredMimeType( + bundle: ReadonlyJSONObject, + safe: 'ensure' | 'prefer' | 'any' = 'ensure' + ): string | undefined { + // Try to find a safe factory first, if preferred. + if (safe === 'ensure' || safe === 'prefer') { + for (let mt of this.mimeTypes) { + if (mt in bundle && this._factories[mt].safe) { + return mt; + } + } + } + + if (safe !== 'ensure') { + // Otherwise, search for the best factory among all factories. + for (let mt of this.mimeTypes) { + if (mt in bundle) { + return mt; + } + } + } + + // Otherwise, no matching mime type exists. + return undefined; + } + + /** + * Create a renderer for a mime type. + * + * @param mimeType - The mime type of interest. + * + * @returns A new renderer for the given mime type. + * + * @throws An error if no factory exists for the mime type. + */ + createRenderer(mimeType: string): IRenderMime.IRenderer { + // Throw an error if no factory exists for the mime type. + if (!(mimeType in this._factories)) { + throw new Error(`No factory for mime type: '${mimeType}'`); + } + + // Invoke the best factory for the given mime type. + return this._factories[mimeType].createRenderer({ + mimeType, + resolver: this.resolver, + sanitizer: this.sanitizer, + linkHandler: this.linkHandler, + latexTypesetter: this.latexTypesetter + }); + } + + /** + * Create a new mime model. This is a convenience method. + * + * @options - The options used to create the model. + * + * @returns A new mime model. + */ + createModel(options: MimeModel.IOptions = {}): MimeModel { + return new MimeModel(options); + } + + /** + * Create a clone of this rendermime instance. + * + * @param options - The options for configuring the clone. + * + * @returns A new independent clone of the rendermime. + */ + clone(options: RenderMimeRegistry.ICloneOptions = {}): RenderMimeRegistry { + // Create the clone. + let clone = new RenderMimeRegistry({ + resolver: options.resolver || this.resolver || undefined, + sanitizer: options.sanitizer || this.sanitizer || undefined, + linkHandler: options.linkHandler || this.linkHandler || undefined, + latexTypesetter: options.latexTypesetter || this.latexTypesetter + }); + + // Clone the internal state. + clone._factories = { ...this._factories }; + clone._ranks = { ...this._ranks }; + clone._id = this._id; + + // Return the cloned object. + return clone; + } + + /** + * Get the renderer factory registered for a mime type. + * + * @param mimeType - The mime type of interest. + * + * @returns The factory for the mime type, or `undefined`. + */ + getFactory(mimeType: string): IRenderMime.IRendererFactory | undefined { + return this._factories[mimeType]; + } + + /** + * Add a renderer factory to the rendermime. + * + * @param factory - The renderer factory of interest. + * + * @param rank - The rank of the renderer. A lower rank indicates + * a higher priority for rendering. If not given, the rank will + * defer to the `defaultRank` of the factory. If no `defaultRank` + * is given, it will default to 100. + * + * #### Notes + * The renderer will replace an existing renderer for the given + * mimeType. + */ + addFactory(factory: IRenderMime.IRendererFactory, rank?: number): void { + if (rank === undefined) { + rank = factory.defaultRank; + if (rank === undefined) { + rank = 100; + } + } + for (let mt of factory.mimeTypes) { + this._factories[mt] = factory; + this._ranks[mt] = { rank, id: this._id++ }; + } + this._types = null; + } + + /** + * Remove a mime type. + * + * @param mimeType - The mime type of interest. + */ + removeMimeType(mimeType: string): void { + delete this._factories[mimeType]; + delete this._ranks[mimeType]; + this._types = null; + } + + /** + * Get the rank for a given mime type. + * + * @param mimeType - The mime type of interest. + * + * @returns The rank of the mime type or undefined. + */ + getRank(mimeType: string): number | undefined { + let rank = this._ranks[mimeType]; + return rank && rank.rank; + } + + /** + * Set the rank of a given mime type. + * + * @param mimeType - The mime type of interest. + * + * @param rank - The new rank to assign. + * + * #### Notes + * This is a no-op if the mime type is not registered. + */ + setRank(mimeType: string, rank: number): void { + if (!this._ranks[mimeType]) { + return; + } + let id = this._id++; + this._ranks[mimeType] = { rank, id }; + this._types = null; + } + + private _id = 0; + private _ranks: Private.RankMap = {}; + private _types: string[] | null = null; + private _factories: Private.FactoryMap = {}; +} + +/** + * The namespace for `RenderMimeRegistry` class statics. + */ +export namespace RenderMimeRegistry { + /** + * The options used to initialize a rendermime instance. + */ + export interface IOptions { + /** + * Initial factories to add to the rendermime instance. + */ + initialFactories?: ReadonlyArray; + + /** + * The sanitizer used to sanitize untrusted html inputs. + * + * If not given, a default sanitizer will be used. + */ + sanitizer?: IRenderMime.ISanitizer; + + /** + * The initial resolver object. + * + * The default is `null`. + */ + resolver?: IRenderMime.IResolver; + + /** + * An optional path handler. + */ + linkHandler?: IRenderMime.ILinkHandler; + + /** + * An optional LaTeX typesetter. + */ + latexTypesetter?: IRenderMime.ILatexTypesetter; + } + + /** + * The options used to clone a rendermime instance. + */ + export interface ICloneOptions { + /** + * The new sanitizer used to sanitize untrusted html inputs. + */ + sanitizer?: IRenderMime.ISanitizer; + + /** + * The new resolver object. + */ + resolver?: IRenderMime.IResolver; + + /** + * The new path handler. + */ + linkHandler?: IRenderMime.ILinkHandler; + + /** + * The new LaTeX typesetter. + */ + latexTypesetter?: IRenderMime.ILatexTypesetter; + } +} + +/** + * The namespace for the module implementation details. + */ +namespace Private { + /** + * A type alias for a mime rank and tie-breaking id. + */ + export type RankPair = { readonly id: number; readonly rank: number }; + + /** + * A type alias for a mapping of mime type -> rank pair. + */ + export type RankMap = { [key: string]: RankPair }; + + /** + * A type alias for a mapping of mime type -> ordered factories. + */ + export type FactoryMap = { [key: string]: IRenderMime.IRendererFactory }; + + /** + * Get the mime types in the map, ordered by rank. + */ + export function sortedTypes(map: RankMap): string[] { + return Object.keys(map).sort((a, b) => { + let p1 = map[a]; + let p2 = map[b]; + if (p1.rank !== p2.rank) { + return p1.rank - p2.rank; + } + return p1.id - p2.id; + }); + } +} diff --git a/src/sql/parts/notebook/outputs/renderers.ts b/src/sql/parts/notebook/outputs/renderers.ts new file mode 100644 index 0000000000..498ebe0435 --- /dev/null +++ b/src/sql/parts/notebook/outputs/renderers.ts @@ -0,0 +1,629 @@ +/*----------------------------------------------------------------------------- +| Copyright (c) Jupyter Development Team. +| Distributed under the terms of the Modified BSD License. +|----------------------------------------------------------------------------*/ + +import { default as AnsiUp } from 'ansi_up'; +import { IRenderMime } from './common/renderMimeInterfaces'; +import { URLExt } from './common/url'; +import URI from 'vs/base/common/uri'; + + +/** + * Render HTML into a host node. + * + * @params options - The options for rendering. + * + * @returns A promise which resolves when rendering is complete. + */ +export function renderHTML(options: renderHTML.IOptions): Promise { + // Unpack the options. + let { + host, + source, + trusted, + sanitizer, + resolver, + linkHandler, + shouldTypeset, + latexTypesetter + } = options; + + let originalSource = source; + + // Bail early if the source is empty. + if (!source) { + host.textContent = ''; + return Promise.resolve(undefined); + } + + // Sanitize the source if it is not trusted. This removes all + // `