diff --git a/.changeset/blue-ways-teach.md b/.changeset/blue-ways-teach.md new file mode 100644 index 00000000..86465d5a --- /dev/null +++ b/.changeset/blue-ways-teach.md @@ -0,0 +1,6 @@ +--- +"hyperbook": minor +"@hyperbook/markdown": minor +--- + +Add pyide diff --git a/.changeset/funny-radios-jog.md b/.changeset/funny-radios-jog.md new file mode 100644 index 00000000..5128ab4b --- /dev/null +++ b/.changeset/funny-radios-jog.md @@ -0,0 +1,6 @@ +--- +"hyperbook": minor +"@hyperbook/markdown": minor +--- + +Add input and tests to pyide diff --git a/packages/markdown/assets/code.css b/packages/markdown/assets/code.css index 3d9c400b..081e39db 100644 --- a/packages/markdown/assets/code.css +++ b/packages/markdown/assets/code.css @@ -182,4 +182,21 @@ code[data-line-numbers-max-digits="4"]>[data-line]::before { code-input:not(.code-input_registered)::before { content: "..."!important; +} + +/** + * Allows code-input elements to be used with the Prism.js line-numbers plugin, as long as the code-input element + * or a parent element of it has the CSS class `line-numbers`. + * https://prismjs.com/plugins/line-numbers/ + * Files: prism-line-numbers.css + */ +/* Update padding to match line-numbers plugin */ +code-input.line-numbers textarea, code-input.line-numbers.code-input_pre-element-styled pre, +.line-numbers code-input textarea, .line-numbers code-input.code-input_pre-element-styled pre { + padding-left: max(3.8em, var(--padding, 16px))!important; +} + +/* Ensure pre code/textarea just wide enough to give 100% width with line numbers */ +code-input.line-numbers, .line-numbers code-input { + grid-template-columns: calc(100% - max(0em, calc(3.8em - var(--padding, 16px)))); } \ No newline at end of file diff --git a/packages/markdown/assets/directive-pyide/client.js b/packages/markdown/assets/directive-pyide/client.js index 4da3c0c0..e4df34a1 100644 --- a/packages/markdown/assets/directive-pyide/client.js +++ b/packages/markdown/assets/directive-pyide/client.js @@ -11,55 +11,114 @@ hyperbook.python = (function () { `${HYPERBOOK_ASSETS}directive-pyide/webworker.js` ); - const callbacks = {}; - let isRunning = false; + let callback = null; + /** + * @type Uint8Array + */ + let interruptBuffer; + /** + * @type Int32Array + */ + let stdinBuffer; + if (window.crossOriginIsolated) { + interruptBuffer = new Uint8Array(new SharedArrayBuffer(1)); + stdinBuffer = new Int32Array(new SharedArrayBuffer(1024)); + pyodideWorker.postMessage({ + type: "setStdinBuffer", + payload: { stdinBuffer }, + }); + pyodideWorker.postMessage({ + type: "setInterruptBuffer", + payload: { interruptBuffer }, + }); + } else { + interruptBuffer = new ArrayBuffer(1); + pyodideWorker.postMessage({ + type: "setInterruptBuffer", + payload: { interruptBuffer }, + }); + } - const asyncRun = (id) => { - if (isRunning) return; + const asyncRun = (id, type) => { + if (callback) return; - isRunning = true; - updateRunning(); + interruptBuffer[0] = 0; return (script, context) => { - // the id could be generated more carefully return new Promise((onSuccess) => { - callbacks[id] = onSuccess; + callback = onSuccess; + updateRunning(id, type); pyodideWorker.postMessage({ - ...context, - python: script, + type: "run", + payload: { + ...context, + python: script, + }, id, }); }); }; }; - const updateRunning = () => { + function interruptExecution() { + // 2 stands for SIGINT. + interruptBuffer[0] = 2; + } + + const updateRunning = (id, type) => { for (let elem of elems) { const run = elem.getElementsByClassName("run")[0]; - if (isRunning) { - run.classList.add("running"); - run.textContent = "Running ..."; + const test = elem.getElementsByClassName("test")[0]; + if (callback) { + if (elem.id === id && type === "run") { + run.textContent = "Running (Click to stop) ..."; + run.addEventListener("click", interruptExecution); + } else if (test && elem.id === id && type === "test") { + test.textContent = "Testing (Click to stop) ..."; + test.addEventListener("click", interruptExecution); + } else { + run.classList.add("running"); + run.disabled = true; + if (test) { + test.classList.add("running"); + test.disabled = true; + } + } } else { run.classList.remove("running"); run.textContent = "Run"; + run.disabled = false; + run.removeEventListener("click", interruptExecution); + if (test) { + test.classList.remove("running"); + test.textContent = "Test"; + test.disabled = false; + test.removeEventListener("click", interruptExecution); + } } - run.disabled = isRunning; } }; pyodideWorker.onmessage = (event) => { - const { id, ...data } = event.data; - if (data.type === "stdout") { - const output = document - .getElementById(id) - .getElementsByClassName("output")[0]; - output.appendChild(document.createTextNode(data.message + "\n")); - return; + const { id, type, payload } = event.data; + switch (type) { + case "stdout": { + const output = document + .getElementById(id) + .getElementsByClassName("output")[0]; + output.appendChild(document.createTextNode(payload + "\n")); + break; + } + case "error": { + const onSuccess = callback; + onSuccess({ error: payload }); + break; + } + case "success": { + const onSuccess = callback; + onSuccess({ results: payload }); + break; + } } - const onSuccess = callbacks[id]; - delete callbacks[id]; - isRunning = false; - updateRunning(); - onSuccess(data); }; const elems = document.getElementsByClassName("directive-pyide"); @@ -67,22 +126,91 @@ hyperbook.python = (function () { for (let elem of elems) { const editor = elem.getElementsByClassName("editor")[0]; const run = elem.getElementsByClassName("run")[0]; + const test = elem.getElementsByClassName("test")[0]; const output = elem.getElementsByClassName("output")[0]; + const input = elem.getElementsByClassName("input")[0]; + const outputBtn = elem.getElementsByClassName("output-btn")[0]; + const inputBtn = elem.getElementsByClassName("input-btn")[0]; const id = elem.id; + let tests = []; + try { + tests = JSON.parse(atob(elem.getAttribute("data-tests"))); + } catch (e) {} + + function showInput() { + outputBtn.classList.remove("active"); + inputBtn.classList.add("active"); + output.classList.add("hidden"); + input.classList.remove("hidden"); + } + function showOutput() { + outputBtn.classList.add("active"); + inputBtn.classList.remove("active"); + output.classList.remove("hidden"); + input.classList.add("hidden"); + } + + outputBtn?.addEventListener("click", showOutput); + inputBtn?.addEventListener("click", showInput); + + test?.addEventListener("click", async () => { + showOutput(); + if (callback) return; + + output.innerHTML = ""; + + const script = editor.value; + for (let test of tests) { + const testCode = test.code.replace("#SCRIPT#", script); + + const heading = document.createElement("div"); + console.log(test); + heading.innerHTML = `== Test ${test.name} ==`; + heading.classList.add("test-heading"); + output.appendChild(heading); + + await asyncRun(id, "test")(testCode, {}) + .then(({ results, error }) => { + if (results) { + output.textContent += results; + } else if (error) { + output.textContent += error; + } + callback = null; + updateRunning(id, "test"); + }) + .catch((e) => { + output.textContent = `Error: ${e}`; + console.log(e); + callback = null; + updateRunning(id, "test"); + }); + } + }); + + run?.addEventListener("click", async () => { + showOutput(); + if (callback) return; - run?.addEventListener("click", () => { const script = editor.value; output.innerHTML = ""; - asyncRun(id)(script, {}) + asyncRun(id, "run")(script, { + inputs: input.value.split("\n"), + }) .then(({ results, error }) => { if (results) { - output.textContent = results; + output.textContent += results; } else if (error) { - output.textContent = error; + output.textContent += error; } + callback = null; + updateRunning(id, "run"); }) .catch((e) => { output.textContent = `Error: ${e}`; + console.log(e); + callback = null; + updateRunning(id, "run"); }); }); } diff --git a/packages/markdown/assets/directive-pyide/style.css b/packages/markdown/assets/directive-pyide/style.css index 42f24330..24f97067 100644 --- a/packages/markdown/assets/directive-pyide/style.css +++ b/packages/markdown/assets/directive-pyide/style.css @@ -14,15 +14,30 @@ .directive-pyide .container { width: 100%; + overflow: hidden; + height: 200px; + display: flex; + flex-direction: column; +} + +.directive-pyide .output, +.directive-pyide .input { + white-space: pre; + height: 100%; border: 1px solid var(--color-spacer); border-radius: 8px; - overflow: hidden; - padding: 10px; + border-top-left-radius: 0; + border-top-right-radius: 0; } .directive-pyide .output { - height: 200px; - white-space: pre; + padding: 16px; + margin-bottom: 0px; + font-family: hyperbook-monospace, monospace; +} + +.directive-pyide .hidden { + display: none; } .directive-pyide .editor-container { @@ -41,26 +56,52 @@ flex: 1; } -.directive-pyide button { - padding: 8px 16px; +.directive-pyide .buttons { + display: flex; border: 1px solid var(--color-spacer); border-radius: 8px; border-bottom: none; border-bottom-left-radius: 0; border-bottom-right-radius: 0; +} + +.directive-pyide .test-heading { + font-size: 1.1em; + font-weight: bold; + text-decoration: underline; + margin-top: 8px; +} + +.directive-pyide .test-heading:first-of-type { + margin-top: 0; +} + +.directive-pyide button { + flex: 1; + padding: 8px 16px; + border: none; + border-right: 1px solid var(--color-spacer); background-color: var(--color--background); color: var(--color-text); cursor: pointer; } +.directive-pyide .buttons:last-child { + border-right: none; +} + +.directive-pyide button.active { + background-color: var(--color-spacer); +} + .directive-pyide button:hover { background-color: var(--color-spacer); } .directive-pyide button.running { - pointer-events: none; - cursor: not-allowed; - opacity: 0.5; + pointer-events: none; + cursor: not-allowed; + opacity: 0.5; } @media screen and (min-width: 1024px) { @@ -69,7 +110,7 @@ height: calc(100dvh - 128px); .output { - height: 100%; + height: 100%; } .container { diff --git a/packages/markdown/assets/directive-pyide/webworker.js b/packages/markdown/assets/directive-pyide/webworker.js index eddb354c..462a54ad 100644 --- a/packages/markdown/assets/directive-pyide/webworker.js +++ b/packages/markdown/assets/directive-pyide/webworker.js @@ -3,40 +3,84 @@ // and `.wasm` files as well: importScripts("https://cdn.jsdelivr.net/pyodide/v0.26.4/full/pyodide.js"); +class StdinHandler { + constructor(results, options) { + this.results = results; + this.idx = 0; + Object.assign(this, options); + } + + stdin() { + return this.results[this.idx++]; + } +} + async function loadPyodideAndPackages() { - self.pyodide = await loadPyodide(); + var pyodide = await loadPyodide(); + self.pyodide = pyodide; await self.pyodide.loadPackage([]); } let pyodideReadyPromise = loadPyodideAndPackages(); -self.onmessage = async (event) => { - // make sure loading is done - await pyodideReadyPromise; - // Don't bother yet with this line, suppose our API is built in such a way: - const { id, python, ...context } = event.data; - // The worker copies the context in its own "memory" (an object mapping name to values) - for (const key of Object.keys(context)) { - self[key] = context[key]; - } +self.onmessage = async ({ data: { id, type, payload } }) => { + switch (type) { + case "run": { + // make sure loading is done + await pyodideReadyPromise; + // Don't bother yet with this line, suppose our API is built in such a way: + const { python, inputs, ...context } = payload; + // The worker copies the context in its own "memory" (an object mapping name to values) + for (const key of Object.keys(context)) { + self[key] = context[key]; + } + + self.pyodide.setStdin(new StdinHandler(inputs)); + + self.pyodide.setStdout({ + batched: (msg) => { + self.postMessage({ id, type: "stdout", payload: msg }); + }, + }); + + self.pyodide.setStderr({ + batched: (msg) => { + self.postMessage({ id, type: "stderr", payload: msg }); + }, + }); - self.pyodide.setStdout({ - batched: (msg) => { - self.postMessage({ id, type: "stdout", message: msg }); - }, - }); - - self.pyodide.setStderr({ - batched: (msg) => { - self.postMessage({ id, type: "stderr", message: msg }); - }, - }); - - // Now is the easy part, the one that is similar to working in the main thread: - try { - await self.pyodide.loadPackagesFromImports(python); - let results = await self.pyodide.runPythonAsync(python); - self.postMessage({ results, id }); - } catch (error) { - self.postMessage({ error: error.message, id }); + // Now is the easy part, the one that is similar to working in the main thread: + const filename = ""; + try { + await self.pyodide.loadPackagesFromImports(python); + const dict = self.pyodide.globals.get("dict"); + const globals = dict(); + let results = await self.pyodide.runPythonAsync(python, { + globals, + locals: globals, + filename, + }); + globals.destroy(); + dict.destroy(); + self.postMessage({ type: "success", id, payload: results }); + } catch (error) { + // clean up trackback + let message = error.message; + if (message.startsWith("Traceback")) { + const lines = message?.split("\n") || []; + const i = lines.findIndex((line) => line.includes(filename)); + message = lines[0] + "\n" + lines.slice(i).join("\n"); + self.postMessage({ type: "error", payload: message, id }); + } + self.postMessage({ type: "error", payload: message, id }); + } + break; + } + case "setInterruptBuffer": { + const { interruptBuffer } = payload; + // make sure loading is done + await pyodideReadyPromise; + self.pyodide.setInterruptBuffer(interruptBuffer); + break; + } } }; diff --git a/packages/markdown/assets/prism/prism.css b/packages/markdown/assets/prism/prism.css index def066e2..868feaf8 100644 --- a/packages/markdown/assets/prism/prism.css +++ b/packages/markdown/assets/prism/prism.css @@ -1,3 +1,5 @@ /* PrismJS 1.29.0 -https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+java+plsql+python+sql&plugins=line-numbers+toolbar+copy-to-clipboard+download-button */ code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help} +pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right} +div.code-toolbar{position:relative}div.code-toolbar>.toolbar{position:absolute;z-index:10;top:.3em;right:.2em;transition:opacity .3s ease-in-out;opacity:0}div.code-toolbar:hover>.toolbar{opacity:1}div.code-toolbar:focus-within>.toolbar{opacity:1}div.code-toolbar>.toolbar>.toolbar-item{display:inline-block}div.code-toolbar>.toolbar>.toolbar-item>a{cursor:pointer}div.code-toolbar>.toolbar>.toolbar-item>button{background:0 0;border:0;color:inherit;font:inherit;line-height:normal;overflow:visible;padding:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}div.code-toolbar>.toolbar>.toolbar-item>a,div.code-toolbar>.toolbar>.toolbar-item>button,div.code-toolbar>.toolbar>.toolbar-item>span{color:#bbb;font-size:.8em;padding:0 .5em;background:#f5f2f0;background:rgba(224,224,224,.2);box-shadow:0 2px 0 0 rgba(0,0,0,.2);border-radius:.5em}div.code-toolbar>.toolbar>.toolbar-item>a:focus,div.code-toolbar>.toolbar>.toolbar-item>a:hover,div.code-toolbar>.toolbar>.toolbar-item>button:focus,div.code-toolbar>.toolbar>.toolbar-item>button:hover,div.code-toolbar>.toolbar>.toolbar-item>span:focus,div.code-toolbar>.toolbar>.toolbar-item>span:hover{color:inherit;text-decoration:none} diff --git a/packages/markdown/assets/prism/prism.js b/packages/markdown/assets/prism/prism.js index 8a133c56..da33f3db 100644 --- a/packages/markdown/assets/prism/prism.js +++ b/packages/markdown/assets/prism/prism.js @@ -1,8 +1,12 @@ /* PrismJS 1.29.0 -https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+python */ +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+java+plsql+python+sql&plugins=line-numbers */ var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=g.reach);A+=w.value.length,w=w.next){var E=w.value;if(n.length>e.length)return;if(!(E instanceof i)){var P,L=1;if(y){if(!(P=l(b,A,e,m))||P.index>=e.length)break;var S=P.index,O=P.index+P[0].length,j=A;for(j+=w.value.length;S>=j;)j+=(w=w.next).value.length;if(A=j-=w.value.length,w.value instanceof i)continue;for(var C=w;C!==n.tail&&(jg.reach&&(g.reach=W);var z=w.prev;if(_&&(z=u(n,z,_),A+=_.length),c(n,z,L),w=u(n,z,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),L>1){var I={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,I),g&&I.reach>g.reach&&(g.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a"+i.content+""},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); Prism.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",(function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))})),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^$/i;var t={"included-cdata":{pattern://i,inside:s}};t["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var n={};n[a]={pattern:RegExp("(<__[^>]*>)(?:))*\\]\\]>|(?!)".replace(/__/g,(function(){return a})),"i"),lookbehind:!0,greedy:!0,inside:t},Prism.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(a,e){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp("(^|[\"'\\s])(?:"+a+")\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))","i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[e,"language-"+e],inside:Prism.languages[e]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml; !function(s){var e=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;s.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:[^;{\\s\"']|\\s+(?!\\s)|"+e.source+")*?(?:;|(?=\\s*\\{))"),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+e.source+"|(?:[^\\\\\r\n()\"']|\\\\[^])*)\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+e.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+e.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:e,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var t=s.languages.markup;t&&(t.tag.addInlined("style","css"),t.tag.addAttribute("style","css"))}(Prism); Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/}; Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp("(^|[^\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?|\\d+(?:_\\d+)*n|(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?)(?![\\w$])"),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp("((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))"),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),Prism.languages.js=Prism.languages.javascript; +!function(e){var n=/\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|non-sealed|null|open|opens|package|permits|private|protected|provides|public|record(?!\s*[(){}[\]<>=%~.:,;?+\-*/&|^])|requires|return|sealed|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\b/,t="(?:[a-z]\\w*\\s*\\.\\s*)*(?:[A-Z]\\w*\\s*\\.\\s*)*",s={pattern:RegExp("(^|[^\\w.])"+t+"[A-Z](?:[\\d_A-Z]*[a-z]\\w*)?\\b"),lookbehind:!0,inside:{namespace:{pattern:/^[a-z]\w*(?:\s*\.\s*[a-z]\w*)*(?:\s*\.)?/,inside:{punctuation:/\./}},punctuation:/\./}};e.languages.java=e.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"/,lookbehind:!0,greedy:!0},"class-name":[s,{pattern:RegExp("(^|[^\\w.])"+t+"[A-Z]\\w*(?=\\s+\\w+\\s*[;,=()]|\\s*(?:\\[[\\s,]*\\]\\s*)?::\\s*new\\b)"),lookbehind:!0,inside:s.inside},{pattern:RegExp("(\\b(?:class|enum|extends|implements|instanceof|interface|new|record|throws)\\s+)"+t+"[A-Z]\\w*\\b"),lookbehind:!0,inside:s.inside}],keyword:n,function:[e.languages.clike.function,{pattern:/(::\s*)[a-z_]\w*/,lookbehind:!0}],number:/\b0b[01][01_]*L?\b|\b0x(?:\.[\da-f_p+-]+|[\da-f_]+(?:\.[\da-f_p+-]+)?)\b|(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i,operator:{pattern:/(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\|\||::|[?:~]|[-+*/%&|^!=<>]=?)/m,lookbehind:!0},constant:/\b[A-Z][A-Z_\d]+\b/}),e.languages.insertBefore("java","string",{"triple-quoted-string":{pattern:/"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/,greedy:!0,alias:"string"},char:{pattern:/'(?:\\.|[^'\\\r\n]){1,6}'/,greedy:!0}}),e.languages.insertBefore("java","class-name",{annotation:{pattern:/(^|[^.])@\w+(?:\s*\.\s*\w+)*/,lookbehind:!0,alias:"punctuation"},generics:{pattern:/<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&))*>)*>)*>)*>/,inside:{"class-name":s,keyword:n,punctuation:/[<>(),.:]/,operator:/[?&|]/}},import:[{pattern:RegExp("(\\bimport\\s+)"+t+"(?:[A-Z]\\w*|\\*)(?=\\s*;)"),lookbehind:!0,inside:{namespace:s.inside.namespace,punctuation:/\./,operator:/\*/,"class-name":/\w+/}},{pattern:RegExp("(\\bimport\\s+static\\s+)"+t+"(?:\\w+|\\*)(?=\\s*;)"),lookbehind:!0,alias:"static",inside:{namespace:s.inside.namespace,static:/\b\w+$/,punctuation:/\./,operator:/\*/,"class-name":/\w+/}}],namespace:{pattern:RegExp("(\\b(?:exports|import(?:\\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\\s+)(?!)[a-z]\\w*(?:\\.[a-z]\\w*)*\\.?".replace(//g,(function(){return n.source}))),lookbehind:!0,inside:{punctuation:/\./}}})}(Prism); +Prism.languages.sql={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,lookbehind:!0},variable:[{pattern:/@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/,greedy:!0},/@[\w.$]+/],string:{pattern:/(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/,greedy:!0,lookbehind:!0},identifier:{pattern:/(^|[^@\\])`(?:\\[\s\S]|[^`\\]|``)*`/,greedy:!0,lookbehind:!0,inside:{punctuation:/^`|`$/}},function:/\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:COL|_INSERT)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURN(?:ING|S)?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i,boolean:/\b(?:FALSE|NULL|TRUE)\b/i,number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?|\B\.\d+\b/i,operator:/[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/}; +Prism.languages.plsql=Prism.languages.extend("sql",{comment:{pattern:/\/\*[\s\S]*?\*\/|--.*/,greedy:!0},keyword:/\b(?:A|ACCESSIBLE|ADD|AGENT|AGGREGATE|ALL|ALTER|AND|ANY|ARRAY|AS|ASC|AT|ATTRIBUTE|AUTHID|AVG|BEGIN|BETWEEN|BFILE_BASE|BINARY|BLOB_BASE|BLOCK|BODY|BOTH|BOUND|BULK|BY|BYTE|C|CALL|CALLING|CASCADE|CASE|CHAR|CHARACTER|CHARSET|CHARSETFORM|CHARSETID|CHAR_BASE|CHECK|CLOB_BASE|CLONE|CLOSE|CLUSTER|CLUSTERS|COLAUTH|COLLECT|COLUMNS|COMMENT|COMMIT|COMMITTED|COMPILED|COMPRESS|CONNECT|CONSTANT|CONSTRUCTOR|CONTEXT|CONTINUE|CONVERT|COUNT|CRASH|CREATE|CREDENTIAL|CURRENT|CURSOR|CUSTOMDATUM|DANGLING|DATA|DATE|DATE_BASE|DAY|DECLARE|DEFAULT|DEFINE|DELETE|DESC|DETERMINISTIC|DIRECTORY|DISTINCT|DOUBLE|DROP|DURATION|ELEMENT|ELSE|ELSIF|EMPTY|END|ESCAPE|EXCEPT|EXCEPTION|EXCEPTIONS|EXCLUSIVE|EXECUTE|EXISTS|EXIT|EXTERNAL|FETCH|FINAL|FIRST|FIXED|FLOAT|FOR|FORALL|FORCE|FROM|FUNCTION|GENERAL|GOTO|GRANT|GROUP|HASH|HAVING|HEAP|HIDDEN|HOUR|IDENTIFIED|IF|IMMEDIATE|IMMUTABLE|IN|INCLUDING|INDEX|INDEXES|INDICATOR|INDICES|INFINITE|INSERT|INSTANTIABLE|INT|INTERFACE|INTERSECT|INTERVAL|INTO|INVALIDATE|IS|ISOLATION|JAVA|LANGUAGE|LARGE|LEADING|LENGTH|LEVEL|LIBRARY|LIKE|LIKE2|LIKE4|LIKEC|LIMIT|LIMITED|LOCAL|LOCK|LONG|LOOP|MAP|MAX|MAXLEN|MEMBER|MERGE|MIN|MINUS|MINUTE|MOD|MODE|MODIFY|MONTH|MULTISET|MUTABLE|NAME|NAN|NATIONAL|NATIVE|NCHAR|NEW|NOCOMPRESS|NOCOPY|NOT|NOWAIT|NULL|NUMBER_BASE|OBJECT|OCICOLL|OCIDATE|OCIDATETIME|OCIDURATION|OCIINTERVAL|OCILOBLOCATOR|OCINUMBER|OCIRAW|OCIREF|OCIREFCURSOR|OCIROWID|OCISTRING|OCITYPE|OF|OLD|ON|ONLY|OPAQUE|OPEN|OPERATOR|OPTION|OR|ORACLE|ORADATA|ORDER|ORGANIZATION|ORLANY|ORLVARY|OTHERS|OUT|OVERLAPS|OVERRIDING|PACKAGE|PARALLEL_ENABLE|PARAMETER|PARAMETERS|PARENT|PARTITION|PASCAL|PERSISTABLE|PIPE|PIPELINED|PLUGGABLE|POLYMORPHIC|PRAGMA|PRECISION|PRIOR|PRIVATE|PROCEDURE|PUBLIC|RAISE|RANGE|RAW|READ|RECORD|REF|REFERENCE|RELIES_ON|REM|REMAINDER|RENAME|RESOURCE|RESULT|RESULT_CACHE|RETURN|RETURNING|REVERSE|REVOKE|ROLLBACK|ROW|SAMPLE|SAVE|SAVEPOINT|SB1|SB2|SB4|SECOND|SEGMENT|SELECT|SELF|SEPARATE|SEQUENCE|SERIALIZABLE|SET|SHARE|SHORT|SIZE|SIZE_T|SOME|SPARSE|SQL|SQLCODE|SQLDATA|SQLNAME|SQLSTATE|STANDARD|START|STATIC|STDDEV|STORED|STRING|STRUCT|STYLE|SUBMULTISET|SUBPARTITION|SUBSTITUTABLE|SUBTYPE|SUM|SYNONYM|TABAUTH|TABLE|TDO|THE|THEN|TIME|TIMESTAMP|TIMEZONE_ABBR|TIMEZONE_HOUR|TIMEZONE_MINUTE|TIMEZONE_REGION|TO|TRAILING|TRANSACTION|TRANSACTIONAL|TRUSTED|TYPE|UB1|UB2|UB4|UNDER|UNION|UNIQUE|UNPLUG|UNSIGNED|UNTRUSTED|UPDATE|USE|USING|VALIST|VALUE|VALUES|VARIABLE|VARIANCE|VARRAY|VARYING|VIEW|VIEWS|VOID|WHEN|WHERE|WHILE|WITH|WORK|WRAPPED|WRITE|YEAR|ZONE)\b/i,operator:/:=?|=>|[<>^~!]=|\.\.|\|\||\*\*|[-+*/%<>=@]/}),Prism.languages.insertBefore("plsql","operator",{label:{pattern:/<<\s*\w+\s*>>/,alias:"symbol"}}); Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},Prism.languages.python["string-interpolation"].inside.interpolation.inside.rest=Prism.languages.python,Prism.languages.py=Prism.languages.python; +!function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){var e="line-numbers",n=/\n(?!$)/g,t=Prism.plugins.lineNumbers={getLine:function(n,t){if("PRE"===n.tagName&&n.classList.contains(e)){var i=n.querySelector(".line-numbers-rows");if(i){var r=parseInt(n.getAttribute("data-start"),10)||1,s=r+(i.children.length-1);ts&&(t=s);var l=t-r;return i.children[l]}}},resize:function(e){r([e])},assumeViewportIndependence:!0},i=void 0;window.addEventListener("resize",(function(){t.assumeViewportIndependence&&i===window.innerWidth||(i=window.innerWidth,r(Array.prototype.slice.call(document.querySelectorAll("pre.line-numbers"))))})),Prism.hooks.add("complete",(function(t){if(t.code){var i=t.element,s=i.parentNode;if(s&&/pre/i.test(s.nodeName)&&!i.querySelector(".line-numbers-rows")&&Prism.util.isActive(i,e)){i.classList.remove(e),s.classList.add(e);var l,o=t.code.match(n),a=o?o.length+1:1,u=new Array(a+1).join("");(l=document.createElement("span")).setAttribute("aria-hidden","true"),l.className="line-numbers-rows",l.innerHTML=u,s.hasAttribute("data-start")&&(s.style.counterReset="linenumber "+(parseInt(s.getAttribute("data-start"),10)-1)),t.element.appendChild(l),r([s]),Prism.hooks.run("line-numbers",t)}}})),Prism.hooks.add("line-numbers",(function(e){e.plugins=e.plugins||{},e.plugins.lineNumbers=!0}))}function r(e){if(0!=(e=e.filter((function(e){var n,t=(n=e,n?window.getComputedStyle?getComputedStyle(n):n.currentStyle||null:null)["white-space"];return"pre-wrap"===t||"pre-line"===t}))).length){var t=e.map((function(e){var t=e.querySelector("code"),i=e.querySelector(".line-numbers-rows");if(t&&i){var r=e.querySelector(".line-numbers-sizer"),s=t.textContent.split(n);r||((r=document.createElement("span")).className="line-numbers-sizer",t.appendChild(r)),r.innerHTML="0",r.style.display="block";var l=r.getBoundingClientRect().height;return r.innerHTML="",{element:e,lines:s,lineHeights:[],oneLinerHeight:l,sizer:r}}})).filter(Boolean);t.forEach((function(e){var n=e.sizer,t=e.lines,i=e.lineHeights,r=e.oneLinerHeight;i[t.length-1]=void 0,t.forEach((function(e,t){if(e&&e.length>1){var s=n.appendChild(document.createElement("span"));s.style.display="block",s.textContent=e}else i[t]=r}))})),t.forEach((function(e){for(var n=e.sizer,t=e.lineHeights,i=0,r=0;r `" +
✎ GitHub© Copyright 2025 by OpenPatch
" `; exports[`process > should result in two link 1`] = ` @@ -123,7 +123,7 @@ HYPERBOOK_ASSETS = "/assets/" in Python Teil des Programms .

-
© Copyright 2024
" +
© Copyright 2025
" `; exports[`process > should transform 1`] = ` @@ -201,7 +201,7 @@ HYPERBOOK_ASSETS = "/assets/" -
© Copyright 2024
" +
© Copyright 2025
" `; exports[`process > should transfrom complex context 1`] = ` @@ -262,5 +262,5 @@ HYPERBOOK_ASSETS = "assets/" -
✎ GitHub© Copyright 2024 by OpenPatch
" +
✎ GitHub© Copyright 2025 by OpenPatch
" `; diff --git a/vercel.json b/vercel.json index bd6fd8fa..4432b4f5 100644 --- a/vercel.json +++ b/vercel.json @@ -1,3 +1,13 @@ { - "cleanUrls": true + "cleanUrls": true, + "headers": [ + { + "source": "/(.*)", + "headers": [ + { "key": "Access-Control-Allow-Origin", "value": "*" }, + { "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" }, + { "key": "Cross-Origin-Opener-Policy", "value": "same-origin" } + ] + } + ] } diff --git a/website/de/book/elements/pyide.md b/website/de/book/elements/pyide.md index 9608c07a..269794c5 100644 --- a/website/de/book/elements/pyide.md +++ b/website/de/book/elements/pyide.md @@ -11,19 +11,16 @@ Dieses Element ermöglicht es Benutzern, Python-Code direkt im Browser zu schrei ````md :::pyide - ```python a = 5 + 2 print(a) ``` -::: - +::: ```` :::pyide - ```python a = 5 + 2 print(a) @@ -36,7 +33,6 @@ Sie können auch jedes Paket verwenden, das hier aufgeführt ist: https://pyodid ````md :::pyide - ```python import numpy as np @@ -49,7 +45,6 @@ print(a) :::pyide - ```python import numpy as np @@ -57,4 +52,118 @@ a = np.arange(15).reshape(3, 5) print(a) ``` +::: + +## Testfälle hinzufügen + +Sie können Testfälle zu den Code-Snippets hinzufügen, indem Sie ein `test`-Tag zum Codeblock hinzufügen. Der Kommentar `#SCRIPT#` wird durch den geschriebenen Code ersetzt. Er kann an jeder Stelle des Codeblocks platziert werden. + +````md +:::pyide + +```python test +#SCRIPT# +r = check_palindrom("uhu") +if r: + print("Pass") +else: + print("Fail") +``` + +```python test +#SCRIPT# +r = check_palindrom("test") +if not r: + print("Pass") +else: + print("Fail") +``` + +```python +def check_palindrom(s): + return True +``` + +::: +```` + +:::pyide + +```python test +#SCRIPT# +r = check_palindrom("uhu") +if r: + print("Pass") +else: + print("Fail") +``` + +```python test +#SCRIPT# +r = check_palindrom("test") +if not r: + print("Pass") +else: + print("Fail") +``` + +```python +def check_palindrom(s): + return True +``` + +::: + +## Input() + +Sie können die `input()`-Funktion in den Code-Snippets verwenden. Die `input()`-Funktion wird durch die im `input`-Tag angegebenen Werte ersetzt. +Wenn es mehrere `input()`-Funktionen gibt, werden die Werte in der Reihenfolge bereitgestellt, in der sie im `input`-Tag geschrieben sind. +Wenn Sie `input()` öfter aufrufen als die Anzahl der bereitgestellten Werte, wird ein Fehler ausgelöst. + +````md +:::pyide + +```input +a +b +c +d +``` + +```python +print(input()) +print(input()) +print(input()) +print(input()) +``` + +::: +```` + +:::pyide + +```input +a +b +c +d +``` + +```python +print(input()) +print(input()) +print(input()) +print(input()) +``` + +::: + +## Ausführung stoppen + +:::alert{warn} +Das Stoppen einer Endlosschleife oder eines lang andauernden Prozesses ist nur durch Aktualisieren der Seite oder durch Setzen dieser beiden Header auf Ihrem Server möglich: +``` +'Cross-Origin-Embedder-Policy': 'require-corp' +'Cross-Origin-Opener-Policy': 'same-origin' +``` ::: \ No newline at end of file diff --git a/website/en/book/elements/pyide.md b/website/en/book/elements/pyide.md index c158bfcc..e8dbde2e 100644 --- a/website/en/book/elements/pyide.md +++ b/website/en/book/elements/pyide.md @@ -56,4 +56,119 @@ a = np.arange(15).reshape(3, 5) print(a) ``` +::: + +## Add test cases + +You can add test cases to the code snippets by adding a `test` tag to the code block. The `#SCRIPT#` comment will be replaced by the written code. It can be placed in any part of the code block. + +````md +:::pyide + +```python test +#SCRIPT# +r = check_palindrom("uhu") +if r: +print("Pass") +else: +print("Fail") +``` + +```python test +#SCRIPT# +r = check_palindrom("test") +if not r: + print("Pass") +else: + print("Fail") +``` + +```python +def check_palindrom(s): + return True +``` + +::: + +```` + +:::pyide + +```python test +#SCRIPT# +r = check_palindrom("uhu") +if r: + print("Pass") +else: + print("Fail") +``` + +```python test +#SCRIPT# +r = check_palindrom("test") +if not r: + print("Pass") +else: + print("Fail") +``` + +```python +def check_palindrom(s): + return True +``` + +::: + +## Input() + +You can use the `input()` function in the code snippets. The `input()` function is replaced by the values provided in the `input` tag. +If there are multiple `input()` functions, the values are provided in the order they are written in the `input` tag. +If you call `input()` more times than the number of values provided, the code will throw an error. + +````md +:::pyide + +```input +a +b +c +d +``` + +```python +print(input()) +print(input()) +print(input()) +print(input()) +``` + +::: +```` + +:::pyide + +```input +a +b +c +d +``` + +```python +print(input()) +print(input()) +print(input()) +print(input()) +``` + +::: + +## Stopping the execution + +:::alert{warn} +Stopping an infinite loop or a long lasting process is only possible by refreshing the page or by setting these two headers on your server: +``` +'Cross-Origin-Embedder-Policy': 'require-corp' +'Cross-Origin-Opener-Policy': 'same-origin' +``` ::: \ No newline at end of file