2 lines
14 KiB
JavaScript
Executable File
2 lines
14 KiB
JavaScript
Executable File
"use strict";(self.webpackChunkWebComponents=self.webpackChunkWebComponents||[]).push([[566],{14566:(t,e,n)=>{n.r(e),n.d(e,{MatchingProblem:()=>i});var o=n(78673);class i extends o.A{constructor(t){super(t);let e=t.orig;this.containerDiv=t.orig;const n=e.querySelector("script");if(n){let t;try{t="text/xml"==n.type?function(t){const e=(new DOMParser).parseFromString(t,"text/html"),n=e.querySelector("parsererror");if(n)throw new Error("XML parse error: "+n.textContent);function o(t){return Array.from(e.querySelectorAll(t)).map((t=>{const e=t.querySelector("id"),n=t.querySelector("label");return{id:e?e.textContent.trim():"",label:n?n.innerHTML.trim():""}}))}return{statement:function(){const t=e.querySelector("statement");return t?t.innerHTML.trim():""}(),feedback:function(){const t=e.querySelector("feedback");return t?t.innerHTML.trim():""}(),left:o("premise"),right:o("response"),correctAnswers:Array.from(e.querySelectorAll("edge")).map((t=>Array.from(t.querySelectorAll("label")).slice(0,2).map((t=>t.textContent.trim()))))}}(n.textContent):JSON.parse(n.textContent),this.boxData=t}catch(t){console.error("Failed to parse boxData JSON:",t)}}this.divid=e.id,this.boxesRenderedPromise=new Promise((t=>{this.boxesRenderedResolve=t})),this.workspace=this.createWorkspace(e);try{this.statement=this.createStatement(e)}catch(t){console.error("Error setting statement:",t)}this.connList=this.createConnList(e),this.ariaLive=this.createAriaLive(e),this.controlDiv=this.createControlDiv(e),this.createHelpModal(),this.connections=[],this.allBoxes=[],this.selectedBox=null,this.startBox=null,this.tempLine=null,this.useRunestoneServices=eBookConfig.useRunestoneServices,this.graderactive=t.graderactive||!1,this.init(),this.boxesRenderedPromise.then((()=>{this.checkServer("matching",!0)}))}init(){this.shuffle(this.boxData.left),this.shuffle(this.boxData.right),this.renderBoxes(),this.attachEvents(),this.queueMathJax(this.containerDiv)}checkCurrentAnswer(){const t=this.boxData.correctAnswers,e=this.connections.map((t=>[t.fromBox.dataset.id,t.toBox.dataset.id])),n=t.filter((t=>e.some((e=>e[0]===t[0]&&e[1]===t[1])))),o=e.filter((e=>!t.some((t=>t[0]===e[0]&&t[1]===e[1]))));this.correctCount=n.length,this.incorrectCount=o.length,this.missingCount=t.length-this.correctCount,this.denominator=this.correctCount+this.incorrectCount+this.missingCount,this.scorePercent=0===this.denominator?0:Math.max(0,Math.min(100,Math.round(this.correctCount/this.denominator*100)))}async logCurrentAnswer(){let t={score:this.scorePercent,percent:this.scorePercent/100,correctCount:this.correctCount,incorrectCount:this.incorrectCount,missingCount:this.missingCount,connections:this.connections.map((t=>({from:t.fromBox.dataset.id,to:t.toBox.dataset.id}))),event:"matching"};t.div_id=this.divid,t.act=`score:${t.score} connections:${JSON.stringify(t.connections)}`,t.correct=100===t.score,t.answer=JSON.stringify({connections:t.connections}),await this.logBookEvent(t)}renderFeedback(){this.connections.forEach((t=>{const e=[t.fromBox.dataset.id,t.toBox.dataset.id],n=this.boxData.correctAnswers.some((t=>t[0]===e[0]&&t[1]===e[1]));t.line.classList.remove("correct","incorrect"),t.line.classList.add(n?"correct":"incorrect")})),this.connList.innerHTML=`<strong>Score: ${this.scorePercent}%</strong><br>`,this.connList.innerHTML+=`<br>Correct: ${this.correctCount}`,this.connList.innerHTML+=`<br>Incorrect: ${this.incorrectCount}`,this.connList.innerHTML+=`<br>Missing: ${this.missingCount}`,100!==this.scorePercent&&(this.connList.innerHTML+=`<div class="match_feedback exercise-content"><strong>Feedback:</strong> ${this.boxData.feedback}</div>`),this.queueMathJax(this.connList)}createStatement(t){const e=document.createElement("div");return e.className="statement",e.classList.add("match_question"),e.classList.add("exercise-statement"),e.innerHTML=this.boxData.statement,t.insertBefore(e,t.firstChild),e}restoreAnswers(t){t&&(this.connections=t.answer.connections.map((t=>({fromBox:this.allBoxes.find((e=>e.dataset.id===t.from)),toBox:this.allBoxes.find((e=>e.dataset.id===t.to))}))),this.updateConnectionModel(),this.correct=t.correct),this.connections.forEach((t=>{const e=this.getRightBoxCenter(t.fromBox),n=this.getLeftBoxCenter(t.toBox),o=this.createLineElement(e.x,e.y,n.x,n.y);o.fromBox=t.fromBox,o.toBox=t.toBox,this.svg.appendChild(o),t.line=o}))}checkLocalStorage(){if(this.graderactive)return;const t=localStorage.getItem(this.divid);if(t){const e=JSON.parse(t);if(e.timestamp&&e.timestamp<eBookConfig.termStartDate)return void localStorage.removeItem(this.divid);this.connections=e.connections.map((t=>({fromBox:this.allBoxes.find((e=>e.dataset.id===t.from)),toBox:this.allBoxes.find((e=>e.dataset.id===t.to))}))),this.updateConnectionModel(),this.correctCount=e.correctCount,this.incorrectCount=e.incorrectCount,this.missingCount=e.missingCount,this.scorePercent=e.score,this.restoreAnswers(),this.renderFeedback()}}setLocalStorage(){const t=new Date,e={connections:this.connections.map((t=>({from:t.fromBox.dataset.id,to:t.toBox.dataset.id}))),score:this.scorePercent,correctCount:this.correctCount,incorrectCount:this.incorrectCount,missingCount:this.missingCount,timestamp:t};localStorage.setItem(this.divid,JSON.stringify(e))}disableInteraction(){}createWorkspace(t){const e=document.createElement("div");e.className="matching-workspace";const n=document.createElement("div");n.className="left-column",this.leftColumn=n;const o=document.createElementNS("http://www.w3.org/2000/svg","svg");o.classList.add("connector-svg"),this.svg=o;const i=document.createElement("div");return i.className="right-column",this.rightColumn=i,e.appendChild(n),e.appendChild(o),e.appendChild(i),t.insertBefore(e,t.firstChild),e}createConnList(t){const e=document.createElement("div");return e.className="conn-list",e.innerHTML="<strong>Connections:</strong><br>",t.appendChild(e),e}createAriaLive(t){const e=document.createElement("div");return e.className="aria-live",e.setAttribute("aria-live","polite"),e.setAttribute("aria-atomic","true"),t.appendChild(e),e}createControlDiv(t){const e=document.createElement("div");e.className="control-div";const n=document.createElement("button");n.className="grade-button",n.textContent="Check Me",n.classList.add("btn","btn-success");const o=document.createElement("button");o.className="reset-button",o.textContent="Reset",o.classList.add("btn","btn-default");const i=document.createElement("button");return i.className="help-button",i.textContent="?",i.setAttribute("aria-label","Help"),e.appendChild(n),e.appendChild(o),e.appendChild(i),t.appendChild(e),n.addEventListener("click",(()=>this.gradeConnections())),o.addEventListener("click",(()=>this.resetConnections())),i.addEventListener("click",(()=>this.showHelp())),this.gradeBtn=n,this.resetBtn=o,this.helpBtn=i,e}createHelpModal(){this.helpModal=document.createElement("div"),this.helpModal.className="help-modal",this.helpModal.innerHTML='\n <div class="help-modal-content">\n <button class="help-close">×</button>\n <div class="help-text"><p>Click and drag between boxes to create connections.</p>\n <p>Use the tab key to navigate to a box and press Enter to select the box. Then tab to the connecting box and press Enter to create a connection between the two selected boxes.</p>\n <p>Click on a connection line to remove it. You can also use the tab key to select lines. Press the delete key to remove a selected line.</p>\n <p>Click the "Check Me" button to check your connections, and save your work.</p>\n <p>Click the "Reset" button to clear all connections.</p></div>\n </div>',this.containerDiv.appendChild(this.helpModal),this.helpModal.querySelector(".help-close").addEventListener("click",(()=>this.hideHelp()))}showHelp(){this.helpModal.style.display="flex"}hideHelp(){this.helpModal.style.display="none"}shuffle(t){for(let e=t.length-1;e>0;e--){const n=Math.floor(Math.random()*(e+1));[t[e],t[n]]=[t[n],t[e]]}return t}renderBoxes(){this.boxData.left.forEach((({id:t,label:e})=>{const n=this.createBox(t,e,"drag");this.leftColumn.appendChild(n),this.allBoxes.push(n)})),this.boxData.right.forEach((({id:t,label:e})=>{const n=this.createBox(t,e,"drop");this.rightColumn.appendChild(n),this.allBoxes.push(n)}));const t=Array.from(this.workspace.querySelectorAll("img"));0===t.length&&this.boxesRenderedResolve();const e=t.map((t=>"function"==typeof t.decode?t.decode():t.complete&&0!==t.naturalWidth?Promise.resolve():new Promise((e=>{t.addEventListener("load",(()=>e())),t.addEventListener("error",(()=>e()))}))));Promise.all(e).then((()=>{this.boxesRenderedResolve()}))}createBox(t,e,n){const o=document.createElement("div");return o.className="box",o.dataset.id=t,o.dataset.role=n,o.innerHTML=e,o.tabIndex=0,o.setAttribute("role","button"),o.setAttribute("aria-label",`${"drag"===n?"Draggable":"Droppable"}: ${e}`),o}getCenter(t){const e=t.getBoundingClientRect(),n=this.workspace.getBoundingClientRect();return{x:e.left-n.left+e.width/2,y:e.top-n.top+e.height/2}}getRightBoxCenter(t){const e=t.getBoundingClientRect(),n=this.workspace.getBoundingClientRect();return{x:e.left-n.left+e.width,y:e.top-n.top+e.height/2}}getLeftBoxCenter(t){const e=t.getBoundingClientRect(),n=this.workspace.getBoundingClientRect();return{x:e.left-n.left,y:e.top-n.top+e.height/2}}createLineElement(t,e,n,o){const i=document.createElementNS("http://www.w3.org/2000/svg","line");return i.setAttribute("x1",t),i.setAttribute("y1",e),i.setAttribute("x2",n),i.setAttribute("y2",o),i.setAttribute("class","line"),i.setAttribute("tabindex","0"),i.setAttribute("focusable","true"),i.setAttribute("role","button"),i.setAttribute("aria-label","Connection line. Press Delete to remove."),i.addEventListener("click",(()=>{this.removeLine(i)})),i.addEventListener("keydown",(t=>{"Delete"!==t.key&&"Backspace"!==t.key||(t.preventDefault(),this.removeLine(i))})),i}removeLine(t){this.svg.removeChild(t);const e=this.connections.findIndex((e=>e.fromBox===t.fromBox&&e.toBox===t.toBox||e.fromBox===t.toBox&&e.toBox===t.fromBox));-1!==e&&this.connections.splice(e,1),this.updateConnectionModel()}isConnected(t,e){return this.connections.some((n=>n.fromBox===t&&n.toBox===e||n.fromBox===e&&n.toBox===t))}createPermanentLine(t,e){if(t.dataset.role===e.dataset.role)return void alert("You can only connect a draggable to a droppable.");if("drop"===t.dataset.role&&([t,e]=[e,t]),this.isConnected(t,e))return;const n=this.getRightBoxCenter(t),o=this.getLeftBoxCenter(e),i=this.createLineElement(n.x,n.y,o.x,o.y);i.fromBox=t,i.toBox=e,this.svg.appendChild(i),this.connections.push({fromBox:t,toBox:e,line:i}),this.updateConnectionModel(),this.isAnswered=!0,this.ariaLive&&(this.ariaLive.textContent=`Connected ${t.textContent} to ${e.textContent}`)}updateConnectionModel(){this.connList.innerHTML="<strong>Connections:</strong><br>",this.connections.forEach((t=>{const e=t.fromBox.textContent;let n=t.toBox.textContent;n||(n=t.toBox.querySelector("img").alt);const o=document.createElement("div");o.className="conn-entry",o.textContent=`${e} → ${n}`,this.connList.appendChild(o)}))}gradeConnections(){this.checkCurrentAnswer(),this.renderFeedback(),this.logCurrentAnswer(),this.setLocalStorage()}resetConnections(){this.connections.forEach((t=>{t.line&&t.line.parentNode===this.svg&&this.svg.removeChild(t.line)})),this.connections.length=0,this.updateConnectionModel(),this.ariaLive&&(this.ariaLive.textContent="All connections have been cleared.")}attachEvents(){this.allBoxes.forEach((t=>{t.addEventListener("pointerdown",(e=>{e.ctrlKey||e.metaKey;{e.preventDefault(),this.startBox=t;const n=this.getRightBoxCenter(this.startBox);this.tempLine=this.createLineElement(n.x,n.y,n.x,n.y),this.tempLine.setAttribute("stroke","gray"),this.tempLine.setAttribute("stroke-dasharray","4"),this.svg.appendChild(this.tempLine),document.addEventListener("pointermove",this.updateTempLine),document.addEventListener("pointerup",this.finishConnection)}})),t.addEventListener("keydown",(e=>{if("Enter"===e.key)if(e.preventDefault(),this.selectedBox){t!==this.selectedBox&&this.createPermanentLine(this.selectedBox,t),this.selectedBox.classList.remove("selected"),this.selectedBox=null;const e=this.allBoxes.indexOf(t),n=this.allBoxes[e+1];n?n.focus():this.allBoxes[0].focus()}else this.selectedBox=t,t.classList.add("selected")})),t.addEventListener("mouseenter",(()=>{this.connections.forEach((e=>{e.fromBox===t||e.toBox===t?(e.line.classList.add("highlighted"),e.line.classList.remove("faded")):(e.line.classList.add("faded"),e.line.classList.remove("highlighted"))}))})),t.addEventListener("mouseleave",(()=>{this.connections.forEach((t=>{t.line.classList.remove("highlighted","faded")}))}))}));const t=this.containerDiv.querySelector(".grade-button"),e=this.containerDiv.querySelector(".reset-button");t&&t.addEventListener("click",(()=>this.gradeConnections())),e&&e.addEventListener("click",(()=>this.resetConnections())),window.addEventListener("resize",(()=>{this.connections.forEach((t=>{const e=this.getRightBoxCenter(t.fromBox),n=this.getLeftBoxCenter(t.toBox);t.line.setAttribute("x1",e.x),t.line.setAttribute("y1",e.y),t.line.setAttribute("x2",n.x),t.line.setAttribute("y2",n.y)}))}))}updateTempLine=t=>{if(t.preventDefault(),!this.startBox||!this.tempLine)return;const e=this.getRightBoxCenter(this.startBox);this.tempLine.setAttribute("x1",e.x),this.tempLine.setAttribute("y1",e.y);const n=this.workspace.getBoundingClientRect(),o=t.clientX-n.left,i=t.clientY-n.top;this.tempLine.setAttribute("x2",o),this.tempLine.setAttribute("y2",i)};finishConnection=t=>{t.preventDefault(),this.tempLine&&(this.svg.removeChild(this.tempLine),this.tempLine=null);const e=t.clientX||t.changedTouches&&t.changedTouches[0].clientX,n=t.clientY||t.changedTouches&&t.changedTouches[0].clientY,o=document.elementFromPoint(e,n),i=this.allBoxes.find((t=>t.contains(o)&&t!==this.startBox));this.startBox&&i&&this.createPermanentLine(this.startBox,i),this.startBox=null,document.removeEventListener("pointermove",this.updateTempLine),document.removeEventListener("pointerup",this.finishConnection)}}document.addEventListener("runestone:login-complete",(()=>{document.querySelectorAll('[data-component="matching"]').forEach((t=>{if(!t.closest("[data-component=timedAssessment]")){let e={orig:t};window.componentMap[t.id]=new i(e)}}))})),void 0===window.component_factory&&(window.component_factory={}),window.component_factory.matching=function(t){return new i(t)}}}]);
|
|
//# sourceMappingURL=566.8345f32fac0929c0.js.map
|