diff --git a/_extensions/live/resources/live-runtime.js b/_extensions/live/resources/live-runtime.js index a3230c7..850ac55 100644 --- a/_extensions/live/resources/live-runtime.js +++ b/_extensions/live/resources/live-runtime.js @@ -27,7 +27,7 @@ var OO=Object.defineProperty;var ci=(i=>typeof require<"u"?require:typeof Proxy< `))}return $h(i,Ko.parse(i),Wr,t,n),e}function Pn(i){let e=document.createElement("code");e.className="sourceCode python";function t(r,s){let o=document.createTextNode(r);if(s){let a=document.createElement("span");a.appendChild(o),a.className=s,o=a}e.appendChild(o)}function n(){e.appendChild(document.createTextNode(` `))}return $h(i,na.parse(i),Wr,t,n),e}function Yc(i,e,t,n){if(i.textContent.includes(e)){let r=!1;for(let s of i.children)r||=Yc(s,e,t,n);if(!r)switch(i.textContent=i.textContent.replaceAll(e,()=>t),n){case"none":break;case"r":i.innerHTML=Tn(i.textContent).innerHTML;break;case"python":i.innerHTML=Pn(i.textContent).innerHTML;break;default:throw new Error(`Can't highlight interpolation, unknown language \`${n}\`.`)}return!0}return!1}var zg=new fo,Gg=new Set(["Script","Body","FunctionDefinition","ClassDefinition","LambdaExpression","ForStatement","MatchClause"]);function ra(i){return(e,t,n)=>{if(n)return!1;let r=e.node.getChild("VariableName");return r&&t(r,i),!0}}var Lk={FunctionDefinition:ra("function"),ClassDefinition:ra("class"),ForStatement(i,e,t){if(t){for(let n=i.node.firstChild;n;n=n.nextSibling)if(n.name=="VariableName")e(n,"variable");else if(n.name=="in")break}},ImportStatement(i,e){var t,n;let{node:r}=i,s=((t=r.firstChild)===null||t===void 0?void 0:t.name)=="from";for(let o=r.getChild("import");o;o=o.nextSibling)o.name=="VariableName"&&((n=o.nextSibling)===null||n===void 0?void 0:n.name)!="as"&&e(o,s?"variable":"namespace")},AssignStatement(i,e){for(let t=i.node.firstChild;t;t=t.nextSibling)if(t.name=="VariableName")e(t,"variable");else if(t.name==":"||t.name=="AssignOp")break},ParamList(i,e){for(let t=null,n=i.node.firstChild;n;n=n.nextSibling)n.name=="VariableName"&&(!t||!/\*|AssignOp/.test(t.name))&&e(n,"variable"),t=n},CapturePattern:ra("variable"),AsPattern:ra("variable"),__proto__:null};function Yg(i,e){let t=zg.get(e);if(t)return t;let n=[],r=!0;function s(o,a){let l=i.sliceString(o.from,o.to);n.push({label:l,type:a})}return e.cursor(oe.IncludeAnonymous).iterate(o=>{if(o.name){let a=Lk[o.name];if(a&&a(o,s,r)||!r&&Gg.has(o.name))return!1;r=!1}else if(o.to-o.from>8192){for(let a of Yg(i,o.node))n.push(a);return!1}}),zg.set(e,n),n}var Ug=/^[\w\xa1-\uffff][\w\d\xa1-\uffff]*$/,Zg=["String","FormatString","Comment","PropertyName"];function Vk(i){let e=he(i.state).resolveInner(i.pos,-1);if(Zg.indexOf(e.name)>-1)return null;let t=e.name=="VariableName"||e.to-e.from<20&&Ug.test(i.state.sliceDoc(e.from,e.to));if(!t&&!i.explicit)return null;let n=[];for(let r=e;r;r=r.parent)Gg.has(r.name)&&(n=n.concat(Yg(i.state.doc,r)));return{options:n,from:t?e.from:i.pos,validFor:Ug}}var qk=["__annotations__","__builtins__","__debug__","__doc__","__import__","__name__","__loader__","__package__","__spec__","False","None","True"].map(i=>({label:i,type:"constant"})).concat(["ArithmeticError","AssertionError","AttributeError","BaseException","BlockingIOError","BrokenPipeError","BufferError","BytesWarning","ChildProcessError","ConnectionAbortedError","ConnectionError","ConnectionRefusedError","ConnectionResetError","DeprecationWarning","EOFError","Ellipsis","EncodingWarning","EnvironmentError","Exception","FileExistsError","FileNotFoundError","FloatingPointError","FutureWarning","GeneratorExit","IOError","ImportError","ImportWarning","IndentationError","IndexError","InterruptedError","IsADirectoryError","KeyError","KeyboardInterrupt","LookupError","MemoryError","ModuleNotFoundError","NameError","NotADirectoryError","NotImplemented","NotImplementedError","OSError","OverflowError","PendingDeprecationWarning","PermissionError","ProcessLookupError","RecursionError","ReferenceError","ResourceWarning","RuntimeError","RuntimeWarning","StopAsyncIteration","StopIteration","SyntaxError","SyntaxWarning","SystemError","SystemExit","TabError","TimeoutError","TypeError","UnboundLocalError","UnicodeDecodeError","UnicodeEncodeError","UnicodeError","UnicodeTranslateError","UnicodeWarning","UserWarning","ValueError","Warning","ZeroDivisionError"].map(i=>({label:i,type:"type"}))).concat(["bool","bytearray","bytes","classmethod","complex","float","frozenset","int","list","map","memoryview","object","range","set","staticmethod","str","super","tuple","type"].map(i=>({label:i,type:"class"}))).concat(["abs","aiter","all","anext","any","ascii","bin","breakpoint","callable","chr","compile","delattr","dict","dir","divmod","enumerate","eval","exec","exit","filter","format","getattr","globals","hasattr","hash","help","hex","id","input","isinstance","issubclass","iter","len","license","locals","max","min","next","oct","open","ord","pow","print","property","quit","repr","reversed","round","setattr","slice","sorted","sum","vars","zip"].map(i=>({label:i,type:"function"}))),Bk=[Rt("def ${name}(${params}):\n ${}",{label:"def",detail:"function",type:"keyword"}),Rt("for ${name} in ${collection}:\n ${}",{label:"for",detail:"loop",type:"keyword"}),Rt("while ${}:\n ${}",{label:"while",detail:"loop",type:"keyword"}),Rt("try:\n ${}\nexcept ${error}:\n ${}",{label:"try",detail:"/ except block",type:"keyword"}),Rt(`if \${}: -`,{label:"if",detail:"block",type:"keyword"}),Rt("if ${}:\n ${}\nelse:\n ${}",{label:"if",detail:"/ else block",type:"keyword"}),Rt("class ${name}:\n def __init__(self, ${params}):\n ${}",{label:"class",detail:"definition",type:"keyword"}),Rt("import ${module}",{label:"import",detail:"statement",type:"keyword"}),Rt("from ${module} import ${names}",{label:"from",detail:"import",type:"keyword"})],Xk=hg(Zg,Tc(qk.concat(Bk)));function Fg(i){let{node:e,pos:t}=i,n=i.lineIndent(t,-1),r=null;for(;;){let s=e.childBefore(t);if(s)if(s.name=="Comment")t=s.from;else if(s.name=="Body")i.baseIndentFor(s)+i.unit<=n&&(r=s),e=s;else if(s.type.is("Statement"))e=s;else break;else break}return r}function Hg(i,e){let t=i.baseIndentFor(e),n=i.lineAt(i.pos,-1),r=n.from+n.text.length;return/^\s*($|#)/.test(n.text)&&i.node.tot?null:t+i.unit}var Zc=dn.define({name:"python",parser:na.configure({props:[Sr.add({Body:i=>{var e;let t=Fg(i);return(e=Hg(i,t||i.node))!==null&&e!==void 0?e:i.continue()},IfStatement:i=>/^\s*(else:|elif )/.test(i.textAfter)?i.baseIndent:i.continue(),"ForStatement WhileStatement":i=>/^\s*else:/.test(i.textAfter)?i.baseIndent:i.continue(),TryStatement:i=>/^\s*(except |finally:|else:)/.test(i.textAfter)?i.baseIndent:i.continue(),"TupleExpression ComprehensionExpression ParamList ArgList ParenthesizedExpression":Mi({closing:")"}),"DictionaryExpression DictionaryComprehensionExpression SetExpression SetComprehensionExpression":Mi({closing:"}"}),"ArrayExpression ArrayComprehensionExpression":Mi({closing:"]"}),"String FormatString":()=>null,Script:i=>{var e;let t=Fg(i);return(e=t&&Hg(i,t))!==null&&e!==void 0?e:i.continue()}}),Tr.add({"ArrayExpression DictionaryExpression SetExpression TupleExpression":vo,Body:(i,e)=>({from:i.from+1,to:i.to-(i.to==e.doc.length?0:1)})})]}),languageData:{closeBrackets:{brackets:["(","[","{","'",'"',"'''",'"""'],stringPrefixes:["f","fr","rf","r","u","b","br","rb","F","FR","RF","R","U","B","BR","RB"]},commentTokens:{line:"#"},indentOnInput:/^\s*([\}\]\)]|else:|elif |except |finally:)$/}});function Jg(){return new pn(Zc,[Zc.data.of({autocomplete:Vk}),Zc.data.of({autocomplete:Xk})])}var Wk=dn.define({parser:Ko.configure({props:[Sr.add({Block:Mi({closing:"}"}),"ParamList ArgList":Mi({closing:")"})}),Tr.add({Block:vo})]}),languageData:{closeBrackets:{brackets:["(","[","{","'",'"']},commentTokens:{line:"#"}}});function Kg(){return new pn(Wk)}var Uk={"arrow-repeat":eO(),"exclamation-circle":tO(),lightbulb:iO(),play:nO()};function Fk(){let i=document.querySelectorAll(".tab-content > .tab-pane");Array.from(i).forEach(t=>{t.innerHTML.trim()==""&&(t.classList.add("d-none"),document.querySelector(`.nav-item > a[data-bs-target="#${t.id}"]`)?.parentElement?.classList.add("d-none"))});let e=document.querySelectorAll(".tab-content");Array.from(e).forEach(t=>{if(Array.from(t.children).reduce((r,s)=>s.classList.contains("d-none")?r:r+1,0)==1){let r=t.querySelector(".tab-pane:not(.d-none)"),s=t.parentElement;s.appendChild(r),s.querySelector(".nav.nav-tabs").remove(),t.remove()}})}var sa=class{constructor(e,t){if(typeof e!="string")throw new Error("Can't create editor, `code` must be a string.");this.container=document.createElement("div"),this.code=this.initialCode=e,this.options=Object.assign({autorun:!0,completion:!0,runbutton:!0,startover:!0,localstorage:!1},t),this.storageKey=`editor-${window.location.href}#${this.options.id}`;let n=[Cg,this.extensions(),E.updateListener.of(s=>this.onViewUpdate(s))];if(t.completion&&n.push(Ho({override:[(...s)=>this.doCompletion(...s)]})),this.options.localstorage){let s=window.localStorage.getItem(this.storageKey);s&&(e=s)}this.state=N.create({doc:e,extensions:n}),this.view=new E({state:this.state});let r=this.render();this.container.oninput=s=>this.onInput(s),this.container.appendChild(r),this.container.value={code:this.options.autorun?e:null,options:this.options},this.container.value.indicator=this.indicator=new Ke({runningCallback:()=>{Array.from(this.container.getElementsByClassName("exercise-editor-eval-indicator")).forEach(s=>s.classList.remove("d-none"))},finishedCallback:()=>{Array.from(this.container.getElementsByClassName("exercise-editor-eval-indicator")).forEach(s=>s.classList.add("d-none"))},busyCallback:()=>{Array.from(this.container.getElementsByClassName("exercise-editor-btn-run-code")).forEach(s=>s.classList.add("disabled"))},idleCallback:()=>{Array.from(this.container.getElementsByClassName("exercise-editor-btn-run-code")).forEach(s=>s.classList.remove("disabled"))}})}extensions(){let e=new Dt,t=new Dt;return[Cr(Wr),e.of(Kg()),t.of(N.tabSize.of(2)),Qe.high(qt.of([{key:"Mod-Enter",run:()=>(this.container.value.code=this.code,this.container.dispatchEvent(new CustomEvent("input",{detail:{manual:!0}})),!0)},{key:"Mod-Shift-m",run:()=>(this.view.dispatch({changes:{from:0,to:this.view.state.doc.length,insert:this.code.trimEnd()+" |> "}}),!0)}]))]}onInput(e){if(this.options.runbutton&&!e.detail.manual){e.preventDefault(),e.stopImmediatePropagation();return}this.options.localstorage&&window.localStorage.setItem(this.storageKey,this.code)}onViewUpdate(e){e.docChanged&&(this.code=e.state.doc.toString(),this.container.value.code=this.code,this.container.dispatchEvent(new CustomEvent("input",{detail:{manual:!1}})))}renderButton(e){let t=document.createElement("a"),n=document.createElement("span");return t.className=`d-flex align-items-center gap-1 btn btn-exercise-editor ${e.className} text-nowrap`,t.setAttribute("role","button"),t.setAttribute("aria-label",e.text),n.className="btn-label-exercise-editor",n.innerText=e.text,t.innerHTML=Uk[e.icon],t.appendChild(n),t.onclick=e.onclick||null,t.onkeydown=e.onclick||null,t}renderButtonGroup(e){let t=document.createElement("div");return t.className="btn-group btn-group-exercise-editor btn-group-sm",e.forEach(n=>t.appendChild(n)),t}renderSpinner(){let e=document.createElement("div");return e.className="exercise-editor-eval-indicator spinner-grow spinner-grow-sm",e.setAttribute("role","status"),e}renderHintButton(e,t){return Array.from(e).reduceRight((n,r,s,o)=>this.renderButton({text:s===0?"Show Hint":"Next Hint",icon:"lightbulb",className:"btn-outline-dark btn-sm",onclick:function(){s>0&&o[s-1].classList.add("d-none"),r.classList.remove("d-none"),n?this.replaceWith(n):this.remove()}}),t)}renderSolutionButton(e,t){return this.renderButton({text:"Show Solution",icon:"exclamation-circle",className:"btn-exercise-solution btn-outline-dark btn-sm",onclick:function(){t&&t.forEach(n=>n.classList.add("d-none")),Array.from(e).forEach(n=>{n.classList.remove("d-none")}),this.remove()}})}renderHintsTabset(e,t){let n=new Set;e.forEach(s=>{let o=s.parentElement;o.id.includes("tabset-")&&n.add(o)});let r=new Set;return t.forEach(s=>{let o=s.parentElement;o.id.includes("tabset-")&&r.add(o)}),n.forEach(s=>{let o=document.createElement("div");o.className="d-flex justify-content-between exercise-tab-pane-header";let a=s.querySelectorAll(`.exercise-hint[data-exercise="${this.options.exercise}"]`);o.appendChild(this.renderHintButton(a,null)),s.prepend(o)}),r.forEach(s=>{let o=document.createElement("div");o.className="d-flex justify-content-between exercise-tab-pane-header";let a=s.querySelectorAll(`.exercise-solution[data-exercise="${this.options.exercise}"]`);o.appendChild(this.renderSolutionButton(a,null)),s.prepend(o)}),null}renderHints(){let e=document.querySelectorAll(`.d-none.exercise-hint[data-exercise="${this.options.exercise}"]`),t=document.querySelectorAll(`.d-none.exercise-solution[data-exercise="${this.options.exercise}"]`),n=Array.from(e).some(s=>s.parentElement.id.includes("tabset-"))||Array.from(t).some(s=>s.parentElement.id.includes("tabset-")),r=null;if(n)this.renderHintsTabset(e,t);else{let s;t.length>0&&(s=this.renderSolutionButton(t,e)),r=this.renderHintButton(e,s)}return Fk(),r}render(){let e=document.createElement("div"),t=document.createElement("div"),n=document.createElement("div");e.className="card exercise-editor my-3",t.className="card-header exercise-editor-header d-flex justify-content-between",n.className="card-body exercise-editor-body p-0";let r=document.createElement("div");r.className="d-flex align-items-center gap-3";let s=document.createElement("div");s.innerHTML="caption"in this.options?this.options.caption:this.defaultCaption,r.appendChild(s);let o=[];this.options.startover&&o.push(this.renderButton({text:"Start Over",icon:"arrow-repeat",className:"btn-outline-dark",onclick:()=>{this.view.dispatch({changes:{from:0,to:this.view.state.doc.length,insert:this.initialCode}}),this.options.runbutton&&(this.container.value.code=null,this.options.autorun&&(this.container.value.code=this.initialCode),this.container.dispatchEvent(new CustomEvent("input",{detail:{manual:!0}})))}}));let a=this.renderHints();a&&o.push(a),o.length>0&&r.appendChild(this.renderButtonGroup(o)),t.appendChild(r);let l=document.createElement("div");l.className="d-flex align-items-center gap-3";let h=[];return this.options.runbutton&&h.push(this.renderButton({text:"Run Code",icon:"play",className:"btn-primary disabled exercise-editor-btn-run-code",onclick:()=>{this.container.value.code=this.code,this.container.dispatchEvent(new CustomEvent("input",{detail:{manual:!0}}))}})),l.appendChild(this.renderSpinner()),h.length>0&&l.appendChild(this.renderButtonGroup(h)),t.appendChild(l),e.appendChild(t),n.appendChild(this.view.dom),e.appendChild(n),e}},oa=class extends sa{constructor(e,t,n){super(t,n),this.webRPromise=e,this.completionMethods=this.setupCompletion()}render(){return this.defaultCaption="R Code",super.render()}async setupCompletion(){let e=await this.webRPromise;return await e.evalRVoid("rc.settings(func=TRUE, fuzzy=TRUE)"),{assignLineBuffer:await e.evalR("utils:::.assignLinebuffer"),assignToken:await e.evalR("utils:::.assignToken"),assignStart:await e.evalR("utils:::.assignStart"),assignEnd:await e.evalR("utils:::.assignEnd"),completeToken:await e.evalR("utils:::.completeToken"),retrieveCompletions:await e.evalR("utils:::.retrieveCompletions")}}async doCompletion(e){let t=await this.completionMethods,n=e.state.doc.lineAt(e.state.selection.main.head).text,{from:r,to:s,text:o}=e.matchBefore(/[a-zA-Z0-9_.:]*/)??{from:0,to:0,text:""};if(r===s&&!e.explicit)return null;await t.assignLineBuffer(n.replace(/\)+$/,"")),await t.assignToken(o),await t.assignStart(r+1),await t.assignEnd(s+1),await t.completeToken();let l=(await t.retrieveCompletions()).values.map(h=>{if(!h)throw new Error("Missing values in completion result.");return{label:h,boost:h.endsWith("=")?10:0}});return{from:r,options:l}}},aa=class extends sa{constructor(e,t,n){super(t,n),this.pyodidePromise=e,this.setupCompletion()}render(){return this.defaultCaption="Python Code",super.render()}extensions(){let e=new Dt,t=new Dt;return[Cr(Wr),e.of(Jg()),t.of(N.tabSize.of(2)),Qe.high(qt.of([{key:"Mod-Enter",run:()=>(this.container.value.code=this.code,this.container.dispatchEvent(new CustomEvent("input",{detail:{manual:!0}})),!0)}]))]}async setupCompletion(){let e=await this.pyodidePromise}async doCompletion(e){return null}};function Cn(i){for(var e="",t=new Uint8Array(i),n=t.byteLength,r=0;rString.fromCharCode(parseInt(t,16))))}function sO(i){return decodeURIComponent(atob(i).split("").map(e=>"%"+("00"+e.charCodeAt(0).toString(16)).slice(-2)).join(""))}function Jc(i){return typeof ImageBitmap<"u"&&i instanceof ImageBitmap}function Ir(i,e,t,...n){return i==null||Jc(i)||i instanceof ArrayBuffer||ArrayBuffer.isView(i)?i:e(i)?t(i,...n):Array.isArray(i)?i.map(r=>Ir(r,e,t,...n)):typeof i=="object"?Object.fromEntries(Object.entries(i).map(([r,s])=>[r,Ir(s,e,t,...n)])):i}var oO=[];async function Nr(i){let e=await i.toJs({depth:-1}),t=await Promise.all(e.names.map(async(n,r)=>[n,await e.values[r].toString()]));return Object.fromEntries(t)}async function Kc(i,e){let t=await i.toJs({depth:-1});return await Promise.all(t.values.map(async n=>({[e]:await n.toString()})))}async function ef(i){let e=await i.toJs({depth:-1});return await Promise.all(e.values.map(t=>Nr(t)))}async function Hk(i){if(!(await(await i.class()).toArray()).includes("html_dependency"))throw new Error("Can't interpret R object of class `${classes}` as HTML dependency.");let t=await i.get("attachment"),n=await i.get("head"),r=await i.get("meta"),s=await(await i.get("name")).toString(),o=await i.get("package"),a=await i.get("restyle"),l=await i.get("script"),h=await i.get("src"),c=await i.get("stylesheet"),f=await(await i.get("version")).toString(),u={attachment:[],head:Re(n)?void 0:await n.toString(),meta:[],name:s,pkg:Re(o)?void 0:await o.toString(),restyle:Re(a)?void 0:await a.toBoolean(),script:[],src:{},stylesheet:[],version:f};if(Ui(h)?u.src={file:await h.toString()}:ot(h)&&(u.src=await Nr(h)),!Re(r)){let d=await r.toObject();u.meta=await Promise.all(Object.entries(d).map(async([m,p])=>({name:m,content:await p.toString()})))}if(Ui(c))u.stylesheet=(await c.toArray()).map(d=>({href:d}));else if(ot(c)){let d=await c.toJs({depth:-1});d.names?d.names.includes("href")?u.stylesheet=[await Nr(c)]:u.stylesheet=await ef(c):u.stylesheet=await Kc(c,"href")}if(Ui(l))u.script=(await l.toArray()).map(d=>({src:d}));else if(ot(l)){let d=await l.toJs({depth:-1});d.names?d.names.includes("src")?u.script=[await Nr(l)]:u.script=await ef(l):u.script=await Kc(l,"src")}if(Ui(t))u.attachment=(await t.toArray()).map((d,m)=>({key:(m+1).toString(),href:d}));else if(ot(t)){let d=await t.toJs({depth:-1});d.names?d.names.includes("href")?(u.attachment=[await Nr(t)],u.attachment[0].key="1"):(u.attachment=await ef(t),u.attachment.forEach((m,p)=>{m.key=(p+1).toString()})):(u.attachment=await Kc(t,"href"),u.attachment.forEach((m,p)=>{m.key=(p+1).toString()}))}return u}async function tf(i,e){let t=await Hk(e),n=t.pkg?await i.evalRString(`find.package("${t.pkg}")`):"";if(t.name in oO)return!1;if(oO[t.name]=t.version,t.head){let r=document.createElement("div");r.innerHTML=t.head,r.childNodes.forEach(s=>document.head.appendChild(s))}if(t.meta&&t.meta.forEach(async r=>{let s=document.createElement("meta");Object.entries(r).map(([o,a])=>{s.setAttribute(o,a||"")}),document.head.appendChild(s)}),t.stylesheet&&t.stylesheet.forEach(async r=>{let s=document.createElement("link");if(t.src.file){let o=await i.FS.readFile(`${n}/${t.src.file}/${r.href}`);r.href=`data:text/css;base64,${Cn(o)}`}else r.href=`${t.src.href}/${r.href}`;r.rel||(s.rel="stylesheet"),r.type||(s.type="text/css"),Object.entries(r).map(([o,a])=>{s.setAttribute(o,a||"")}),document.head.appendChild(s)}),t.script){let r=t.script.map(async s=>{let o=document.createElement("script");if(t.src.file){let l=await i.FS.readFile(`${n}/${t.src.file}/${s.src}`);s.src=`data:text/javascript;base64,${Cn(l)}`}else s.src=`${t.src.href}/${s.src}`;o.async=!1,Object.entries(s).map(([l,h])=>{l==="async"&&(o.async=h==="true"),o.setAttribute(l,h||"")});let a=new Promise((l,h)=>{o.onload=()=>l(null),o.onerror=c=>h(c)});return document.head.appendChild(o),a});await Promise.allSettled(r)}return!0}var la=class{constructor(e,t,n){this.container=document.createElement("div"),this.nullResult={result:null,evaluate_result:null,evaluator:this},this.container.value=this.nullResult,this.webR=e,this.context=n,this.shelter=new e.Shelter,this.options=Object.assign({envir:"global",eval:!0,echo:!1,warning:!0,error:!0,include:!0,output:!0,timelimit:30},n.options),!this.options.exercise||this.options.envir==="global"?this.envLabels={prep:this.options.envir,result:this.options.envir,grading:this.options.envir,solution:this.options.envir}:this.envLabels={prep:`${this.options.envir}-prep`,result:`${this.options.envir}-result`,grading:`${this.options.envir}-grading`,solution:`${this.options.envir}-solution`},this.envManager=t}async purge(){(await this.shelter).purge()}getSetupCode(){let e=this.options.exercise,t=document.querySelectorAll(`script[type="exercise-setup-${e}-contents"]`);if(t.length>0)return t.length>1&&console.warn(`Multiple \`setup\` blocks found for exercise "${e}", using the first.`),JSON.parse(atob(t[0].textContent)).code}async process(e){if(!this.options.eval){this.container=this.asSourceHTML(this.context.code),this.container.value=this.nullResult;return}if(this.options.exercise&&this.context.code&&this.context.code.match(/_{6}_*/g)){this.container.value.result=null;return}let t=this.context.indicator;this.context.indicator||(t=new Ke),t.running();try{await Promise.all(Object.entries(e).map(async([l,h])=>{await this.envManager.bind(l,h,this.envLabels.prep)}));let n=this.getSetupCode();await this.evaluate(n,"prep"),await this.envManager.create(this.envLabels.result,this.envLabels.prep);let r=await this.evaluate(this.context.code,"result");if(!r)this.container.value.result=null;else if(this.options.output==="asis"){let l=await r.toArray(),h=await l[l.length-1].get("value");this.container.innerHTML=await h.toString()}else if(this.container=await this.asHtml(r),!this.options.output){let l=this.container.value;this.container=document.createElement("div"),this.container.value=l}let s=await this.envManager.get(this.envLabels.result),a=await(await this.webR.objs.globalEnv.get(".webr_ojs")).toObject({depth:-1});typeof this.options.define=="string"?a[this.options.define]=await s.get(this.options.define):this.options.define&&Object.assign(a,Object.fromEntries(await Promise.all(this.options.define.map(async l=>{let h=await s.get(l);return[l,h]})))),Object.keys(a).forEach(async l=>{let h=await this.asOjs(a[l]);window._ojs.ojsConnector.mainModule._scope.has(l)?window._ojs.ojsConnector.mainModule.redefine(l,()=>h):window._ojs.ojsConnector.define(l)(h)}),await this.webR.evalRVoid("rm(list = ls(.webr_ojs), envir = .webr_ojs)")}finally{this.purge(),t.finished(),this.context.indicator||t.destroy()}}async evaluate(e,t,n=this.options){return e==null||!n.include?null:(await(await this.shelter).captureR(` +`,{label:"if",detail:"block",type:"keyword"}),Rt("if ${}:\n ${}\nelse:\n ${}",{label:"if",detail:"/ else block",type:"keyword"}),Rt("class ${name}:\n def __init__(self, ${params}):\n ${}",{label:"class",detail:"definition",type:"keyword"}),Rt("import ${module}",{label:"import",detail:"statement",type:"keyword"}),Rt("from ${module} import ${names}",{label:"from",detail:"import",type:"keyword"})],Xk=hg(Zg,Tc(qk.concat(Bk)));function Fg(i){let{node:e,pos:t}=i,n=i.lineIndent(t,-1),r=null;for(;;){let s=e.childBefore(t);if(s)if(s.name=="Comment")t=s.from;else if(s.name=="Body")i.baseIndentFor(s)+i.unit<=n&&(r=s),e=s;else if(s.type.is("Statement"))e=s;else break;else break}return r}function Hg(i,e){let t=i.baseIndentFor(e),n=i.lineAt(i.pos,-1),r=n.from+n.text.length;return/^\s*($|#)/.test(n.text)&&i.node.tot?null:t+i.unit}var Zc=dn.define({name:"python",parser:na.configure({props:[Sr.add({Body:i=>{var e;let t=Fg(i);return(e=Hg(i,t||i.node))!==null&&e!==void 0?e:i.continue()},IfStatement:i=>/^\s*(else:|elif )/.test(i.textAfter)?i.baseIndent:i.continue(),"ForStatement WhileStatement":i=>/^\s*else:/.test(i.textAfter)?i.baseIndent:i.continue(),TryStatement:i=>/^\s*(except |finally:|else:)/.test(i.textAfter)?i.baseIndent:i.continue(),"TupleExpression ComprehensionExpression ParamList ArgList ParenthesizedExpression":Mi({closing:")"}),"DictionaryExpression DictionaryComprehensionExpression SetExpression SetComprehensionExpression":Mi({closing:"}"}),"ArrayExpression ArrayComprehensionExpression":Mi({closing:"]"}),"String FormatString":()=>null,Script:i=>{var e;let t=Fg(i);return(e=t&&Hg(i,t))!==null&&e!==void 0?e:i.continue()}}),Tr.add({"ArrayExpression DictionaryExpression SetExpression TupleExpression":vo,Body:(i,e)=>({from:i.from+1,to:i.to-(i.to==e.doc.length?0:1)})})]}),languageData:{closeBrackets:{brackets:["(","[","{","'",'"',"'''",'"""'],stringPrefixes:["f","fr","rf","r","u","b","br","rb","F","FR","RF","R","U","B","BR","RB"]},commentTokens:{line:"#"},indentOnInput:/^\s*([\}\]\)]|else:|elif |except |finally:)$/}});function Jg(){return new pn(Zc,[Zc.data.of({autocomplete:Vk}),Zc.data.of({autocomplete:Xk})])}var Wk=dn.define({parser:Ko.configure({props:[Sr.add({Block:Mi({closing:"}"}),"ParamList ArgList":Mi({closing:")"})}),Tr.add({Block:vo})]}),languageData:{closeBrackets:{brackets:["(","[","{","'",'"']},commentTokens:{line:"#"}}});function Kg(){return new pn(Wk)}var Uk={"arrow-repeat":eO(),"exclamation-circle":tO(),lightbulb:iO(),play:nO()};function Fk(){let i=document.querySelectorAll(".tab-content > .tab-pane");Array.from(i).forEach(t=>{t.innerHTML.trim()==""&&(t.classList.add("d-none"),document.querySelector(`.nav-item > a[data-bs-target="#${t.id}"]`)?.parentElement?.classList.add("d-none"))});let e=document.querySelectorAll(".tab-content");Array.from(e).forEach(t=>{if(Array.from(t.children).reduce((r,s)=>s.classList.contains("d-none")?r:r+1,0)==1){let r=t.querySelector(".tab-pane:not(.d-none)"),s=t.parentElement;s.appendChild(r),s.querySelector(".nav.nav-tabs").remove(),t.remove()}})}var sa=class{constructor(e,t){if(typeof e!="string")throw new Error("Can't create editor, `code` must be a string.");this.container=document.createElement("div"),this.code=this.initialCode=e,this.options=Object.assign({autorun:!0,completion:!0,runbutton:!0,startover:!0,localstorage:!1},t),this.storageKey=`editor-${window.location.href}#${this.options.id}`;let n=[Cg,this.extensions(),E.updateListener.of(s=>this.onViewUpdate(s))];if(t.completion&&n.push(Ho({override:[(...s)=>this.doCompletion(...s)]})),this.options.localstorage){let s=window.localStorage.getItem(this.storageKey);s&&(e=s)}this.state=N.create({doc:e,extensions:n}),this.view=new E({state:this.state});let r=this.render();this.container.oninput=s=>this.onInput(s),this.container.appendChild(r),this.container.value={code:this.options.autorun?e:null,options:this.options},this.container.value.indicator=this.indicator=new Ke({runningCallback:()=>{Array.from(this.container.getElementsByClassName("exercise-editor-eval-indicator")).forEach(s=>s.classList.remove("d-none"))},finishedCallback:()=>{Array.from(this.container.getElementsByClassName("exercise-editor-eval-indicator")).forEach(s=>s.classList.add("d-none"))},busyCallback:()=>{Array.from(this.container.getElementsByClassName("exercise-editor-btn-run-code")).forEach(s=>s.classList.add("disabled"))},idleCallback:()=>{Array.from(this.container.getElementsByClassName("exercise-editor-btn-run-code")).forEach(s=>s.classList.remove("disabled"))}})}extensions(){let e=new Dt,t=new Dt;return[Cr(Wr),e.of(Kg()),t.of(N.tabSize.of(2)),Qe.high(qt.of([{key:"Mod-Enter",run:()=>(this.container.value.code=this.code,this.container.dispatchEvent(new CustomEvent("input",{detail:{manual:!0}})),!0)},{key:"Mod-Shift-m",run:()=>(this.view.dispatch({changes:{from:0,to:this.view.state.doc.length,insert:this.code.trimEnd()+" |> "}}),!0)}]))]}onInput(e){if(this.options.runbutton&&!e.detail.manual){e.preventDefault(),e.stopImmediatePropagation();return}this.code=this.view.state.doc.toString(),this.container.value.code=this.code,this.options.localstorage&&window.localStorage.setItem(this.storageKey,this.code)}onViewUpdate(e){e.docChanged&&this.container.dispatchEvent(new CustomEvent("input",{detail:{manual:!1}}))}renderButton(e){let t=document.createElement("a"),n=document.createElement("span");return t.className=`d-flex align-items-center gap-1 btn btn-exercise-editor ${e.className} text-nowrap`,t.setAttribute("role","button"),t.setAttribute("aria-label",e.text),n.className="btn-label-exercise-editor",n.innerText=e.text,t.innerHTML=Uk[e.icon],t.appendChild(n),t.onclick=e.onclick||null,t.onkeydown=e.onclick||null,t}renderButtonGroup(e){let t=document.createElement("div");return t.className="btn-group btn-group-exercise-editor btn-group-sm",e.forEach(n=>t.appendChild(n)),t}renderSpinner(){let e=document.createElement("div");return e.className="exercise-editor-eval-indicator spinner-grow spinner-grow-sm",e.setAttribute("role","status"),e}renderHintButton(e,t){return Array.from(e).reduceRight((n,r,s,o)=>this.renderButton({text:s===0?"Show Hint":"Next Hint",icon:"lightbulb",className:"btn-outline-dark btn-sm",onclick:function(){s>0&&o[s-1].classList.add("d-none"),r.classList.remove("d-none"),n?this.replaceWith(n):this.remove()}}),t)}renderSolutionButton(e,t){return this.renderButton({text:"Show Solution",icon:"exclamation-circle",className:"btn-exercise-solution btn-outline-dark btn-sm",onclick:function(){t&&t.forEach(n=>n.classList.add("d-none")),Array.from(e).forEach(n=>{n.classList.remove("d-none")}),this.remove()}})}renderHintsTabset(e,t){let n=new Set;e.forEach(s=>{let o=s.parentElement;o.id.includes("tabset-")&&n.add(o)});let r=new Set;return t.forEach(s=>{let o=s.parentElement;o.id.includes("tabset-")&&r.add(o)}),n.forEach(s=>{let o=document.createElement("div");o.className="d-flex justify-content-between exercise-tab-pane-header";let a=s.querySelectorAll(`.exercise-hint[data-exercise="${this.options.exercise}"]`);o.appendChild(this.renderHintButton(a,null)),s.prepend(o)}),r.forEach(s=>{let o=document.createElement("div");o.className="d-flex justify-content-between exercise-tab-pane-header";let a=s.querySelectorAll(`.exercise-solution[data-exercise="${this.options.exercise}"]`);o.appendChild(this.renderSolutionButton(a,null)),s.prepend(o)}),null}renderHints(){let e=document.querySelectorAll(`.d-none.exercise-hint[data-exercise="${this.options.exercise}"]`),t=document.querySelectorAll(`.d-none.exercise-solution[data-exercise="${this.options.exercise}"]`),n=Array.from(e).some(s=>s.parentElement.id.includes("tabset-"))||Array.from(t).some(s=>s.parentElement.id.includes("tabset-")),r=null;if(n)this.renderHintsTabset(e,t);else{let s;t.length>0&&(s=this.renderSolutionButton(t,e)),r=this.renderHintButton(e,s)}return Fk(),r}render(){let e=document.createElement("div"),t=document.createElement("div"),n=document.createElement("div");e.className="card exercise-editor my-3",t.className="card-header exercise-editor-header d-flex justify-content-between",n.className="card-body exercise-editor-body p-0";let r=document.createElement("div");r.className="d-flex align-items-center gap-3";let s=document.createElement("div");s.innerHTML="caption"in this.options?this.options.caption:this.defaultCaption,r.appendChild(s);let o=[];this.options.startover&&o.push(this.renderButton({text:"Start Over",icon:"arrow-repeat",className:"btn-outline-dark",onclick:()=>{this.view.dispatch({changes:{from:0,to:this.view.state.doc.length,insert:this.initialCode}}),this.options.runbutton&&(this.container.value.code=null,this.options.autorun&&(this.container.value.code=this.initialCode),this.container.dispatchEvent(new CustomEvent("input",{detail:{manual:!0}})))}}));let a=this.renderHints();a&&o.push(a),o.length>0&&r.appendChild(this.renderButtonGroup(o)),t.appendChild(r);let l=document.createElement("div");l.className="d-flex align-items-center gap-3";let h=[];return this.options.runbutton&&h.push(this.renderButton({text:"Run Code",icon:"play",className:"btn-primary disabled exercise-editor-btn-run-code",onclick:()=>{this.container.value.code=this.code,this.container.dispatchEvent(new CustomEvent("input",{detail:{manual:!0}}))}})),l.appendChild(this.renderSpinner()),h.length>0&&l.appendChild(this.renderButtonGroup(h)),t.appendChild(l),e.appendChild(t),n.appendChild(this.view.dom),e.appendChild(n),e}},oa=class extends sa{constructor(e,t,n){super(t,n),this.webRPromise=e,this.completionMethods=this.setupCompletion()}render(){return this.defaultCaption="R Code",super.render()}async setupCompletion(){let e=await this.webRPromise;return await e.evalRVoid("rc.settings(func=TRUE, fuzzy=TRUE)"),{assignLineBuffer:await e.evalR("utils:::.assignLinebuffer"),assignToken:await e.evalR("utils:::.assignToken"),assignStart:await e.evalR("utils:::.assignStart"),assignEnd:await e.evalR("utils:::.assignEnd"),completeToken:await e.evalR("utils:::.completeToken"),retrieveCompletions:await e.evalR("utils:::.retrieveCompletions")}}async doCompletion(e){let t=await this.completionMethods,n=e.state.doc.lineAt(e.state.selection.main.head).text,{from:r,to:s,text:o}=e.matchBefore(/[a-zA-Z0-9_.:]*/)??{from:0,to:0,text:""};if(r===s&&!e.explicit)return null;await t.assignLineBuffer(n.replace(/\)+$/,"")),await t.assignToken(o),await t.assignStart(r+1),await t.assignEnd(s+1),await t.completeToken();let l=(await t.retrieveCompletions()).values.map(h=>{if(!h)throw new Error("Missing values in completion result.");return{label:h,boost:h.endsWith("=")?10:0}});return{from:r,options:l}}},aa=class extends sa{constructor(e,t,n){super(t,n),this.pyodidePromise=e,this.setupCompletion()}render(){return this.defaultCaption="Python Code",super.render()}extensions(){let e=new Dt,t=new Dt;return[Cr(Wr),e.of(Jg()),t.of(N.tabSize.of(2)),Qe.high(qt.of([{key:"Mod-Enter",run:()=>(this.container.value.code=this.code,this.container.dispatchEvent(new CustomEvent("input",{detail:{manual:!0}})),!0)}]))]}async setupCompletion(){let e=await this.pyodidePromise}async doCompletion(e){return null}};function Cn(i){for(var e="",t=new Uint8Array(i),n=t.byteLength,r=0;rString.fromCharCode(parseInt(t,16))))}function sO(i){return decodeURIComponent(atob(i).split("").map(e=>"%"+("00"+e.charCodeAt(0).toString(16)).slice(-2)).join(""))}function Jc(i){return typeof ImageBitmap<"u"&&i instanceof ImageBitmap}function Ir(i,e,t,...n){return i==null||Jc(i)||i instanceof ArrayBuffer||ArrayBuffer.isView(i)?i:e(i)?t(i,...n):Array.isArray(i)?i.map(r=>Ir(r,e,t,...n)):typeof i=="object"?Object.fromEntries(Object.entries(i).map(([r,s])=>[r,Ir(s,e,t,...n)])):i}var oO=[];async function Nr(i){let e=await i.toJs({depth:-1}),t=await Promise.all(e.names.map(async(n,r)=>[n,await e.values[r].toString()]));return Object.fromEntries(t)}async function Kc(i,e){let t=await i.toJs({depth:-1});return await Promise.all(t.values.map(async n=>({[e]:await n.toString()})))}async function ef(i){let e=await i.toJs({depth:-1});return await Promise.all(e.values.map(t=>Nr(t)))}async function Hk(i){if(!(await(await i.class()).toArray()).includes("html_dependency"))throw new Error("Can't interpret R object of class `${classes}` as HTML dependency.");let t=await i.get("attachment"),n=await i.get("head"),r=await i.get("meta"),s=await(await i.get("name")).toString(),o=await i.get("package"),a=await i.get("restyle"),l=await i.get("script"),h=await i.get("src"),c=await i.get("stylesheet"),f=await(await i.get("version")).toString(),u={attachment:[],head:Re(n)?void 0:await n.toString(),meta:[],name:s,pkg:Re(o)?void 0:await o.toString(),restyle:Re(a)?void 0:await a.toBoolean(),script:[],src:{},stylesheet:[],version:f};if(Ui(h)?u.src={file:await h.toString()}:ot(h)&&(u.src=await Nr(h)),!Re(r)){let d=await r.toObject();u.meta=await Promise.all(Object.entries(d).map(async([m,p])=>({name:m,content:await p.toString()})))}if(Ui(c))u.stylesheet=(await c.toArray()).map(d=>({href:d}));else if(ot(c)){let d=await c.toJs({depth:-1});d.names?d.names.includes("href")?u.stylesheet=[await Nr(c)]:u.stylesheet=await ef(c):u.stylesheet=await Kc(c,"href")}if(Ui(l))u.script=(await l.toArray()).map(d=>({src:d}));else if(ot(l)){let d=await l.toJs({depth:-1});d.names?d.names.includes("src")?u.script=[await Nr(l)]:u.script=await ef(l):u.script=await Kc(l,"src")}if(Ui(t))u.attachment=(await t.toArray()).map((d,m)=>({key:(m+1).toString(),href:d}));else if(ot(t)){let d=await t.toJs({depth:-1});d.names?d.names.includes("href")?(u.attachment=[await Nr(t)],u.attachment[0].key="1"):(u.attachment=await ef(t),u.attachment.forEach((m,p)=>{m.key=(p+1).toString()})):(u.attachment=await Kc(t,"href"),u.attachment.forEach((m,p)=>{m.key=(p+1).toString()}))}return u}async function tf(i,e){let t=await Hk(e),n=t.pkg?await i.evalRString(`find.package("${t.pkg}")`):"";if(t.name in oO)return!1;if(oO[t.name]=t.version,t.head){let r=document.createElement("div");r.innerHTML=t.head,r.childNodes.forEach(s=>document.head.appendChild(s))}if(t.meta&&t.meta.forEach(async r=>{let s=document.createElement("meta");Object.entries(r).map(([o,a])=>{s.setAttribute(o,a||"")}),document.head.appendChild(s)}),t.stylesheet&&t.stylesheet.forEach(async r=>{let s=document.createElement("link");if(t.src.file){let o=await i.FS.readFile(`${n}/${t.src.file}/${r.href}`);r.href=`data:text/css;base64,${Cn(o)}`}else r.href=`${t.src.href}/${r.href}`;r.rel||(s.rel="stylesheet"),r.type||(s.type="text/css"),Object.entries(r).map(([o,a])=>{s.setAttribute(o,a||"")}),document.head.appendChild(s)}),t.script){let r=t.script.map(async s=>{let o=document.createElement("script");if(t.src.file){let l=await i.FS.readFile(`${n}/${t.src.file}/${s.src}`);s.src=`data:text/javascript;base64,${Cn(l)}`}else s.src=`${t.src.href}/${s.src}`;o.async=!1,Object.entries(s).map(([l,h])=>{l==="async"&&(o.async=h==="true"),o.setAttribute(l,h||"")});let a=new Promise((l,h)=>{o.onload=()=>l(null),o.onerror=c=>h(c)});return document.head.appendChild(o),a});await Promise.allSettled(r)}return!0}var la=class{constructor(e,t,n){this.container=document.createElement("div"),this.nullResult={result:null,evaluate_result:null,evaluator:this},this.container.value=this.nullResult,this.webR=e,this.context=n,this.shelter=new e.Shelter,this.options=Object.assign({envir:"global",eval:!0,echo:!1,warning:!0,error:!0,include:!0,output:!0,timelimit:30},n.options),!this.options.exercise||this.options.envir==="global"?this.envLabels={prep:this.options.envir,result:this.options.envir,grading:this.options.envir,solution:this.options.envir}:this.envLabels={prep:`${this.options.envir}-prep`,result:`${this.options.envir}-result`,grading:`${this.options.envir}-grading`,solution:`${this.options.envir}-solution`},this.envManager=t}async purge(){(await this.shelter).purge()}getSetupCode(){let e=this.options.exercise,t=document.querySelectorAll(`script[type="exercise-setup-${e}-contents"]`);if(t.length>0)return t.length>1&&console.warn(`Multiple \`setup\` blocks found for exercise "${e}", using the first.`),JSON.parse(atob(t[0].textContent)).code}async process(e){if(!this.options.eval){this.container=this.asSourceHTML(this.context.code),this.container.value=this.nullResult;return}if(this.options.exercise&&this.context.code&&this.context.code.match(/_{6}_*/g)){this.container.value.result=null;return}let t=this.context.indicator;this.context.indicator||(t=new Ke),t.running();try{await Promise.all(Object.entries(e).map(async([l,h])=>{await this.envManager.bind(l,h,this.envLabels.prep)}));let n=this.getSetupCode();await this.evaluate(n,"prep"),await this.envManager.create(this.envLabels.result,this.envLabels.prep);let r=await this.evaluate(this.context.code,"result");if(!r)this.container.value.result=null;else if(this.options.output==="asis"){let l=await r.toArray(),h=await l[l.length-1].get("value");this.container.innerHTML=await h.toString()}else if(this.container=await this.asHtml(r),!this.options.output){let l=this.container.value;this.container=document.createElement("div"),this.container.value=l}let s=await this.envManager.get(this.envLabels.result),a=await(await this.webR.objs.globalEnv.get(".webr_ojs")).toObject({depth:-1});typeof this.options.define=="string"?a[this.options.define]=await s.get(this.options.define):this.options.define&&Object.assign(a,Object.fromEntries(await Promise.all(this.options.define.map(async l=>{let h=await s.get(l);return[l,h]})))),Object.keys(a).forEach(async l=>{let h=await this.asOjs(a[l]);window._ojs.ojsConnector.mainModule._scope.has(l)?window._ojs.ojsConnector.mainModule.redefine(l,()=>h):window._ojs.ojsConnector.define(l)(h)}),await this.webR.evalRVoid("rm(list = ls(.webr_ojs), envir = .webr_ojs)")}finally{this.purge(),t.finished(),this.context.indicator||t.destroy()}}async evaluate(e,t,n=this.options){return e==null||!n.include?null:(await(await this.shelter).captureR(` setTimeLimit(elapsed = timelimit) on.exit(setTimeLimit(elapsed = Inf)) evaluate::evaluate( diff --git a/live-runtime/src/editor.ts b/live-runtime/src/editor.ts index c11ab13..708709c 100644 --- a/live-runtime/src/editor.ts +++ b/live-runtime/src/editor.ts @@ -220,6 +220,10 @@ abstract class ExerciseEditor { return; } + // Update reactive value for code contents + this.code = this.view.state.doc.toString(); + this.container.value.code = this.code; + // Store latest updates to editor content to local browser storage if (this.options.localstorage) { window.localStorage.setItem(this.storageKey, this.code); @@ -228,10 +232,6 @@ abstract class ExerciseEditor { onViewUpdate(update: ViewUpdate) { if (!update.docChanged) return; - - this.code = update.state.doc.toString(); - this.container.value.code = this.code; - this.container.dispatchEvent( new CustomEvent('input', { detail: { manual: false } }) );