From eeb866f5a64103626133809fc19c595b219497e3 Mon Sep 17 00:00:00 2001 From: Anupama Sarjoshi Date: Mon, 21 Oct 2024 12:29:49 +0100 Subject: [PATCH] Fix grunt errors --- amd/build/question.min.js | 2 +- amd/build/question.min.js.map | 2 +- amd/src/question.js | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/amd/build/question.min.js b/amd/build/question.min.js index 6dce6ef..1693f93 100644 --- a/amd/build/question.min.js +++ b/amd/build/question.min.js @@ -5,6 +5,6 @@ * @copyright 2024 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("qtype_drawlines/question",["jquery","core/dragdrop","qtype_drawlines/line","core/key_codes","core_form/changechecker"],(function($,dragDrop,Line){function DrawlinesQuestion(containerId,readOnly,visibleDropZones,questionLines){var thisQ=this;this.containerId=containerId,this.visibleDropZones=visibleDropZones,this.questionLines=questionLines,this.lineSVGs=[],this.lines=[],this.svgEl=null,readOnly&&this.getRoot().classList.add("qtype_drawlines-readonly"),thisQ.allImagesLoaded=!1,thisQ.waitForAllImagesToBeLoaded().then((()=>(thisQ.drawDropzone(),null))).catch((error=>{throw error}))}DrawlinesQuestion.prototype.updateCoordinates=function(){for(var line=0;line');this.drawSVGLines(this.questionLines)},DrawlinesQuestion.prototype.drawSVGLines=function(questionLines){var height,startcoordinates,endcoordinates,draginitialcoords,bgImage=this.bgImage();this.getRoot().querySelector(".draghomes").innerHTML='';var draghomeSvg=this.getRoot().querySelector(".dragshome"),dropzoneSvg=this.getRoot().querySelector(".dropzones");for(let line=0;linebgImage.height-20,(isMoveFromDragsToDropzones||isMoveFromDropzonesToDrags)&&movingDrag.lines[dropzoneNo].addToDropZone("mouse",selectedElement,closestSVGs.svgDropZone,closestSVGs.svgDragsHome,dropX,dropY),closeTo=selectedElement.closest("svg");var dimensions=movingDrag.getSvgDimensionsByClass(closeTo,closeTo.getAttribute("class"));maxX=dimensions.maxX,maxY=dimensions.maxY,whichSVG=dimensions.whichSVG,movingDrag.lines[dropzoneNo].moveDrags(parseInt(pageX)-parseInt(lastX),parseInt(pageY)-parseInt(lastY),parseInt(maxX),parseInt(maxY),whichSVG),lastX=pageX,lastY=pageY,movingDrag.updateSvgEl(dropzoneNo),movingDrag.saveCoordsForChoice(dropzoneNo)}),(function(){document.body.removeChild(dragProxy)}))},DrawlinesQuestion.prototype.makeDragProxy=function(x,y){var dragProxy=document.createElement("div");return dragProxy.style.position="absolute",dragProxy.style.top=y+"px",dragProxy.style.left=x+"px",dragProxy.style.width="1px",dragProxy.style.height="1px",document.body.appendChild(dragProxy),dragProxy},DrawlinesQuestion.prototype.saveCoordsForChoice=function(choiceNo){let imageCoords=[];var items=this.getRoot().querySelector("svg g.choice"+choiceNo),gEleClassAttributes="";items&&(imageCoords=items.querySelector("polyline").getAttribute("points"),gEleClassAttributes=items.getAttribute("class")),""!==gEleClassAttributes&&gEleClassAttributes.includes("placed")?this.getRoot().querySelector("input.choice"+choiceNo).value=imageCoords:""!==gEleClassAttributes&&gEleClassAttributes.includes("inactive")&&(this.getRoot().querySelector("input.choice"+choiceNo).value="")},DrawlinesQuestion.prototype.handleKeyPress=function(e,drag,dropzoneNo,activeElement){var dropzoneElement,x=0,y=0,question=questionManager.getQuestionForEvent(e);switch(dropzoneElement=drag.closest("g"),e.code){case"ArrowLeft":case"KeyA":x=-1;break;case"ArrowRight":case"KeyD":x=1;break;case"ArrowDown":case"KeyS":y=1;break;case"ArrowUp":case"KeyW":y=-1;break;case"Space":case"Escape":break;default:return}e.preventDefault();var maxX,maxY,whichSVG,closeTo=drag.closest("svg"),svgClass=closeTo.getAttribute("class"),bgImage=this.bgImage(),closestSVGs=this.getSvgsClosestToElement(drag),isMoveFromDragsToDropzones="dragshome"===svgClass,isMoveFromDropzonesToDrags="dropzones"===svgClass&&question.lines[dropzoneNo].centre1.y>bgImage.height-20;isMoveFromDragsToDropzones?question.lines[dropzoneNo].addToDropZone("keyboard",dropzoneElement,closestSVGs.svgDropZone,closestSVGs.svgDragsHome,null,null,"DragsSVG"):isMoveFromDropzonesToDrags&&question.lines[dropzoneNo].addToDropZone("keyboard",dropzoneElement,closestSVGs.svgDropZone,closestSVGs.svgDragsHome,null,null,"DropZonesSVG"),closeTo=drag.closest("svg");var dimensions=question.getSvgDimensionsByClass(closeTo,closeTo.getAttribute("class"));maxX=dimensions.maxX,maxY=dimensions.maxY,whichSVG=dimensions.whichSVG,"line"===activeElement?question.lines[dropzoneNo].moveDrags(parseInt(x),parseInt(y),parseInt(maxX),parseInt(maxY),whichSVG):question.lines[dropzoneNo].move(activeElement,parseInt(x),parseInt(y),parseInt(maxX),parseInt(maxY)),question.updateSvgEl(dropzoneNo),this.saveCoordsForChoice(dropzoneNo),drag.focus()},DrawlinesQuestion.prototype.getSvgDimensionsByClass=function(dragSVG,className){return{maxX:dragSVG.width.baseVal.value,maxY:dragSVG.height.baseVal.value,whichSVG:"dragshome"===className?"DragsSVG":"DropZonesSVG"}},DrawlinesQuestion.prototype.getSvgsClosestToElement=function(dragElement){var svgDragsHome,svgDropZone,svgElement=dragElement.closest("svg");return"dragshome"===svgElement.getAttribute("class")?(svgDragsHome=svgElement,svgDropZone=svgElement.closest(".ddarea").querySelector(".dropzones")):(svgDropZone=svgElement,svgDragsHome=svgElement.closest(".ddarea").querySelector(".dragshome")),{svgDropZone:svgDropZone,svgDragsHome:svgDragsHome}},DrawlinesQuestion.prototype.waitForAllImagesToBeLoaded=function(){if(this.allImagesLoaded)return Promise.resolve(null);const images=document.querySelectorAll("img"),promises=Array.from(images).map((img=>new Promise(((resolve,reject)=>{img.complete?resolve():(img.onload=()=>resolve(),img.onerror=()=>reject(new Error(`Failed to load image: ${img.src}`)))}))));return Promise.all(promises).then((()=>(this.allImagesLoaded=!0,null))).catch((error=>{throw this.allImagesLoaded=!0,error}))},DrawlinesQuestion.prototype.getNotYetLoadedImages=function(){const images=this.getRoot().querySelectorAll(".drawlines img.dropbackground");Array.from(images).filter((imgNode=>!this.imageIsLoaded(imgNode)))},DrawlinesQuestion.prototype.imageIsLoaded=function(imgElement){return imgElement.complete&&0!==imgElement.naturalHeight};var questionManager={eventHandlersInitialised:!1,lineEventHandlersInitialised:{},isPrinting:!1,isKeyboardNavigation:!1,questions:{},noOfLines:null,dropZones:[],questionLines:[],init:function(containerId,readOnly,visibleDropZones,questionLines){if(questionManager.questions[containerId]=new DrawlinesQuestion(containerId,readOnly,visibleDropZones,questionLines),questionManager.questions[containerId].updateCoordinates(),!questionManager.lineEventHandlersInitialised.hasOwnProperty(containerId)){questionManager.lineEventHandlersInitialised[containerId]=!0;var questionContainer=document.getElementById(containerId);if(questionContainer.classList.contains("drawlines")&&!questionContainer.classList.contains("qtype_drawlines-readonly")){var dropArea=questionContainer.querySelector(".droparea");dropArea.addEventListener("mousedown",questionManager.handleDropZoneEventMove),dropArea.addEventListener("touchstart",questionManager.handleDropZoneEventMove),dropArea.addEventListener("keydown",questionManager.handleKeyPress),dropArea.addEventListener("keypress",questionManager.handleKeyPress);var drags=questionContainer.querySelector(".draghomes");drags.addEventListener("mousedown",questionManager.handleDragHomeEventMove),drags.addEventListener("touchstart",questionManager.handleDragHomeEventMove),drags.addEventListener("keydown",questionManager.handleKeyPress),drags.addEventListener("keypress",questionManager.handleKeyPress)}}},handleDropZoneEventMove:function(event){var dropzoneNo,question=questionManager.getQuestionForEvent(event);event.target.closest(".dropzone .startcircle.shape")?(dropzoneNo=event.target.closest("g").dataset.dropzoneNo,question.handleCircleMove(event,"startcircle",dropzoneNo)):event.target.closest(".dropzone .endcircle.shape")?(dropzoneNo=event.target.closest("g").dataset.dropzoneNo,question.handleCircleMove(event,"endcircle",dropzoneNo)):event.target.closest("polyline.shape")&&(dropzoneNo=event.target.closest("g").dataset.dropzoneNo,question.handleLineMove(event,dropzoneNo))},handleDragHomeEventMove:function(event){var dropzoneNo,question=questionManager.getQuestionForEvent(event);event.target.closest("g")&&(dropzoneNo=event.target.closest("g").dataset.dropzoneNo,question.handleLineMove(event,dropzoneNo),question.saveCoordsForChoice(dropzoneNo))},handleKeyPress:function(e){var dropzoneElement,dropzoneNo,drag,activeElement,question=questionManager.getQuestionForEvent(e);e.target.closest(".dropzone circle.startcircle")?(dropzoneNo=(dropzoneElement=e.target.closest(".dropzone")).dataset.dropzoneNo,drag=e.target.closest(".dropzone circle.startcircle"),activeElement="startcircle"):e.target.closest(".dropzone circle.endcircle")?(drag=e.target.closest(".dropzone circle.endcircle"),dropzoneNo=(dropzoneElement=e.target.closest(".dropzone")).dataset.dropzoneNo,activeElement="endcircle"):e.target.closest("g.dropzone")&&(drag=e.target.closest("g.dropzone"),dropzoneNo=(dropzoneElement=e.target.closest(".dropzone")).dataset.dropzoneNo,activeElement="line"),question&&dropzoneElement&&question.handleKeyPress(e,drag,dropzoneNo,activeElement)},handleWindowResize:function(isPrinting){for(var containerId in questionManager.questions)questionManager.questions.hasOwnProperty(containerId)&&(questionManager.questions[containerId].isPrinting=isPrinting,questionManager.questions[containerId].handleResize())},getQuestionForEvent:function(e){var containerId=$(e.currentTarget).closest(".que.drawlines").attr("id");return questionManager.questions[containerId]}};return{init:questionManager.init}})); +define("qtype_drawlines/question",["jquery","core/dragdrop","qtype_drawlines/line","core/key_codes","core_form/changechecker"],(function($,dragDrop,Line){function DrawlinesQuestion(containerId,readOnly,visibleDropZones,questionLines){var thisQ=this;this.containerId=containerId,this.visibleDropZones=visibleDropZones,this.questionLines=questionLines,this.lineSVGs=[],this.lines=[],this.svgEl=null,readOnly&&this.getRoot().classList.add("qtype_drawlines-readonly"),thisQ.allImagesLoaded=!1,thisQ.waitForAllImagesToBeLoaded().then((()=>(thisQ.drawDropzone(),null))).catch((error=>{throw error}))}DrawlinesQuestion.prototype.updateCoordinates=function(){for(var line=0;line');this.drawSVGLines(this.questionLines)},DrawlinesQuestion.prototype.drawSVGLines=function(questionLines){var height,startcoordinates,endcoordinates,draginitialcoords,bgImage=this.bgImage();this.getRoot().querySelector(".draghomes").innerHTML='';var draghomeSvg=this.getRoot().querySelector(".dragshome"),dropzoneSvg=this.getRoot().querySelector(".dropzones");for(let line=0;linebgImage.height-20,(isMoveFromDragsToDropzones||isMoveFromDropzonesToDrags)&&movingDrag.lines[dropzoneNo].addToDropZone("mouse",selectedElement,closestSVGs.svgDropZone,closestSVGs.svgDragsHome,dropX,dropY),closeTo=selectedElement.closest("svg");var dimensions=movingDrag.getSvgDimensionsByClass(closeTo,closeTo.getAttribute("class"));maxX=dimensions.maxX,maxY=dimensions.maxY,whichSVG=dimensions.whichSVG,movingDrag.lines[dropzoneNo].moveDrags(parseInt(pageX)-parseInt(lastX),parseInt(pageY)-parseInt(lastY),parseInt(maxX),parseInt(maxY),whichSVG),lastX=pageX,lastY=pageY,movingDrag.updateSvgEl(dropzoneNo),movingDrag.saveCoordsForChoice(dropzoneNo)}),(function(){document.body.removeChild(dragProxy)}))},DrawlinesQuestion.prototype.makeDragProxy=function(x,y){var dragProxy=document.createElement("div");return dragProxy.style.position="absolute",dragProxy.style.top=y+"px",dragProxy.style.left=x+"px",dragProxy.style.width="1px",dragProxy.style.height="1px",document.body.appendChild(dragProxy),dragProxy},DrawlinesQuestion.prototype.saveCoordsForChoice=function(choiceNo){let imageCoords=[];var items=this.getRoot().querySelector("svg g.choice"+choiceNo),gEleClassAttributes="";items&&(imageCoords=items.querySelector("polyline").getAttribute("points"),gEleClassAttributes=items.getAttribute("class")),""!==gEleClassAttributes&&gEleClassAttributes.includes("placed")?this.getRoot().querySelector("input.choice"+choiceNo).value=imageCoords:""!==gEleClassAttributes&&gEleClassAttributes.includes("inactive")&&(this.getRoot().querySelector("input.choice"+choiceNo).value="")},DrawlinesQuestion.prototype.handleKeyPress=function(e,drag,dropzoneNo,activeElement){var dropzoneElement,x=0,y=0,question=questionManager.getQuestionForEvent(e);switch(dropzoneElement=drag.closest("g"),e.code){case"ArrowLeft":case"KeyA":x=-1;break;case"ArrowRight":case"KeyD":x=1;break;case"ArrowDown":case"KeyS":y=1;break;case"ArrowUp":case"KeyW":y=-1;break;case"Space":case"Escape":break;default:return}e.preventDefault();var maxX,maxY,whichSVG,closeTo=drag.closest("svg"),svgClass=closeTo.getAttribute("class"),bgImage=this.bgImage(),closestSVGs=this.getSvgsClosestToElement(drag),isMoveFromDragsToDropzones="dragshome"===svgClass,isMoveFromDropzonesToDrags="dropzones"===svgClass&&question.lines[dropzoneNo].centre1.y>bgImage.height-20;isMoveFromDragsToDropzones?question.lines[dropzoneNo].addToDropZone("keyboard",dropzoneElement,closestSVGs.svgDropZone,closestSVGs.svgDragsHome,null,null,"DragsSVG"):isMoveFromDropzonesToDrags&&question.lines[dropzoneNo].addToDropZone("keyboard",dropzoneElement,closestSVGs.svgDropZone,closestSVGs.svgDragsHome,null,null,"DropZonesSVG"),closeTo=drag.closest("svg");var dimensions=question.getSvgDimensionsByClass(closeTo,closeTo.getAttribute("class"));maxX=dimensions.maxX,maxY=dimensions.maxY,whichSVG=dimensions.whichSVG,"line"===activeElement?question.lines[dropzoneNo].moveDrags(parseInt(x),parseInt(y),parseInt(maxX),parseInt(maxY),whichSVG):question.lines[dropzoneNo].move(activeElement,parseInt(x),parseInt(y),parseInt(maxX),parseInt(maxY)),question.updateSvgEl(dropzoneNo),this.saveCoordsForChoice(dropzoneNo),drag.focus()},DrawlinesQuestion.prototype.getSvgDimensionsByClass=function(dragSVG,className){return{maxX:dragSVG.width.baseVal.value,maxY:dragSVG.height.baseVal.value,whichSVG:"dragshome"===className?"DragsSVG":"DropZonesSVG"}},DrawlinesQuestion.prototype.getSvgsClosestToElement=function(dragElement){var svgDragsHome,svgDropZone,svgElement=dragElement.closest("svg");return"dragshome"===svgElement.getAttribute("class")?(svgDragsHome=svgElement,svgDropZone=svgElement.closest(".ddarea").querySelector(".dropzones")):(svgDropZone=svgElement,svgDragsHome=svgElement.closest(".ddarea").querySelector(".dragshome")),{svgDropZone:svgDropZone,svgDragsHome:svgDragsHome}},DrawlinesQuestion.prototype.waitForAllImagesToBeLoaded=function(){if(this.allImagesLoaded)return Promise.resolve(null);const images=document.querySelectorAll("img"),promises=Array.from(images).map((img=>new Promise(((resolve,reject)=>{img.complete?resolve():(img.onload=()=>resolve(),img.onerror=()=>reject(new Error(`Failed to load image: ${img.src}`)))}))));return Promise.all(promises).then((()=>(this.allImagesLoaded=!0,null))).catch((error=>{throw this.allImagesLoaded=!0,error}))},DrawlinesQuestion.prototype.getNotYetLoadedImages=function(){const images=this.getRoot().querySelectorAll(".drawlines img.dropbackground");Array.from(images).filter((imgNode=>!this.imageIsLoaded(imgNode)))},DrawlinesQuestion.prototype.imageIsLoaded=function(imgElement){return imgElement.complete&&0!==imgElement.naturalHeight};var questionManager={eventHandlersInitialised:!1,lineEventHandlersInitialised:{},isPrinting:!1,isKeyboardNavigation:!1,questions:{},noOfLines:null,dropZones:[],questionLines:[],init:function(containerId,readOnly,visibleDropZones,questionLines){if(questionManager.questions[containerId]=new DrawlinesQuestion(containerId,readOnly,visibleDropZones,questionLines),questionManager.questions[containerId].updateCoordinates(),!questionManager.lineEventHandlersInitialised.hasOwnProperty(containerId)){questionManager.lineEventHandlersInitialised[containerId]=!0;var questionContainer=document.getElementById(containerId);if(questionContainer.classList.contains("drawlines")&&!questionContainer.classList.contains("qtype_drawlines-readonly")){var dropArea=questionContainer.querySelector(".droparea");dropArea.addEventListener("mousedown",questionManager.handleDropZoneEventMove),dropArea.addEventListener("touchstart",questionManager.handleDropZoneEventMove),dropArea.addEventListener("keydown",questionManager.handleKeyPress),dropArea.addEventListener("keypress",questionManager.handleKeyPress);var drags=questionContainer.querySelector(".draghomes");drags.addEventListener("mousedown",questionManager.handleDragHomeEventMove),drags.addEventListener("touchstart",questionManager.handleDragHomeEventMove),drags.addEventListener("keydown",questionManager.handleKeyPress),drags.addEventListener("keypress",questionManager.handleKeyPress)}}},handleDropZoneEventMove:function(event){var dropzoneNo,question=questionManager.getQuestionForEvent(event);event.target.closest(".dropzone .startcircle.shape")?(dropzoneNo=event.target.closest("g").dataset.dropzoneNo,question.handleCircleMove(event,"startcircle",dropzoneNo)):event.target.closest(".dropzone .endcircle.shape")?(dropzoneNo=event.target.closest("g").dataset.dropzoneNo,question.handleCircleMove(event,"endcircle",dropzoneNo)):event.target.closest("polyline.shape")&&(dropzoneNo=event.target.closest("g").dataset.dropzoneNo,question.handleLineMove(event,dropzoneNo))},handleDragHomeEventMove:function(event){var dropzoneNo,question=questionManager.getQuestionForEvent(event);event.target.closest("g")&&(dropzoneNo=event.target.closest("g").dataset.dropzoneNo,question.handleLineMove(event,dropzoneNo),question.saveCoordsForChoice(dropzoneNo))},handleKeyPress:function(e){var dropzoneElement,dropzoneNo,drag,activeElement,question=questionManager.getQuestionForEvent(e);e.target.closest(".dropzone circle.startcircle")?(dropzoneNo=(dropzoneElement=e.target.closest(".dropzone")).dataset.dropzoneNo,drag=e.target.closest(".dropzone circle.startcircle"),activeElement="startcircle"):e.target.closest(".dropzone circle.endcircle")?(drag=e.target.closest(".dropzone circle.endcircle"),dropzoneNo=(dropzoneElement=e.target.closest(".dropzone")).dataset.dropzoneNo,activeElement="endcircle"):e.target.closest("g.dropzone")&&(drag=e.target.closest("g.dropzone"),dropzoneNo=(dropzoneElement=e.target.closest(".dropzone")).dataset.dropzoneNo,activeElement="line"),question&&dropzoneElement&&question.handleKeyPress(e,drag,dropzoneNo,activeElement)},handleWindowResize:function(isPrinting){for(var containerId in questionManager.questions)questionManager.questions.hasOwnProperty(containerId)&&(questionManager.questions[containerId].isPrinting=isPrinting,questionManager.questions[containerId].handleResize())},getQuestionForEvent:function(e){var containerId=$(e.currentTarget).closest(".que.drawlines").attr("id");return questionManager.questions[containerId]}};return{init:questionManager.init}})); //# sourceMappingURL=question.min.js.map \ No newline at end of file diff --git a/amd/build/question.min.js.map b/amd/build/question.min.js.map index f7d77bc..f431d8e 100644 --- a/amd/build/question.min.js.map +++ b/amd/build/question.min.js.map @@ -1 +1 @@ -{"version":3,"file":"question.min.js","sources":["../src/question.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * JavaScript to allow dragging options for lines (using mouse down or touch) or tab through lines using keyboard.\n *\n * @module qtype_drawlines/question\n * @copyright 2024 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine([\n 'jquery',\n 'core/dragdrop',\n 'qtype_drawlines/line',\n 'core/key_codes',\n 'core_form/changechecker',\n], function(\n $,\n dragDrop,\n Line,\n) {\n\n \"use strict\";\n\n /**\n * Object to handle one drag-drop markers question.\n *\n * @param {String} containerId id of the outer div for this question.\n * @param {boolean} readOnly whether the question is being displayed read-only.\n * @param {Object[]} visibleDropZones the geometry of any drop-zones to show.\n * Objects have fields line, coords and markertext.\n * @param {line[]} questionLines\n * @constructor\n */\n function DrawlinesQuestion(containerId, readOnly, visibleDropZones, questionLines) {\n var thisQ = this;\n this.containerId = containerId;\n this.visibleDropZones = visibleDropZones;\n this.questionLines = questionLines;\n this.lineSVGs = [];\n this.lines = [];\n this.svgEl = null;\n if (readOnly) {\n this.getRoot().classList.add('qtype_drawlines-readonly');\n }\n thisQ.allImagesLoaded = false;\n // Get all images that are not yet loaded\n thisQ.waitForAllImagesToBeLoaded()\n .then(() => {\n thisQ.drawDropzone(); // Call your function here\n return null;\n })\n .catch((error) => {\n throw error;\n });\n }\n\n /**\n * Update the coordinates from a particular string.\n */\n DrawlinesQuestion.prototype.updateCoordinates = function() {\n // We don't need to scale the shape for editing form.\n for (var line = 0; line < this.lineSVGs.length; line++) {\n var coordinates = this.getSVGLineCoordinates(this.lineSVGs[line]);\n if (!this.lines[line].parse(coordinates[0], coordinates[1], 1)) {\n // Invalid coordinates. Don't update the preview.\n return;\n }\n this.updateSvgEl(line);\n }\n };\n\n /**\n * Parse the coordinates from a particular string.\n *\n * @param {String} coordinates The coordinates to be parsed. The values are in the format: x1,y1 x2,y2.\n * Except for infinite line type where it's in the format x1,y1 x2,y2, x3,y3, x4,y4.\n * Here, x1,y1 and x4,y4 are the two very end points of the infinite line and\n * x2,y2 and x3,y3 are the pints with the handles.\n * @param {String} lineType The type of the line.\n */\n DrawlinesQuestion.prototype.parseCoordinates = function(coordinates, lineType) {\n var bits = coordinates.split(' ');\n if (lineType === 'lineinfinite' && bits.length !== 2) {\n // Remove the first and last coordinates.\n bits = bits.slice(1, -1);\n }\n if (bits.length !== 2) {\n throw new Error(coordinates + ' is not a valid point');\n }\n return bits;\n };\n\n /**\n * Draws the svg lines of any drop zones that should be visible for feedback purposes.\n */\n DrawlinesQuestion.prototype.drawDropzone = function() {\n var bgImage = this.bgImage();\n var svg = this.getRoot().querySelector('svg.dropzones');\n var rootElement = this.getRoot();\n rootElement.querySelector('.que-dlines-dropzone').style.position = 'relative';\n rootElement.querySelector('.que-dlines-dropzone').style.top = (bgImage.height + 1) * -1 + \"px\";\n rootElement.querySelector('.que-dlines-dropzone').style.height = bgImage.height + \"px\";\n rootElement.querySelector('.droparea').style.height = bgImage.height + \"px\";\n if (!svg) {\n var dropZone = this.getRoot().querySelector('.que-dlines-dropzone');\n dropZone.innerHTML =\n '';\n }\n this.drawSVGLines(this.questionLines);\n };\n\n /**\n * Draws the svg lines of any drop zones.\n *\n * @param {Object[]} questionLines\n */\n DrawlinesQuestion.prototype.drawSVGLines = function(questionLines) {\n var bgImage = this.bgImage(),\n height, startcoordinates, endcoordinates, draginitialcoords;\n\n var drags = this.getRoot().querySelector('.draghomes');\n drags.innerHTML =\n '';\n\n var draghomeSvg = this.getRoot().querySelector('.dragshome');\n var dropzoneSvg = this.getRoot().querySelector('.dropzones');\n var initialHeight = 25;\n for (let line = 0; line < questionLines.length; line++) {\n height = initialHeight + line * 50;\n startcoordinates = '50,' + height + ';10';\n endcoordinates = '200,' + height + ';10';\n\n // Check if the lines are to be set with initial coordinates.\n draginitialcoords = this.visibleDropZones['c' + line];\n if (draginitialcoords !== undefined && draginitialcoords !== '') {\n // The visibleDropZones array holds the response in the format x1,y1 x2,y2 - to be added to svgdropzone.\n var coords = this.parseCoordinates(draginitialcoords, questionLines[line].type);\n startcoordinates = coords[0] + ';10';\n endcoordinates = coords[1] + ';10';\n this.lines[line] = Line.make(\n [startcoordinates, endcoordinates],\n [questionLines[line].labelstart, questionLines[line].labelend],\n questionLines[line].type\n );\n this.addToSvg(line, dropzoneSvg);\n } else {\n // Need to be added to draghomeSvg.\n this.lines[line] = Line.make(\n [startcoordinates, endcoordinates],\n [questionLines[line].labelstart, questionLines[line].labelend],\n questionLines[line].type\n );\n this.addToSvg(line, draghomeSvg);\n }\n }\n };\n\n // TODO: The below methods can be refractored for window resizing.\n //\n // /**\n // * Adds a dropzone line with colour, coords and link provided to the array of Lines.\n // *\n // * @param {jQuery} svg the SVG image to which to add this drop zone.\n // * @param {int} dropZoneNo which drop-zone to add.\n // * @param {string} colourClass class name\n // */\n // DrawlinesQuestion.prototype.addDropzone = function(svg, dropZoneNo, colourClass) {\n // var dropZone = this.visibleDropZones[dropZoneNo],\n // line = Line.make(dropZone.line, ''),\n // existingmarkertext,\n // bgRatio = this.bgRatio();\n // if (!line.parse(dropZone.coords, bgRatio)) {\n // return;\n // }\n //\n // existingmarkertext = this.getRoot().find('div.markertexts span.markerlabelstart' + dropZoneNo);\n // if (existingmarkertext.length) {\n // if (dropZone.markertext !== '') {\n // existingmarkertext.html(dropZone.markertext);\n // } else {\n // existingmarkertext.remove();\n // }\n // } else if (dropZone.markertext !== '') {\n // var classnames = 'markertext markertext' + dropZoneNo;\n // this.getRoot().find('div.markertexts').append('' +\n // dropZone.markertext + '');\n // var markerspan = this.getRoot().find('div.ddarea div.markertexts span.markertext' + dropZoneNo);\n // if (markerspan.length) {\n // var handles = line.getHandlePositions();\n // var positionLeft = handles.moveHandles.x - (markerspan.outerWidth() / 2) - 4;\n // var positionTop = handles.moveHandles.y - (markerspan.outerHeight() / 2);\n // markerspan\n // .css('left', positionLeft)\n // .css('top', positionTop);\n // markerspan\n // .data('originX', markerspan.position().left / bgRatio)\n // .data('originY', markerspan.position().top / bgRatio);\n // this.handleElementScale(markerspan, 'center');\n // }\n // }\n //\n // var lineSVG = line.makeSvg(svg[0]);\n // lineSVG.setAttribute('class', 'dropzone ' + colourClass);\n //\n // this.lines[this.Line.length] = line;\n // this.lineSVGs[this.lineSVGs.length] = lineSVG;\n // };\n\n // /**\n // * Draws the drag items on the page (and drop zones if required).\n // * The idea is to re-draw all the drags and drops whenever there is a change\n // * like a widow resize or an item dropped in place.\n // */\n // DrawlinesQuestion.prototype.repositionDrags = function() {\n // var root = this.getRoot(),\n // thisQ = this;\n //\n // root.find('div.draghomes .marker').not('.dragplaceholder').each(function(key, item) {\n // $(item).addClass('unneeded');\n // });\n //\n // root.find('input.choices').each(function(key, input) {\n // var choiceNo = thisQ.getChoiceNoFromElement(input),\n // imageCoords = thisQ.getImageCoords(input);\n //\n // if (imageCoords.length) {\n // var drag = thisQ.getRoot().find('.draghomes' + ' span.marker' + '.choice' + choiceNo).not('.dragplaceholder');\n // drag.remove();\n // for (var i = 0; i < imageCoords.length; i++) {\n // var dragInDrop = drag.clone();\n // // Convert image coords to screen coords.\n // const screenCoords = thisQ.convertToWindowXY(imageCoords[i]);\n // dragInDrop.data('pagex', screenCoords.x).data('pagey', screenCoords.y);\n // // Save image coords to the drag item so we can use it later.\n // dragInDrop.data('imageCoords', imageCoords[i]);\n // // We always save the coordinates in the 1:1 ratio.\n // // So we need to set the scale ratio to 1 for the initial load.\n // dragInDrop.data('scaleRatio', 1);\n // thisQ.sendDragToDrop(dragInDrop, false, true);\n // }\n // thisQ.getDragClone(drag).addClass('active');\n // thisQ.cloneDragIfNeeded(drag);\n // }\n // });\n //\n // // Save the question answer.\n // thisQ.questionAnswer = thisQ.getQuestionAnsweredValues();\n // };\n //\n // /**\n // * Determine what drag items need to be shown and\n // * return coords of all drag items except any that are currently being dragged\n // * based on contents of hidden inputs and whether drags are 'infinite' or how many\n // * drags should be shown.\n // *\n // * @param {jQuery} inputNode\n // * @returns {Point[]} image coordinates of however many copies of the drag item should be shown.\n // */\n // DrawlinesQuestion.prototype.getImageCoords = function(inputNode) {\n // var imageCoords = [],\n // val = $(inputNode).val();\n // if (val !== '') {\n // var coordsStrings = val.split(' ');\n // for (var i = 0; i < coordsStrings.length; i++) {\n // imageCoords[i] = Line.Point.parse(coordsStrings[i]);\n // }\n // }\n // return imageCoords;\n // };\n //\n // /**\n // * Converts the relative x and y position coordinates into\n // * absolute x and y position coordinates.\n // *\n // * @param {Point} point relative to the background image.\n // * @returns {Point} point relative to the page.\n // */\n // DrawlinesQuestion.prototype.convertToWindowXY = function(point) {\n // var bgImage = this.bgImage();\n // // The +1 seems rather odd, but seems to give the best results in\n // // the three main browsers at a range of zoom levels.\n // // (Its due to the 1px border around the image, that shifts the\n // // image pixels by 1 down and to the left.)\n // return point.offset(bgImage.offset().left + 1, bgImage.offset().top + 1);\n // };\n\n // /**\n // * Utility function converting window coordinates to relative to the\n // * background image coordinates.\n // *\n // * @param {Point} point relative to the page.\n // * @returns {Point} point relative to the background image.\n // */\n // DrawlinesQuestion.prototype.convertToBgImgXY = function(point) {\n // var bgImage = this.bgImage();\n // return point.offset(-bgImage.offset().left - 1, -bgImage.offset().top - 1);\n // };\n //\n // /**\n // * Functionality at the end of a drag drop.\n // * @param {jQuery} dragged the marker that was dragged.\n // */\n // DrawlinesQuestion.prototype.dragEnd = function(dragged) {\n // var placed = false,\n // choiceNo = this.getChoiceNoFromElement(dragged),\n // bgRatio = this.bgRatio(),\n // dragXY;\n //\n // dragged.data('pagex', dragged.offset().left).data('pagey', dragged.offset().top);\n // dragXY = new Line.Point(dragged.data('pagex'), dragged.data('pagey'));\n // if (this.coordsInBgImg(dragXY)) {\n // this.sendDragToDrop(dragged, true);\n // placed = true;\n // // Since we already move the drag item to new position.\n // // Remove the image coords if this drag item have it.\n // // We will get the new image coords for this drag item in saveCoordsForChoice.\n // if (dragged.data('imageCoords')) {\n // dragged.data('imageCoords', null);\n // }\n // // It seems that the dragdrop sometimes leaves the drag\n // // one pixel out of position. Put it in exactly the right place.\n // var bgImgXY = this.convertToBgImgXY(dragXY);\n // bgImgXY = new Line.Point(bgImgXY.x / bgRatio, bgImgXY.y / bgRatio);\n // dragged.data('originX', bgImgXY.x).data('originY', bgImgXY.y);\n // }\n //\n // if (!placed) {\n // this.sendDragHome(dragged);\n // this.removeDragIfNeeded(dragged);\n // } else {\n // this.cloneDragIfNeeded(dragged);\n // }\n //\n // this.saveCoordsForChoice(choiceNo);\n // };\n //\n // /**\n // * Makes sure the dragged item always exists within the background image area.\n // *\n // * @param {Point} windowxy\n // * @returns {Point} coordinates\n // */\n // DrawlinesQuestion.prototype.constrainToBgImg = function(windowxy) {\n // var bgImg = this.bgImage(),\n // bgImgXY = this.convertToBgImgXY(windowxy);\n // bgImgXY.x = Math.max(0, bgImgXY.x);\n // bgImgXY.y = Math.max(0, bgImgXY.y);\n // bgImgXY.x = Math.min(bgImg.width(), bgImgXY.x);\n // bgImgXY.y = Math.min(bgImg.height(), bgImgXY.y);\n // return this.convertToWindowXY(bgImgXY);\n // };\n\n //\n // /**\n // * Handle when the window is resized.\n // */\n // DrawlinesQuestion.prototype.handleResize = function() {\n // var thisQ = this,\n // bgRatio = this.bgRatio();\n // if (this.isPrinting) {\n // bgRatio = 1;\n // }\n //\n // this.getRoot().find('div.droparea .marker').not('.beingdragged').each(function(key, drag) {\n // $(drag)\n // .css('left', parseFloat($(drag).data('originX')) * parseFloat(bgRatio))\n // .css('top', parseFloat($(drag).data('originY')) * parseFloat(bgRatio));\n // thisQ.handleElementScale(drag, 'left top');\n // });\n //\n // this.getRoot().find('div.droparea svg.dropzones')\n // .width(this.bgImage().width())\n // .height(this.bgImage().height());\n //\n // for (var dropZoneNo = 0; dropZoneNo < this.visibleDropZones.length; dropZoneNo++) {\n // var dropZone = thisQ.visibleDropZones[dropZoneNo];\n // var originCoords = dropZone.coords;\n // var line = thisQ.lines[dropZoneNo];\n // var lineSVG = thisQ.lineSVGs[dropZoneNo];\n // line.parse(originCoords, bgRatio);\n // line.updateSvg(lineSVG);\n //\n // var handles = line.getHandlePositions();\n // var markerSpan = this.getRoot().find('div.ddarea div.markertexts span.markertext' + dropZoneNo);\n // markerSpan\n // .css('left', handles.moveHandles.x - (markerSpan.outerWidth() / 2) - 4)\n // .css('top', handles.moveHandles.y - (markerSpan.outerHeight() / 2));\n // thisQ.handleElementScale(markerSpan, 'center');\n // }\n // };\n\n // /**\n // * Animate a drag item into a given place.\n // *\n // * @param {jQuery} drag the item to place.\n // * @param {boolean} isScaling Scaling or not.\n // * @param {boolean} initialLoad Whether it is the initial load or not.\n // */\n // DrawlinesQuestion.prototype.sendDragToDrop = function(drag, isScaling, initialLoad = false) {\n // var dropArea = this.dropArea(),\n // bgRatio = this.bgRatio();\n // drag.removeClass('beingdragged').removeClass('unneeded');\n // var dragXY = this.convertToBgImgXY(new Line.Point(drag.data('pagex'), drag.data('pagey')));\n // if (isScaling) {\n // drag.data('originX', dragXY.x / bgRatio).data('originY', dragXY.y / bgRatio);\n // drag.css('left', dragXY.x).css('top', dragXY.y);\n // } else {\n // drag.data('originX', dragXY.x).data('originY', dragXY.y);\n // drag.css('left', dragXY.x * bgRatio).css('top', dragXY.y * bgRatio);\n // }\n // // We need to save the original scale ratio for each draggable item.\n // if (!initialLoad) {\n // // Only set the scale ratio for a current being-dragged item, not for the initial loading.\n // drag.data('scaleRatio', bgRatio);\n // }\n // dropArea.append(drag);\n // this.handleElementScale(drag, 'left top');\n // };\n //\n // /**\n // * Scale the drag if needed.\n // *\n // * @param {jQuery} element the item to place.\n // * @param {String} type scaling type\n // */\n // DrawlinesQuestion.prototype.handleElementScale = function(element, type) {\n // var bgRatio = parseFloat(this.bgRatio());\n // if (this.isPrinting) {\n // bgRatio = 1;\n // }\n // $(element).css({\n // '-webkit-transform': 'scale(' + bgRatio + ')',\n // '-moz-transform': 'scale(' + bgRatio + ')',\n // '-ms-transform': 'scale(' + bgRatio + ')',\n // '-o-transform': 'scale(' + bgRatio + ')',\n // 'transform': 'scale(' + bgRatio + ')',\n // 'transform-origin': type\n // });\n // };\n\n // /**\n // * Sometimes, despite our best efforts, things change in a way that cannot\n // * be specifically caught (e.g. dock expanding or collapsing in Boost).\n // * Therefore, we need to periodically check everything is in the right position.\n // */\n // fixLayoutIfThingsMoved: function() {\n // if (!questionManager.isKeyboardNavigation) {\n // this.handleWindowResize(questionManager.isPrinting);\n // }\n // // We use setTimeout after finishing work, rather than setInterval,\n // // in case positioning things is slow. We want 100 ms gap\n // // between executions, not what setInterval does.\n // setTimeout(function() {\n // questionManager.fixLayoutIfThingsMoved(questionManager.isPrinting);\n // }, 100);\n // },\n\n /**\n * Get the outer div for this question.\n *\n * @return {*}\n */\n DrawlinesQuestion.prototype.getRoot = function() {\n return document.getElementById(this.containerId);\n };\n\n /**\n * Get the img that is the background image.\n *\n * @returns {element|undefined} the DOM element (if any)\n */\n DrawlinesQuestion.prototype.bgImage = function() {\n return this.getRoot().querySelector('img.dropbackground');\n };\n\n /**\n * Returns the coordinates for the line from the SVG.\n * @param {SVGElement} svgEl\n * @returns {Array} the coordinates.\n */\n DrawlinesQuestion.prototype.getSVGLineCoordinates = function(svgEl) {\n\n var circleStartXCoords = svgEl.childNodes[1].getAttribute('cx');\n var circleStartYCoords = svgEl.childNodes[1].getAttribute('cy');\n var circleStartRCoords = svgEl.childNodes[1].getAttribute('r');\n var circleEndXCoords = svgEl.childNodes[2].getAttribute('cx');\n var circleEndYCoords = svgEl.childNodes[2].getAttribute('cy');\n var circleEndRCoords = svgEl.childNodes[2].getAttribute('r');\n return [circleStartXCoords + ',' + circleStartYCoords + ';' + circleStartRCoords,\n circleEndXCoords + ',' + circleEndYCoords + ';' + circleEndRCoords];\n };\n\n /**\n * Return the background ratio.\n *\n * @returns {number} Background ratio.\n */\n DrawlinesQuestion.prototype.bgRatio = function() {\n var bgImg = this.bgImage();\n var bgImgNaturalWidth = bgImg.get(0).naturalWidth;\n var bgImgClientWidth = bgImg.width();\n\n return bgImgClientWidth / bgImgNaturalWidth;\n };\n\n /**\n * Add this line to an SVG graphic.\n *\n * @param {int} lineNumber Line Number\n * @param {SVGElement} svg the SVG image to which to add this drop zone.\n */\n DrawlinesQuestion.prototype.addToSvg = function(lineNumber, svg) {\n this.lineSVGs[lineNumber] = this.lines[lineNumber].makeSvg(svg);\n if (!this.lineSVGs[lineNumber]) {\n return;\n }\n this.lineSVGs[lineNumber].setAttribute('data-dropzone-no', lineNumber);\n if (svg.getAttribute('class') === 'dropzones') {\n this.lineSVGs[lineNumber].setAttribute('class', 'dropzone choice' + lineNumber + ' placed');\n } else {\n this.lineSVGs[lineNumber].setAttribute('class', 'dropzone choice' + lineNumber + ' inactive');\n }\n };\n\n /**\n * Update the line of this drop zone in an SVG image.\n *\n * @param {int} dropzoneNo\n */\n DrawlinesQuestion.prototype.updateSvgEl = function(dropzoneNo) {\n this.lines[dropzoneNo].updateSvg(this.lineSVGs[dropzoneNo]);\n };\n\n /**\n * Start responding to dragging the move handle attached to the line ends (circles).\n *\n * @param {Event} e Event object\n * @param {String} whichHandle which circle handle was moved, i.e., startcircle or endcircle.\n * @param {int} dropzoneNo\n */\n DrawlinesQuestion.prototype.handleCircleMove = function(e, whichHandle, dropzoneNo) {\n var info = dragDrop.prepare(e);\n if (!info.start) {\n return;\n }\n var movingDropZone = this,\n lastX = info.x,\n lastY = info.y,\n dragProxy = this.makeDragProxy(info.x, info.y),\n svg = this.getRoot().querySelector('svg.dropzones'),\n maxX = svg.width.baseVal.value,\n maxY = svg.height.baseVal.value;\n\n dragDrop.start(e, $(dragProxy), function(pageX, pageY) {\n movingDropZone.lines[dropzoneNo].move(whichHandle,\n parseInt(pageX) - parseInt(lastX), parseInt(pageY) - parseInt(lastY), parseInt(maxX), parseInt(maxY));\n lastX = pageX;\n lastY = pageY;\n movingDropZone.updateSvgEl(dropzoneNo);\n movingDropZone.saveCoordsForChoice(dropzoneNo);\n }, function() {\n document.body.removeChild(dragProxy);\n });\n };\n\n /**\n * Start responding to dragging the move handle attached to the line.\n *\n * @param {Event} e Event object\n * @param {int} dropzoneNo\n */\n DrawlinesQuestion.prototype.handleLineMove = function(e, dropzoneNo) {\n var info = dragDrop.prepare(e);\n if (!info.start) {\n return;\n }\n var movingDrag = this,\n lastX = info.x,\n lastY = info.y,\n dragProxy = this.makeDragProxy(info.x, info.y),\n maxX,\n maxY,\n whichSVG = \"\",\n bgImage = this.bgImage(),\n isMoveFromDragsToDropzones,\n isMoveFromDropzonesToDrags,\n svgClass = '';\n\n var selectedElement = this.lineSVGs[dropzoneNo];\n const dropX = e.clientX;\n const dropY = e.clientY;\n\n dragDrop.start(e, $(dragProxy), function(pageX, pageY) {\n\n // The svg's which are associated with this question.\n var closestSVGs = movingDrag.getSvgsClosestToElement(selectedElement);\n\n // Check if the drags need to be moved from one svg to another.\n var closeTo = selectedElement.closest('svg');\n svgClass = closeTo.getAttribute('class');\n\n // Moving the drags between the SVG's.\n // If true, the drag is moved from draghomes SVG to dropZone SVG.\n isMoveFromDragsToDropzones = (svgClass === \"dragshome\");\n\n // If true, the drag is moved from dropZone SVG to draghomes SVG.\n isMoveFromDropzonesToDrags = (svgClass === 'dropzones') &&\n (movingDrag.lines[dropzoneNo].centre1.y > (bgImage.height - 20));\n\n if (isMoveFromDragsToDropzones || isMoveFromDropzonesToDrags) {\n movingDrag.lines[dropzoneNo].addToDropZone('mouse', selectedElement,\n closestSVGs.svgDropZone, closestSVGs.svgDragsHome, dropX, dropY);\n }\n\n // Drag the lines within the SVG\n // Get the dimensions of the selected element's svg.\n closeTo = selectedElement.closest('svg');\n var dimensions = movingDrag.getSvgDimensionsByClass(closeTo, closeTo.getAttribute('class'));\n maxX = dimensions.maxX;\n maxY = dimensions.maxY;\n whichSVG = dimensions.whichSVG;\n\n movingDrag.lines[dropzoneNo].moveDrags(\n parseInt(pageX) - parseInt(lastX), parseInt(pageY) - parseInt(lastY),\n parseInt(maxX), parseInt(maxY), whichSVG);\n lastX = pageX;\n lastY = pageY;\n\n movingDrag.updateSvgEl(dropzoneNo);\n movingDrag.saveCoordsForChoice(dropzoneNo);\n }, function() {\n document.body.removeChild(dragProxy);\n });\n };\n\n /**\n * Make an invisible drag proxy.\n *\n * @param {int} x x position .\n * @param {int} y y position.\n * @returns {HTMLElement} the drag proxy.\n */\n DrawlinesQuestion.prototype.makeDragProxy = function(x, y) {\n var dragProxy = document.createElement('div');\n dragProxy.style.position = 'absolute';\n dragProxy.style.top = y + 'px';\n dragProxy.style.left = x + 'px';\n dragProxy.style.width = '1px';\n dragProxy.style.height = '1px';\n document.body.appendChild(dragProxy);\n return dragProxy;\n };\n\n /**\n * Save the coordinates for a dropped item in the form field.\n *\n * @param {Number} choiceNo which copy of the choice this was.\n **/\n DrawlinesQuestion.prototype.saveCoordsForChoice = function(choiceNo) {\n let imageCoords = [];\n var items = this.getRoot().querySelector('svg g.choice' + choiceNo),\n gEleClassAttributes = '';\n if (items) {\n imageCoords = items.querySelector('polyline').getAttribute('points');\n gEleClassAttributes = items.getAttribute('class');\n // TODO: Kept the below comment as this could be needed for window resizing.\n\n // thiQ = this,\n // bgRatio = this.bgRatio();\n // if (drag.data('scaleRatio') !== bgRatio) {\n // // The scale ratio for the draggable item was changed. We need to update that.\n // drag.data('pagex', drag.offset().left).data('pagey', drag.offset().top);\n // }\n // var dragXY = new Line.Point(drag.data('pagex'), drag.data('pagey'));\n // window.console.log(\"dragXY:\" + dragXY);\n //\n // window.console.log(\"thiQ:\" + thiQ);\n // if (thiQ.coordsInBgImg(dragXY)) {\n // var bgImgXY = thiQ.convertToBgImgXY(dragXY);\n // bgImgXY = new Line.Point(bgImgXY.x / bgRatio, bgImgXY.y / bgRatio);\n // imageCoords[imageCoords.length] = bgImgXY;\n // window.console.log(\"bgImgXY:\" + bgImgXY);\n // }\n // } else if (drag.data('imageCoords')) {\n // imageCoords[imageCoords.length] = drag.data('imageCoords');\n // }\n\n }\n if (gEleClassAttributes !== '' && gEleClassAttributes.includes('placed')) {\n this.getRoot().querySelector('input.choice' + choiceNo).value = imageCoords;\n } else if (gEleClassAttributes !== '' && gEleClassAttributes.includes('inactive')) {\n this.getRoot().querySelector('input.choice' + choiceNo).value = '';\n }\n };\n\n /**\n * Handle key down / press events on svg lines.\n * @param {KeyboardEvent} e\n * @param {SVGElement} drag SVG element being dragged.\n * @param {int} dropzoneNo\n * @param {String} activeElement The element being dragged, whether it is the line or the line endpoints.\n */\n DrawlinesQuestion.prototype.handleKeyPress = function(e, drag, dropzoneNo, activeElement) {\n\n var x = 0,\n y = 0,\n dropzoneElement,\n question = questionManager.getQuestionForEvent(e);\n\n dropzoneElement = drag.closest('g');\n switch (e.code) {\n case 'ArrowLeft':\n case 'KeyA': // A.\n x = -1;\n break;\n case 'ArrowRight':\n case 'KeyD': // D.\n x = 1;\n break;\n case 'ArrowDown':\n case 'KeyS': // S.\n y = 1;\n break;\n case 'ArrowUp':\n case 'KeyW': // W.\n y = -1;\n break;\n case 'Space':\n case 'Escape':\n break;\n default:\n return; // Ingore other keys.\n }\n e.preventDefault();\n\n // Moving the drags between the SVG's.\n var closeTo = drag.closest('svg');\n var svgClass = closeTo.getAttribute('class');\n var maxX,\n maxY,\n whichSVG;\n var bgImage = this.bgImage();\n var closestSVGs = this.getSvgsClosestToElement(drag);\n var isMoveFromDragsToDropzones = (svgClass === \"dragshome\");\n var isMoveFromDropzonesToDrags = (svgClass === 'dropzones') &&\n (question.lines[dropzoneNo].centre1.y > (bgImage.height - 20));\n\n if (isMoveFromDragsToDropzones) {\n question.lines[dropzoneNo].addToDropZone('keyboard', dropzoneElement,\n closestSVGs.svgDropZone, closestSVGs.svgDragsHome, null, null, 'DragsSVG');\n } else if (isMoveFromDropzonesToDrags) {\n question.lines[dropzoneNo].addToDropZone('keyboard', dropzoneElement,\n closestSVGs.svgDropZone, closestSVGs.svgDragsHome, null, null, 'DropZonesSVG');\n }\n\n // Get the dimensions of the selected element's svg.\n closeTo = drag.closest('svg');\n var dimensions = question.getSvgDimensionsByClass(closeTo, closeTo.getAttribute('class'));\n maxX = dimensions.maxX;\n maxY = dimensions.maxY;\n whichSVG = dimensions.whichSVG;\n\n if (activeElement === 'line') {\n // Move the entire line when the focus is on it.\n question.lines[dropzoneNo].moveDrags(parseInt(x), parseInt(y), parseInt(maxX), parseInt(maxY), whichSVG);\n } else {\n // Move the line endpoints.\n question.lines[dropzoneNo].move(activeElement, parseInt(x), parseInt(y), parseInt(maxX), parseInt(maxY));\n }\n question.updateSvgEl(dropzoneNo);\n this.saveCoordsForChoice(dropzoneNo);\n drag.focus();\n };\n\n /**\n * Returns the dimensions of the SVG image to which the drag element belongs.\n *\n * @param {SVG} dragSVG The SVG to which the drag element belongs.\n * @param {String} className Class asscociated with the SVG\n * @return {{whichSVG: (string), maxY: number, maxX: number}}\n */\n DrawlinesQuestion.prototype.getSvgDimensionsByClass = function(dragSVG, className) {\n return {\n maxX: dragSVG.width.baseVal.value,\n maxY: dragSVG.height.baseVal.value,\n whichSVG: className === 'dragshome' ? 'DragsSVG' : 'DropZonesSVG'\n };\n };\n\n /**\n * Returns the SVG's to which the drag element belongs.\n *\n * @param {SVGElement} dragElement The element which is being moved.\n * @return {{svgDragsHome, svgDropZone}}\n */\n DrawlinesQuestion.prototype.getSvgsClosestToElement = function(dragElement) {\n var svgElement = dragElement.closest('svg');\n var svgElementClass = svgElement.getAttribute('class');\n var svgDragsHome, svgDropZone, parent;\n if (svgElementClass === \"dragshome\") {\n svgDragsHome = svgElement;\n parent = svgElement.closest('.ddarea');\n svgDropZone = parent.querySelector('.dropzones');\n } else {\n svgDropZone = svgElement;\n parent = svgElement.closest('.ddarea');\n svgDragsHome = parent.querySelector('.dragshome');\n }\n return {\n svgDropZone: svgDropZone,\n svgDragsHome: svgDragsHome\n };\n };\n\n /**\n * Waits until all images are loaded before setting up question.\n *\n * This function is called from the onLoad of each image, and also polls with\n * a time-out, because image on-loads are allegedly unreliable.\n *\n * @return {Promise}\n */\n DrawlinesQuestion.prototype.waitForAllImagesToBeLoaded = function() {\n\n // This method may get called multiple times (via image on-loads or timeouts.\n // If we are already done, don't do it again.\n if (this.allImagesLoaded) {\n return Promise.resolve(null); // Return a resolved promise;\n }\n const images = document.querySelectorAll('img');\n const promises = Array.from(images).map((img) => {\n return new Promise((resolve, reject) => {\n if (img.complete) {\n resolve(); // If image is already loaded\n } else {\n img.onload = () => resolve();\n img.onerror = () => reject(new Error(`Failed to load image: ${img.src}`));\n }\n });\n });\n\n // Wait for all images to load\n return Promise.all(promises)\n .then(() => {\n // Only mark allImagesLoaded as true once all images are successfully loaded.\n this.allImagesLoaded = true;\n return null; // Return null explicitly to avoid ESLint errors.\n })\n .catch((error) => {\n this.allImagesLoaded = true; // Even on error, set this to avoid re-checking\n throw error; // Rethrow the error so it can be caught in a calling function if necessary.\n });\n };\n\n /**\n * Get any of the images in the drag-drop area that are not yet fully loaded.\n *\n * @returns {boolean} Returns true if images are loaded without errors.\n */\n DrawlinesQuestion.prototype.getNotYetLoadedImages = function() {\n // Get all 'img' elements with the class 'dropbackground' within '.drawlines' inside the root element\n const images = this.getRoot().querySelectorAll('.drawlines img.dropbackground');\n\n // Filter out the images that are already loaded\n Array.from(images).filter((imgNode) => {\n return !this.imageIsLoaded(imgNode);\n });\n };\n\n /**\n * Check if an image has loaded without errors.\n *\n * @param {HTMLImageElement} imgElement an image.\n * @returns {boolean} true if this image has loaded without errors.\n */\n DrawlinesQuestion.prototype.imageIsLoaded = function(imgElement) {\n return imgElement.complete && imgElement.naturalHeight !== 0;\n };\n\n /**\n * Singleton that tracks all the DrawlinesQuestions on this page, and deals\n * with event dispatching.\n *\n * @type {Object}\n */\n var questionManager = {\n\n /**\n * {boolean} ensures that the event handlers are only initialised once per page.\n */\n eventHandlersInitialised: false,\n\n /**\n * {Object} ensures that the marker event handlers are only initialised once per question,\n * indexed by containerId (id on the .que div).\n */\n lineEventHandlersInitialised: {},\n\n /**\n * {boolean} is printing or not.\n */\n isPrinting: false,\n\n /**\n * {boolean} is keyboard navigation.\n */\n isKeyboardNavigation: false,\n\n /**\n * {Object} all the questions on this page, indexed by containerId (id on the .que div).\n */\n questions: {}, // An object containing all the information about each question on the page.\n\n /**\n * @var {int} the number of lines on the form.\n */\n noOfLines: null,\n\n /**\n * @var {DrawlinesQuestion[]} the lines in the preview, indexed by line number.\n */\n dropZones: [],\n\n /**\n * @var {line[]} the question lines in the preview, indexed by line number.\n */\n questionLines: [],\n\n /**\n * Initialise one question.\n *\n * @param {String} containerId the id of the div.que that contains this question.\n * @param {boolean} readOnly whether the question is read-only.\n * @param {Object[]} visibleDropZones data on any drop zones to draw as part of the feedback.\n * @param {Object[]} questionLines\n */\n init: function(containerId, readOnly, visibleDropZones, questionLines) {\n questionManager.questions[containerId] =\n new DrawlinesQuestion(containerId, readOnly, visibleDropZones, questionLines);\n\n questionManager.questions[containerId].updateCoordinates();\n\n if (!questionManager.lineEventHandlersInitialised.hasOwnProperty(containerId)) {\n questionManager.lineEventHandlersInitialised[containerId] = true;\n\n var questionContainer = document.getElementById(containerId);\n if (questionContainer.classList.contains('drawlines') &&\n !questionContainer.classList.contains('qtype_drawlines-readonly')) {\n\n // Add event listeners to the 'previewArea'.\n // For dropzone SVG.\n var dropArea = questionContainer.querySelector('.droparea');\n // Add event listener for mousedown and touchstart events.\n dropArea.addEventListener('mousedown', questionManager.handleDropZoneEventMove);\n dropArea.addEventListener('touchstart', questionManager.handleDropZoneEventMove);\n // Add event listener for keydown and keypress events.\n dropArea.addEventListener('keydown', questionManager.handleKeyPress);\n dropArea.addEventListener('keypress', questionManager.handleKeyPress);\n\n // For draghomes SVG.\n var drags = questionContainer.querySelector('.draghomes');\n // Add event listener for mousedown and touchstart events.\n drags.addEventListener('mousedown', questionManager.handleDragHomeEventMove);\n drags.addEventListener('touchstart', questionManager.handleDragHomeEventMove);\n // Add event listener for keydown and keypress events.\n drags.addEventListener('keydown', questionManager.handleKeyPress);\n drags.addEventListener('keypress', questionManager.handleKeyPress);\n }\n }\n },\n\n // TODO: commented as currently we are not using this function. To be removed later if not needed.\n // /**\n // * Set up the event handlers that make this question type work. (Done once per page.)\n // */\n // setupEventHandlers: function() {\n // $(window).on('resize', function() {\n // questionManager.handleWindowResize(false);\n // });\n // window.addEventListener('beforeprint', function() {\n // questionManager.isPrinting = true;\n // questionManager.handleWindowResize(questionManager.isPrinting);\n // });\n // window.addEventListener('afterprint', function() {\n // questionManager.isPrinting = false;\n // questionManager.handleWindowResize(questionManager.isPrinting);\n // });\n // setTimeout(function() {\n // questionManager.fixLayoutIfThingsMoved();\n // }, 100);\n // },\n\n /**\n * Handle mouse and touch events for dropzone svg.\n *\n * @param {Event} event\n */\n handleDropZoneEventMove: function(event) {\n var dropzoneElement, dropzoneNo;\n var question = questionManager.getQuestionForEvent(event);\n if (event.target.closest('.dropzone .startcircle.shape')) {\n // Dragging the move handle circle attached to the start of the line.\n dropzoneElement = event.target.closest('g');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n question.handleCircleMove(event, 'startcircle', dropzoneNo);\n } else if (event.target.closest('.dropzone .endcircle.shape')) {\n // Dragging the move handle circle attached to the end of the line.\n dropzoneElement = event.target.closest('g');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n question.handleCircleMove(event, 'endcircle', dropzoneNo);\n } else if (event.target.closest('polyline.shape')) {\n // Dragging the entire line.\n dropzoneElement = event.target.closest('g');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n question.handleLineMove(event, dropzoneNo);\n }\n },\n\n /**\n * Handle mouse and touch events for dragshome svg.\n *\n * @param {Event} event\n */\n handleDragHomeEventMove: function(event) {\n var dropzoneElement, dropzoneNo;\n var question = questionManager.getQuestionForEvent(event);\n if (event.target.closest('g')) {\n dropzoneElement = event.target.closest('g');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n question.handleLineMove(event, dropzoneNo);\n question.saveCoordsForChoice(dropzoneNo);\n }\n },\n\n /**\n * Handle key down / press events on markers.\n *\n * @param {Event} e\n */\n handleKeyPress: function(e) {\n var question = questionManager.getQuestionForEvent(e);\n var dropzoneElement, dropzoneNo, drag, activeElement;\n if (e.target.closest('.dropzone circle.startcircle')) {\n dropzoneElement = e.target.closest('.dropzone');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n drag = e.target.closest('.dropzone circle.startcircle');\n activeElement = 'startcircle';\n } else if (e.target.closest('.dropzone circle.endcircle')) {\n drag = e.target.closest('.dropzone circle.endcircle');\n dropzoneElement = e.target.closest('.dropzone');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n activeElement = 'endcircle';\n } else if (e.target.closest('g.dropzone')) {\n drag = e.target.closest('g.dropzone');\n dropzoneElement = e.target.closest('.dropzone');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n activeElement = 'line';\n }\n if (question && dropzoneElement) {\n question.handleKeyPress(e, drag, dropzoneNo, activeElement);\n }\n },\n\n /**\n * Handle when the window is resized.\n *\n * @param {boolean} isPrinting\n */\n handleWindowResize: function(isPrinting) {\n for (var containerId in questionManager.questions) {\n if (questionManager.questions.hasOwnProperty(containerId)) {\n questionManager.questions[containerId].isPrinting = isPrinting;\n questionManager.questions[containerId].handleResize();\n }\n }\n },\n\n /**\n * Given an event, work out which question it effects.\n *\n * @param {Event} e the event.\n * @returns {DrawlinesQuestion|undefined} The question, or undefined.\n */\n getQuestionForEvent: function(e) {\n var containerId = $(e.currentTarget).closest('.que.drawlines').attr('id');\n return questionManager.questions[containerId];\n },\n };\n\n /**\n * @alias module:qtype_drawlines/question\n */\n return {\n /**\n * Initialise one drag-drop markers question.\n *\n * @param {String} containerId id of the outer div for this question.\n * @param {boolean} readOnly whether the question is being displayed read-only.\n * @param {String[]} visibleDropZones the geometry of any drop-zones to show.\n * @param {Object[]} questionLines\n */\n init: questionManager.init,\n };\n});\n"],"names":["define","$","dragDrop","Line","DrawlinesQuestion","containerId","readOnly","visibleDropZones","questionLines","thisQ","this","lineSVGs","lines","svgEl","getRoot","classList","add","allImagesLoaded","waitForAllImagesToBeLoaded","then","drawDropzone","catch","error","prototype","updateCoordinates","line","length","coordinates","getSVGLineCoordinates","parse","updateSvgEl","parseCoordinates","lineType","bits","split","slice","Error","bgImage","svg","querySelector","rootElement","style","position","top","height","innerHTML","width","drawSVGLines","startcoordinates","endcoordinates","draginitialcoords","draghomeSvg","dropzoneSvg","undefined","coords","type","make","labelstart","labelend","addToSvg","document","getElementById","childNodes","getAttribute","bgRatio","bgImg","bgImgNaturalWidth","get","naturalWidth","lineNumber","makeSvg","setAttribute","dropzoneNo","updateSvg","handleCircleMove","e","whichHandle","info","prepare","start","movingDropZone","lastX","x","lastY","y","dragProxy","makeDragProxy","maxX","baseVal","value","maxY","pageX","pageY","move","parseInt","saveCoordsForChoice","body","removeChild","handleLineMove","isMoveFromDragsToDropzones","isMoveFromDropzonesToDrags","movingDrag","whichSVG","svgClass","selectedElement","dropX","clientX","dropY","clientY","closestSVGs","getSvgsClosestToElement","closeTo","closest","centre1","addToDropZone","svgDropZone","svgDragsHome","dimensions","getSvgDimensionsByClass","moveDrags","createElement","left","appendChild","choiceNo","imageCoords","items","gEleClassAttributes","includes","handleKeyPress","drag","activeElement","dropzoneElement","question","questionManager","getQuestionForEvent","code","preventDefault","focus","dragSVG","className","dragElement","svgElement","Promise","resolve","images","querySelectorAll","promises","Array","from","map","img","reject","complete","onload","onerror","src","all","getNotYetLoadedImages","filter","imgNode","imageIsLoaded","imgElement","naturalHeight","eventHandlersInitialised","lineEventHandlersInitialised","isPrinting","isKeyboardNavigation","questions","noOfLines","dropZones","init","hasOwnProperty","questionContainer","contains","dropArea","addEventListener","handleDropZoneEventMove","drags","handleDragHomeEventMove","event","target","dataset","handleWindowResize","handleResize","currentTarget","attr"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"question.min.js","sources":["../src/question.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * JavaScript to allow dragging options for lines (using mouse down or touch) or tab through lines using keyboard.\n *\n * @module qtype_drawlines/question\n * @copyright 2024 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine([\n 'jquery',\n 'core/dragdrop',\n 'qtype_drawlines/line',\n 'core/key_codes',\n 'core_form/changechecker',\n], function(\n $,\n dragDrop,\n Line,\n) {\n\n \"use strict\";\n\n /**\n * Object to handle one drag-drop markers question.\n *\n * @param {String} containerId id of the outer div for this question.\n * @param {boolean} readOnly whether the question is being displayed read-only.\n * @param {Object[]} visibleDropZones the geometry of any drop-zones to show.\n * Objects have fields line, coords and markertext.\n * @param {line[]} questionLines\n * @constructor\n */\n function DrawlinesQuestion(containerId, readOnly, visibleDropZones, questionLines) {\n var thisQ = this;\n this.containerId = containerId;\n this.visibleDropZones = visibleDropZones;\n this.questionLines = questionLines;\n this.lineSVGs = [];\n this.lines = [];\n this.svgEl = null;\n if (readOnly) {\n this.getRoot().classList.add('qtype_drawlines-readonly');\n }\n thisQ.allImagesLoaded = false;\n // Get all images that are not yet loaded\n thisQ.waitForAllImagesToBeLoaded()\n .then(() => {\n thisQ.drawDropzone(); // Call your function here\n return null;\n })\n .catch((error) => {\n throw error;\n });\n }\n\n /**\n * Update the coordinates from a particular string.\n */\n DrawlinesQuestion.prototype.updateCoordinates = function() {\n // We don't need to scale the shape for editing form.\n for (var line = 0; line < this.lineSVGs.length; line++) {\n var coordinates = this.getSVGLineCoordinates(this.lineSVGs[line]);\n if (!this.lines[line].parse(coordinates[0], coordinates[1], 1)) {\n // Invalid coordinates. Don't update the preview.\n return;\n }\n this.updateSvgEl(line);\n }\n };\n\n /**\n * Parse the coordinates from a particular string.\n *\n * @param {String} coordinates The coordinates to be parsed. The values are in the format: x1,y1 x2,y2.\n * Except for infinite line type where it's in the format x1,y1 x2,y2, x3,y3, x4,y4.\n * Here, x1,y1 and x4,y4 are the two very end points of the infinite line and\n * x2,y2 and x3,y3 are the pints with the handles.\n * @param {String} lineType The type of the line.\n */\n DrawlinesQuestion.prototype.parseCoordinates = function(coordinates, lineType) {\n var bits = coordinates.split(' ');\n if (lineType === 'lineinfinite' && bits.length !== 2) {\n // Remove the first and last coordinates.\n bits = bits.slice(1, -1);\n }\n if (bits.length !== 2) {\n throw new Error(coordinates + ' is not a valid point');\n }\n return bits;\n };\n\n /**\n * Draws the svg lines of any drop zones that should be visible for feedback purposes.\n */\n DrawlinesQuestion.prototype.drawDropzone = function() {\n var bgImage = this.bgImage();\n var svg = this.getRoot().querySelector('svg.dropzones');\n var rootElement = this.getRoot();\n rootElement.querySelector('.que-dlines-dropzone').style.position = 'relative';\n rootElement.querySelector('.que-dlines-dropzone').style.top = (bgImage.height + 1) * -1 + \"px\";\n rootElement.querySelector('.que-dlines-dropzone').style.height = bgImage.height + \"px\";\n rootElement.querySelector('.droparea').style.height = bgImage.height + \"px\";\n if (!svg) {\n var dropZone = this.getRoot().querySelector('.que-dlines-dropzone');\n dropZone.innerHTML =\n '';\n }\n this.drawSVGLines(this.questionLines);\n };\n\n /**\n * Draws the svg lines of any drop zones.\n *\n * @param {Object[]} questionLines\n */\n DrawlinesQuestion.prototype.drawSVGLines = function(questionLines) {\n var bgImage = this.bgImage(),\n height, startcoordinates, endcoordinates, draginitialcoords;\n\n var drags = this.getRoot().querySelector('.draghomes');\n drags.innerHTML =\n '';\n\n var draghomeSvg = this.getRoot().querySelector('.dragshome');\n var dropzoneSvg = this.getRoot().querySelector('.dropzones');\n var initialHeight = 25;\n for (let line = 0; line < questionLines.length; line++) {\n height = initialHeight + line * 50;\n startcoordinates = '50,' + height + ';10';\n endcoordinates = '200,' + height + ';10';\n\n // Check if the lines are to be set with initial coordinates.\n draginitialcoords = this.visibleDropZones['c' + line];\n if (draginitialcoords !== undefined && draginitialcoords !== '') {\n // The visibleDropZones array holds the response in the format x1,y1 x2,y2 - to be added to svgdropzone.\n var coords = this.parseCoordinates(draginitialcoords, questionLines[line].type);\n startcoordinates = coords[0] + ';10';\n endcoordinates = coords[1] + ';10';\n this.lines[line] = Line.make(\n [startcoordinates, endcoordinates],\n [questionLines[line].labelstart, questionLines[line].labelend],\n questionLines[line].type\n );\n this.addToSvg(line, dropzoneSvg);\n } else {\n // Need to be added to draghomeSvg.\n this.lines[line] = Line.make(\n [startcoordinates, endcoordinates],\n [questionLines[line].labelstart, questionLines[line].labelend],\n questionLines[line].type\n );\n this.addToSvg(line, draghomeSvg);\n }\n }\n };\n\n // TODO: The below methods can be refractored for window resizing.\n //\n // /**\n // * Adds a dropzone line with colour, coords and link provided to the array of Lines.\n // *\n // * @param {jQuery} svg the SVG image to which to add this drop zone.\n // * @param {int} dropZoneNo which drop-zone to add.\n // * @param {string} colourClass class name\n // */\n // DrawlinesQuestion.prototype.addDropzone = function(svg, dropZoneNo, colourClass) {\n // var dropZone = this.visibleDropZones[dropZoneNo],\n // line = Line.make(dropZone.line, ''),\n // existingmarkertext,\n // bgRatio = this.bgRatio();\n // if (!line.parse(dropZone.coords, bgRatio)) {\n // return;\n // }\n //\n // existingmarkertext = this.getRoot().find('div.markertexts span.markerlabelstart' + dropZoneNo);\n // if (existingmarkertext.length) {\n // if (dropZone.markertext !== '') {\n // existingmarkertext.html(dropZone.markertext);\n // } else {\n // existingmarkertext.remove();\n // }\n // } else if (dropZone.markertext !== '') {\n // var classnames = 'markertext markertext' + dropZoneNo;\n // this.getRoot().find('div.markertexts').append('' +\n // dropZone.markertext + '');\n // var markerspan = this.getRoot().find('div.ddarea div.markertexts span.markertext' + dropZoneNo);\n // if (markerspan.length) {\n // var handles = line.getHandlePositions();\n // var positionLeft = handles.moveHandles.x - (markerspan.outerWidth() / 2) - 4;\n // var positionTop = handles.moveHandles.y - (markerspan.outerHeight() / 2);\n // markerspan\n // .css('left', positionLeft)\n // .css('top', positionTop);\n // markerspan\n // .data('originX', markerspan.position().left / bgRatio)\n // .data('originY', markerspan.position().top / bgRatio);\n // this.handleElementScale(markerspan, 'center');\n // }\n // }\n //\n // var lineSVG = line.makeSvg(svg[0]);\n // lineSVG.setAttribute('class', 'dropzone ' + colourClass);\n //\n // this.lines[this.Line.length] = line;\n // this.lineSVGs[this.lineSVGs.length] = lineSVG;\n // };\n\n // /**\n // * Draws the drag items on the page (and drop zones if required).\n // * The idea is to re-draw all the drags and drops whenever there is a change\n // * like a widow resize or an item dropped in place.\n // */\n // DrawlinesQuestion.prototype.repositionDrags = function() {\n // var root = this.getRoot(),\n // thisQ = this;\n //\n // root.find('div.draghomes .marker').not('.dragplaceholder').each(function(key, item) {\n // $(item).addClass('unneeded');\n // });\n //\n // root.find('input.choices').each(function(key, input) {\n // var choiceNo = thisQ.getChoiceNoFromElement(input),\n // imageCoords = thisQ.getImageCoords(input);\n //\n // if (imageCoords.length) {\n // var drag = thisQ.getRoot().find('.draghomes' + ' span.marker' + '.choice' + choiceNo).not('.dragplaceholder');\n // drag.remove();\n // for (var i = 0; i < imageCoords.length; i++) {\n // var dragInDrop = drag.clone();\n // // Convert image coords to screen coords.\n // const screenCoords = thisQ.convertToWindowXY(imageCoords[i]);\n // dragInDrop.data('pagex', screenCoords.x).data('pagey', screenCoords.y);\n // // Save image coords to the drag item so we can use it later.\n // dragInDrop.data('imageCoords', imageCoords[i]);\n // // We always save the coordinates in the 1:1 ratio.\n // // So we need to set the scale ratio to 1 for the initial load.\n // dragInDrop.data('scaleRatio', 1);\n // thisQ.sendDragToDrop(dragInDrop, false, true);\n // }\n // thisQ.getDragClone(drag).addClass('active');\n // thisQ.cloneDragIfNeeded(drag);\n // }\n // });\n //\n // // Save the question answer.\n // thisQ.questionAnswer = thisQ.getQuestionAnsweredValues();\n // };\n //\n // /**\n // * Determine what drag items need to be shown and\n // * return coords of all drag items except any that are currently being dragged\n // * based on contents of hidden inputs and whether drags are 'infinite' or how many\n // * drags should be shown.\n // *\n // * @param {jQuery} inputNode\n // * @returns {Point[]} image coordinates of however many copies of the drag item should be shown.\n // */\n // DrawlinesQuestion.prototype.getImageCoords = function(inputNode) {\n // var imageCoords = [],\n // val = $(inputNode).val();\n // if (val !== '') {\n // var coordsStrings = val.split(' ');\n // for (var i = 0; i < coordsStrings.length; i++) {\n // imageCoords[i] = Line.Point.parse(coordsStrings[i]);\n // }\n // }\n // return imageCoords;\n // };\n //\n // /**\n // * Converts the relative x and y position coordinates into\n // * absolute x and y position coordinates.\n // *\n // * @param {Point} point relative to the background image.\n // * @returns {Point} point relative to the page.\n // */\n // DrawlinesQuestion.prototype.convertToWindowXY = function(point) {\n // var bgImage = this.bgImage();\n // // The +1 seems rather odd, but seems to give the best results in\n // // the three main browsers at a range of zoom levels.\n // // (Its due to the 1px border around the image, that shifts the\n // // image pixels by 1 down and to the left.)\n // return point.offset(bgImage.offset().left + 1, bgImage.offset().top + 1);\n // };\n\n // /**\n // * Utility function converting window coordinates to relative to the\n // * background image coordinates.\n // *\n // * @param {Point} point relative to the page.\n // * @returns {Point} point relative to the background image.\n // */\n // DrawlinesQuestion.prototype.convertToBgImgXY = function(point) {\n // var bgImage = this.bgImage();\n // return point.offset(-bgImage.offset().left - 1, -bgImage.offset().top - 1);\n // };\n //\n // /**\n // * Functionality at the end of a drag drop.\n // * @param {jQuery} dragged the marker that was dragged.\n // */\n // DrawlinesQuestion.prototype.dragEnd = function(dragged) {\n // var placed = false,\n // choiceNo = this.getChoiceNoFromElement(dragged),\n // bgRatio = this.bgRatio(),\n // dragXY;\n //\n // dragged.data('pagex', dragged.offset().left).data('pagey', dragged.offset().top);\n // dragXY = new Line.Point(dragged.data('pagex'), dragged.data('pagey'));\n // if (this.coordsInBgImg(dragXY)) {\n // this.sendDragToDrop(dragged, true);\n // placed = true;\n // // Since we already move the drag item to new position.\n // // Remove the image coords if this drag item have it.\n // // We will get the new image coords for this drag item in saveCoordsForChoice.\n // if (dragged.data('imageCoords')) {\n // dragged.data('imageCoords', null);\n // }\n // // It seems that the dragdrop sometimes leaves the drag\n // // one pixel out of position. Put it in exactly the right place.\n // var bgImgXY = this.convertToBgImgXY(dragXY);\n // bgImgXY = new Line.Point(bgImgXY.x / bgRatio, bgImgXY.y / bgRatio);\n // dragged.data('originX', bgImgXY.x).data('originY', bgImgXY.y);\n // }\n //\n // if (!placed) {\n // this.sendDragHome(dragged);\n // this.removeDragIfNeeded(dragged);\n // } else {\n // this.cloneDragIfNeeded(dragged);\n // }\n //\n // this.saveCoordsForChoice(choiceNo);\n // };\n //\n // /**\n // * Makes sure the dragged item always exists within the background image area.\n // *\n // * @param {Point} windowxy\n // * @returns {Point} coordinates\n // */\n // DrawlinesQuestion.prototype.constrainToBgImg = function(windowxy) {\n // var bgImg = this.bgImage(),\n // bgImgXY = this.convertToBgImgXY(windowxy);\n // bgImgXY.x = Math.max(0, bgImgXY.x);\n // bgImgXY.y = Math.max(0, bgImgXY.y);\n // bgImgXY.x = Math.min(bgImg.width(), bgImgXY.x);\n // bgImgXY.y = Math.min(bgImg.height(), bgImgXY.y);\n // return this.convertToWindowXY(bgImgXY);\n // };\n\n //\n // /**\n // * Handle when the window is resized.\n // */\n // DrawlinesQuestion.prototype.handleResize = function() {\n // var thisQ = this,\n // bgRatio = this.bgRatio();\n // if (this.isPrinting) {\n // bgRatio = 1;\n // }\n //\n // this.getRoot().find('div.droparea .marker').not('.beingdragged').each(function(key, drag) {\n // $(drag)\n // .css('left', parseFloat($(drag).data('originX')) * parseFloat(bgRatio))\n // .css('top', parseFloat($(drag).data('originY')) * parseFloat(bgRatio));\n // thisQ.handleElementScale(drag, 'left top');\n // });\n //\n // this.getRoot().find('div.droparea svg.dropzones')\n // .width(this.bgImage().width())\n // .height(this.bgImage().height());\n //\n // for (var dropZoneNo = 0; dropZoneNo < this.visibleDropZones.length; dropZoneNo++) {\n // var dropZone = thisQ.visibleDropZones[dropZoneNo];\n // var originCoords = dropZone.coords;\n // var line = thisQ.lines[dropZoneNo];\n // var lineSVG = thisQ.lineSVGs[dropZoneNo];\n // line.parse(originCoords, bgRatio);\n // line.updateSvg(lineSVG);\n //\n // var handles = line.getHandlePositions();\n // var markerSpan = this.getRoot().find('div.ddarea div.markertexts span.markertext' + dropZoneNo);\n // markerSpan\n // .css('left', handles.moveHandles.x - (markerSpan.outerWidth() / 2) - 4)\n // .css('top', handles.moveHandles.y - (markerSpan.outerHeight() / 2));\n // thisQ.handleElementScale(markerSpan, 'center');\n // }\n // };\n\n // /**\n // * Animate a drag item into a given place.\n // *\n // * @param {jQuery} drag the item to place.\n // * @param {boolean} isScaling Scaling or not.\n // * @param {boolean} initialLoad Whether it is the initial load or not.\n // */\n // DrawlinesQuestion.prototype.sendDragToDrop = function(drag, isScaling, initialLoad = false) {\n // var dropArea = this.dropArea(),\n // bgRatio = this.bgRatio();\n // drag.removeClass('beingdragged').removeClass('unneeded');\n // var dragXY = this.convertToBgImgXY(new Line.Point(drag.data('pagex'), drag.data('pagey')));\n // if (isScaling) {\n // drag.data('originX', dragXY.x / bgRatio).data('originY', dragXY.y / bgRatio);\n // drag.css('left', dragXY.x).css('top', dragXY.y);\n // } else {\n // drag.data('originX', dragXY.x).data('originY', dragXY.y);\n // drag.css('left', dragXY.x * bgRatio).css('top', dragXY.y * bgRatio);\n // }\n // // We need to save the original scale ratio for each draggable item.\n // if (!initialLoad) {\n // // Only set the scale ratio for a current being-dragged item, not for the initial loading.\n // drag.data('scaleRatio', bgRatio);\n // }\n // dropArea.append(drag);\n // this.handleElementScale(drag, 'left top');\n // };\n //\n // /**\n // * Scale the drag if needed.\n // *\n // * @param {jQuery} element the item to place.\n // * @param {String} type scaling type\n // */\n // DrawlinesQuestion.prototype.handleElementScale = function(element, type) {\n // var bgRatio = parseFloat(this.bgRatio());\n // if (this.isPrinting) {\n // bgRatio = 1;\n // }\n // $(element).css({\n // '-webkit-transform': 'scale(' + bgRatio + ')',\n // '-moz-transform': 'scale(' + bgRatio + ')',\n // '-ms-transform': 'scale(' + bgRatio + ')',\n // '-o-transform': 'scale(' + bgRatio + ')',\n // 'transform': 'scale(' + bgRatio + ')',\n // 'transform-origin': type\n // });\n // };\n\n // /**\n // * Sometimes, despite our best efforts, things change in a way that cannot\n // * be specifically caught (e.g. dock expanding or collapsing in Boost).\n // * Therefore, we need to periodically check everything is in the right position.\n // */\n // fixLayoutIfThingsMoved: function() {\n // if (!questionManager.isKeyboardNavigation) {\n // this.handleWindowResize(questionManager.isPrinting);\n // }\n // // We use setTimeout after finishing work, rather than setInterval,\n // // in case positioning things is slow. We want 100 ms gap\n // // between executions, not what setInterval does.\n // setTimeout(function() {\n // questionManager.fixLayoutIfThingsMoved(questionManager.isPrinting);\n // }, 100);\n // },\n\n /**\n * Get the outer div for this question.\n *\n * @return {*}\n */\n DrawlinesQuestion.prototype.getRoot = function() {\n return document.getElementById(this.containerId);\n };\n\n /**\n * Get the img that is the background image.\n *\n * @returns {element|undefined} the DOM element (if any)\n */\n DrawlinesQuestion.prototype.bgImage = function() {\n return this.getRoot().querySelector('img.dropbackground');\n };\n\n /**\n * Returns the coordinates for the line from the SVG.\n * @param {SVGElement} svgEl\n * @returns {Array} the coordinates.\n */\n DrawlinesQuestion.prototype.getSVGLineCoordinates = function(svgEl) {\n\n var circleStartXCoords = svgEl.childNodes[1].getAttribute('cx');\n var circleStartYCoords = svgEl.childNodes[1].getAttribute('cy');\n var circleStartRCoords = svgEl.childNodes[1].getAttribute('r');\n var circleEndXCoords = svgEl.childNodes[2].getAttribute('cx');\n var circleEndYCoords = svgEl.childNodes[2].getAttribute('cy');\n var circleEndRCoords = svgEl.childNodes[2].getAttribute('r');\n return [circleStartXCoords + ',' + circleStartYCoords + ';' + circleStartRCoords,\n circleEndXCoords + ',' + circleEndYCoords + ';' + circleEndRCoords];\n };\n\n /**\n * Return the background ratio.\n *\n * @returns {number} Background ratio.\n */\n DrawlinesQuestion.prototype.bgRatio = function() {\n var bgImg = this.bgImage();\n var bgImgNaturalWidth = bgImg.get(0).naturalWidth;\n var bgImgClientWidth = bgImg.width();\n\n return bgImgClientWidth / bgImgNaturalWidth;\n };\n\n /**\n * Add this line to an SVG graphic.\n *\n * @param {int} lineNumber Line Number\n * @param {SVGElement} svg the SVG image to which to add this drop zone.\n */\n DrawlinesQuestion.prototype.addToSvg = function(lineNumber, svg) {\n this.lineSVGs[lineNumber] = this.lines[lineNumber].makeSvg(svg);\n if (!this.lineSVGs[lineNumber]) {\n return;\n }\n this.lineSVGs[lineNumber].setAttribute('data-dropzone-no', lineNumber);\n if (svg.getAttribute('class') === 'dropzones') {\n this.lineSVGs[lineNumber].setAttribute('class', 'dropzone choice' + lineNumber + ' placed');\n } else {\n this.lineSVGs[lineNumber].setAttribute('class', 'dropzone choice' + lineNumber + ' inactive');\n }\n };\n\n /**\n * Update the line of this drop zone in an SVG image.\n *\n * @param {int} dropzoneNo\n */\n DrawlinesQuestion.prototype.updateSvgEl = function(dropzoneNo) {\n this.lines[dropzoneNo].updateSvg(this.lineSVGs[dropzoneNo]);\n };\n\n /**\n * Start responding to dragging the move handle attached to the line ends (circles).\n *\n * @param {Event} e Event object\n * @param {String} whichHandle which circle handle was moved, i.e., startcircle or endcircle.\n * @param {int} dropzoneNo\n */\n DrawlinesQuestion.prototype.handleCircleMove = function(e, whichHandle, dropzoneNo) {\n var info = dragDrop.prepare(e);\n if (!info.start) {\n return;\n }\n var movingDropZone = this,\n lastX = info.x,\n lastY = info.y,\n dragProxy = this.makeDragProxy(info.x, info.y),\n svg = this.getRoot().querySelector('svg.dropzones'),\n maxX = svg.width.baseVal.value,\n maxY = svg.height.baseVal.value;\n\n dragDrop.start(e, $(dragProxy), function(pageX, pageY) {\n movingDropZone.lines[dropzoneNo].move(whichHandle,\n parseInt(pageX) - parseInt(lastX), parseInt(pageY) - parseInt(lastY), parseInt(maxX), parseInt(maxY));\n lastX = pageX;\n lastY = pageY;\n movingDropZone.updateSvgEl(dropzoneNo);\n movingDropZone.saveCoordsForChoice(dropzoneNo);\n }, function() {\n document.body.removeChild(dragProxy);\n });\n };\n\n /**\n * Start responding to dragging the move handle attached to the line.\n *\n * @param {Event} e Event object\n * @param {int} dropzoneNo\n */\n DrawlinesQuestion.prototype.handleLineMove = function(e, dropzoneNo) {\n var info = dragDrop.prepare(e);\n if (!info.start) {\n return;\n }\n var movingDrag = this,\n lastX = info.x,\n lastY = info.y,\n dragProxy = this.makeDragProxy(info.x, info.y),\n maxX,\n maxY,\n whichSVG = \"\",\n bgImage = this.bgImage(),\n isMoveFromDragsToDropzones,\n isMoveFromDropzonesToDrags,\n svgClass;\n\n var selectedElement = this.lineSVGs[dropzoneNo];\n const dropX = e.clientX;\n const dropY = e.clientY;\n\n dragDrop.start(e, $(dragProxy), function(pageX, pageY) {\n\n // The svg's which are associated with this question.\n var closestSVGs = movingDrag.getSvgsClosestToElement(selectedElement);\n\n // Check if the drags need to be moved from one svg to another.\n var closeTo = selectedElement.closest('svg');\n svgClass = closeTo.getAttribute('class');\n\n // Moving the drags between the SVG's.\n // If true, the drag is moved from draghomes SVG to dropZone SVG.\n isMoveFromDragsToDropzones = (svgClass === \"dragshome\");\n\n // If true, the drag is moved from dropZone SVG to draghomes SVG.\n isMoveFromDropzonesToDrags = (svgClass === 'dropzones') &&\n (movingDrag.lines[dropzoneNo].centre1.y > (bgImage.height - 20));\n\n if (isMoveFromDragsToDropzones || isMoveFromDropzonesToDrags) {\n movingDrag.lines[dropzoneNo].addToDropZone('mouse', selectedElement,\n closestSVGs.svgDropZone, closestSVGs.svgDragsHome, dropX, dropY);\n }\n\n // Drag the lines within the SVG\n // Get the dimensions of the selected element's svg.\n closeTo = selectedElement.closest('svg');\n var dimensions = movingDrag.getSvgDimensionsByClass(closeTo, closeTo.getAttribute('class'));\n maxX = dimensions.maxX;\n maxY = dimensions.maxY;\n whichSVG = dimensions.whichSVG;\n\n movingDrag.lines[dropzoneNo].moveDrags(\n parseInt(pageX) - parseInt(lastX), parseInt(pageY) - parseInt(lastY),\n parseInt(maxX), parseInt(maxY), whichSVG);\n lastX = pageX;\n lastY = pageY;\n\n movingDrag.updateSvgEl(dropzoneNo);\n movingDrag.saveCoordsForChoice(dropzoneNo);\n }, function() {\n document.body.removeChild(dragProxy);\n });\n };\n\n /**\n * Make an invisible drag proxy.\n *\n * @param {int} x x position .\n * @param {int} y y position.\n * @returns {HTMLElement} the drag proxy.\n */\n DrawlinesQuestion.prototype.makeDragProxy = function(x, y) {\n var dragProxy = document.createElement('div');\n dragProxy.style.position = 'absolute';\n dragProxy.style.top = y + 'px';\n dragProxy.style.left = x + 'px';\n dragProxy.style.width = '1px';\n dragProxy.style.height = '1px';\n document.body.appendChild(dragProxy);\n return dragProxy;\n };\n\n /**\n * Save the coordinates for a dropped item in the form field.\n *\n * @param {Number} choiceNo which copy of the choice this was.\n **/\n DrawlinesQuestion.prototype.saveCoordsForChoice = function(choiceNo) {\n let imageCoords = [];\n var items = this.getRoot().querySelector('svg g.choice' + choiceNo),\n gEleClassAttributes = '';\n if (items) {\n imageCoords = items.querySelector('polyline').getAttribute('points');\n gEleClassAttributes = items.getAttribute('class');\n // TODO: Kept the below comment as this could be needed for window resizing.\n\n // thiQ = this,\n // bgRatio = this.bgRatio();\n // if (drag.data('scaleRatio') !== bgRatio) {\n // // The scale ratio for the draggable item was changed. We need to update that.\n // drag.data('pagex', drag.offset().left).data('pagey', drag.offset().top);\n // }\n // var dragXY = new Line.Point(drag.data('pagex'), drag.data('pagey'));\n // window.console.log(\"dragXY:\" + dragXY);\n //\n // window.console.log(\"thiQ:\" + thiQ);\n // if (thiQ.coordsInBgImg(dragXY)) {\n // var bgImgXY = thiQ.convertToBgImgXY(dragXY);\n // bgImgXY = new Line.Point(bgImgXY.x / bgRatio, bgImgXY.y / bgRatio);\n // imageCoords[imageCoords.length] = bgImgXY;\n // window.console.log(\"bgImgXY:\" + bgImgXY);\n // }\n // } else if (drag.data('imageCoords')) {\n // imageCoords[imageCoords.length] = drag.data('imageCoords');\n // }\n\n }\n if (gEleClassAttributes !== '' && gEleClassAttributes.includes('placed')) {\n this.getRoot().querySelector('input.choice' + choiceNo).value = imageCoords;\n } else if (gEleClassAttributes !== '' && gEleClassAttributes.includes('inactive')) {\n this.getRoot().querySelector('input.choice' + choiceNo).value = '';\n }\n };\n\n /**\n * Handle key down / press events on svg lines.\n *\n * @param {KeyboardEvent} e\n * @param {SVGElement} drag SVG element being dragged.\n * @param {int} dropzoneNo\n * @param {String} activeElement The element being dragged, whether it is the line or the line endpoints.\n */\n DrawlinesQuestion.prototype.handleKeyPress = function(e, drag, dropzoneNo, activeElement) {\n\n var x = 0,\n y = 0,\n dropzoneElement,\n question = questionManager.getQuestionForEvent(e);\n\n dropzoneElement = drag.closest('g');\n switch (e.code) {\n case 'ArrowLeft':\n case 'KeyA': // A.\n x = -1;\n break;\n case 'ArrowRight':\n case 'KeyD': // D.\n x = 1;\n break;\n case 'ArrowDown':\n case 'KeyS': // S.\n y = 1;\n break;\n case 'ArrowUp':\n case 'KeyW': // W.\n y = -1;\n break;\n case 'Space':\n case 'Escape':\n break;\n default:\n return; // Ingore other keys.\n }\n e.preventDefault();\n\n // Moving the drags between the SVG's.\n var closeTo = drag.closest('svg');\n var svgClass = closeTo.getAttribute('class');\n var maxX,\n maxY,\n whichSVG;\n var bgImage = this.bgImage();\n var closestSVGs = this.getSvgsClosestToElement(drag);\n var isMoveFromDragsToDropzones = (svgClass === \"dragshome\");\n var isMoveFromDropzonesToDrags = (svgClass === 'dropzones') &&\n (question.lines[dropzoneNo].centre1.y > (bgImage.height - 20));\n\n if (isMoveFromDragsToDropzones) {\n question.lines[dropzoneNo].addToDropZone('keyboard', dropzoneElement,\n closestSVGs.svgDropZone, closestSVGs.svgDragsHome, null, null, 'DragsSVG');\n } else if (isMoveFromDropzonesToDrags) {\n question.lines[dropzoneNo].addToDropZone('keyboard', dropzoneElement,\n closestSVGs.svgDropZone, closestSVGs.svgDragsHome, null, null, 'DropZonesSVG');\n }\n\n // Get the dimensions of the selected element's svg.\n closeTo = drag.closest('svg');\n var dimensions = question.getSvgDimensionsByClass(closeTo, closeTo.getAttribute('class'));\n maxX = dimensions.maxX;\n maxY = dimensions.maxY;\n whichSVG = dimensions.whichSVG;\n\n if (activeElement === 'line') {\n // Move the entire line when the focus is on it.\n question.lines[dropzoneNo].moveDrags(parseInt(x), parseInt(y), parseInt(maxX), parseInt(maxY), whichSVG);\n } else {\n // Move the line endpoints.\n question.lines[dropzoneNo].move(activeElement, parseInt(x), parseInt(y), parseInt(maxX), parseInt(maxY));\n }\n question.updateSvgEl(dropzoneNo);\n this.saveCoordsForChoice(dropzoneNo);\n drag.focus();\n };\n\n /**\n * Returns the dimensions of the SVG image to which the drag element belongs.\n *\n * @param {SVG} dragSVG The SVG to which the drag element belongs.\n * @param {String} className Class asscociated with the SVG\n * @return {{whichSVG: (string), maxY: number, maxX: number}}\n */\n DrawlinesQuestion.prototype.getSvgDimensionsByClass = function(dragSVG, className) {\n return {\n maxX: dragSVG.width.baseVal.value,\n maxY: dragSVG.height.baseVal.value,\n whichSVG: className === 'dragshome' ? 'DragsSVG' : 'DropZonesSVG'\n };\n };\n\n /**\n * Returns the SVG's to which the drag element belongs.\n *\n * @param {SVGElement} dragElement The element which is being moved.\n * @return {{svgDragsHome, svgDropZone}}\n */\n DrawlinesQuestion.prototype.getSvgsClosestToElement = function(dragElement) {\n var svgElement = dragElement.closest('svg');\n var svgElementClass = svgElement.getAttribute('class');\n var svgDragsHome, svgDropZone, parent;\n\n if (svgElementClass === \"dragshome\") {\n svgDragsHome = svgElement;\n parent = svgElement.closest('.ddarea');\n svgDropZone = parent.querySelector('.dropzones');\n } else {\n svgDropZone = svgElement;\n parent = svgElement.closest('.ddarea');\n svgDragsHome = parent.querySelector('.dragshome');\n }\n return {\n svgDropZone: svgDropZone,\n svgDragsHome: svgDragsHome\n };\n };\n\n /**\n * Waits until all images are loaded before setting up question.\n *\n * This function is called from the onLoad of each image, and also polls with\n * a time-out, because image on-loads are allegedly unreliable.\n *\n * @return {Promise}\n */\n DrawlinesQuestion.prototype.waitForAllImagesToBeLoaded = function() {\n\n // This method may get called multiple times (via image on-loads or timeouts.\n // If we are already done, don't do it again.\n if (this.allImagesLoaded) {\n return Promise.resolve(null); // Return a resolved promise;\n }\n const images = document.querySelectorAll('img');\n const promises = Array.from(images).map((img) => {\n return new Promise((resolve, reject) => {\n if (img.complete) {\n resolve(); // If image is already loaded\n } else {\n img.onload = () => resolve();\n img.onerror = () => reject(new Error(`Failed to load image: ${img.src}`));\n }\n });\n });\n\n // Wait for all images to load\n return Promise.all(promises)\n .then(() => {\n // Only mark allImagesLoaded as true once all images are successfully loaded.\n this.allImagesLoaded = true;\n return null; // Return null explicitly to avoid ESLint errors.\n })\n .catch((error) => {\n this.allImagesLoaded = true; // Even on error, set this to avoid re-checking\n throw error; // Rethrow the error so it can be caught in a calling function if necessary.\n });\n };\n\n /**\n * Get any of the images in the drag-drop area that are not yet fully loaded.\n *\n * @returns {boolean} Returns true if images are loaded without errors.\n */\n DrawlinesQuestion.prototype.getNotYetLoadedImages = function() {\n // Get all 'img' elements with the class 'dropbackground' within '.drawlines' inside the root element\n const images = this.getRoot().querySelectorAll('.drawlines img.dropbackground');\n\n // Filter out the images that are already loaded\n Array.from(images).filter((imgNode) => {\n return !this.imageIsLoaded(imgNode);\n });\n };\n\n /**\n * Check if an image has loaded without errors.\n *\n * @param {HTMLImageElement} imgElement an image.\n * @returns {boolean} true if this image has loaded without errors.\n */\n DrawlinesQuestion.prototype.imageIsLoaded = function(imgElement) {\n return imgElement.complete && imgElement.naturalHeight !== 0;\n };\n\n /**\n * Singleton that tracks all the DrawlinesQuestions on this page, and deals\n * with event dispatching.\n *\n * @type {Object}\n */\n var questionManager = {\n\n /**\n * {boolean} ensures that the event handlers are only initialised once per page.\n */\n eventHandlersInitialised: false,\n\n /**\n * {Object} ensures that the marker event handlers are only initialised once per question,\n * indexed by containerId (id on the .que div).\n */\n lineEventHandlersInitialised: {},\n\n /**\n * {boolean} is printing or not.\n */\n isPrinting: false,\n\n /**\n * {boolean} is keyboard navigation.\n */\n isKeyboardNavigation: false,\n\n /**\n * {Object} all the questions on this page, indexed by containerId (id on the .que div).\n */\n questions: {}, // An object containing all the information about each question on the page.\n\n /**\n * @var {int} the number of lines on the form.\n */\n noOfLines: null,\n\n /**\n * @var {DrawlinesQuestion[]} the lines in the preview, indexed by line number.\n */\n dropZones: [],\n\n /**\n * @var {line[]} the question lines in the preview, indexed by line number.\n */\n questionLines: [],\n\n /**\n * Initialise one question.\n *\n * @param {String} containerId the id of the div.que that contains this question.\n * @param {boolean} readOnly whether the question is read-only.\n * @param {Object[]} visibleDropZones data on any drop zones to draw as part of the feedback.\n * @param {Object[]} questionLines\n */\n init: function(containerId, readOnly, visibleDropZones, questionLines) {\n questionManager.questions[containerId] =\n new DrawlinesQuestion(containerId, readOnly, visibleDropZones, questionLines);\n\n questionManager.questions[containerId].updateCoordinates();\n\n if (!questionManager.lineEventHandlersInitialised.hasOwnProperty(containerId)) {\n questionManager.lineEventHandlersInitialised[containerId] = true;\n\n var questionContainer = document.getElementById(containerId);\n if (questionContainer.classList.contains('drawlines') &&\n !questionContainer.classList.contains('qtype_drawlines-readonly')) {\n\n // Add event listeners to the 'previewArea'.\n // For dropzone SVG.\n var dropArea = questionContainer.querySelector('.droparea');\n // Add event listener for mousedown and touchstart events.\n dropArea.addEventListener('mousedown', questionManager.handleDropZoneEventMove);\n dropArea.addEventListener('touchstart', questionManager.handleDropZoneEventMove);\n // Add event listener for keydown and keypress events.\n dropArea.addEventListener('keydown', questionManager.handleKeyPress);\n dropArea.addEventListener('keypress', questionManager.handleKeyPress);\n\n // For draghomes SVG.\n var drags = questionContainer.querySelector('.draghomes');\n // Add event listener for mousedown and touchstart events.\n drags.addEventListener('mousedown', questionManager.handleDragHomeEventMove);\n drags.addEventListener('touchstart', questionManager.handleDragHomeEventMove);\n // Add event listener for keydown and keypress events.\n drags.addEventListener('keydown', questionManager.handleKeyPress);\n drags.addEventListener('keypress', questionManager.handleKeyPress);\n }\n }\n },\n\n // TODO: commented as currently we are not using this function. To be removed later if not needed.\n // /**\n // * Set up the event handlers that make this question type work. (Done once per page.)\n // */\n // setupEventHandlers: function() {\n // $(window).on('resize', function() {\n // questionManager.handleWindowResize(false);\n // });\n // window.addEventListener('beforeprint', function() {\n // questionManager.isPrinting = true;\n // questionManager.handleWindowResize(questionManager.isPrinting);\n // });\n // window.addEventListener('afterprint', function() {\n // questionManager.isPrinting = false;\n // questionManager.handleWindowResize(questionManager.isPrinting);\n // });\n // setTimeout(function() {\n // questionManager.fixLayoutIfThingsMoved();\n // }, 100);\n // },\n\n /**\n * Handle mouse and touch events for dropzone svg.\n *\n * @param {Event} event\n */\n handleDropZoneEventMove: function(event) {\n var dropzoneElement, dropzoneNo;\n var question = questionManager.getQuestionForEvent(event);\n if (event.target.closest('.dropzone .startcircle.shape')) {\n // Dragging the move handle circle attached to the start of the line.\n dropzoneElement = event.target.closest('g');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n question.handleCircleMove(event, 'startcircle', dropzoneNo);\n } else if (event.target.closest('.dropzone .endcircle.shape')) {\n // Dragging the move handle circle attached to the end of the line.\n dropzoneElement = event.target.closest('g');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n question.handleCircleMove(event, 'endcircle', dropzoneNo);\n } else if (event.target.closest('polyline.shape')) {\n // Dragging the entire line.\n dropzoneElement = event.target.closest('g');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n question.handleLineMove(event, dropzoneNo);\n }\n },\n\n /**\n * Handle mouse and touch events for dragshome svg.\n *\n * @param {Event} event\n */\n handleDragHomeEventMove: function(event) {\n var dropzoneElement, dropzoneNo;\n var question = questionManager.getQuestionForEvent(event);\n if (event.target.closest('g')) {\n dropzoneElement = event.target.closest('g');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n question.handleLineMove(event, dropzoneNo);\n question.saveCoordsForChoice(dropzoneNo);\n }\n },\n\n /**\n * Handle key down / press events on markers.\n *\n * @param {Event} e\n */\n handleKeyPress: function(e) {\n var question = questionManager.getQuestionForEvent(e);\n var dropzoneElement, dropzoneNo, drag, activeElement;\n if (e.target.closest('.dropzone circle.startcircle')) {\n dropzoneElement = e.target.closest('.dropzone');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n drag = e.target.closest('.dropzone circle.startcircle');\n activeElement = 'startcircle';\n } else if (e.target.closest('.dropzone circle.endcircle')) {\n drag = e.target.closest('.dropzone circle.endcircle');\n dropzoneElement = e.target.closest('.dropzone');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n activeElement = 'endcircle';\n } else if (e.target.closest('g.dropzone')) {\n drag = e.target.closest('g.dropzone');\n dropzoneElement = e.target.closest('.dropzone');\n dropzoneNo = dropzoneElement.dataset.dropzoneNo;\n activeElement = 'line';\n }\n if (question && dropzoneElement) {\n question.handleKeyPress(e, drag, dropzoneNo, activeElement);\n }\n },\n\n /**\n * Handle when the window is resized.\n *\n * @param {boolean} isPrinting\n */\n handleWindowResize: function(isPrinting) {\n for (var containerId in questionManager.questions) {\n if (questionManager.questions.hasOwnProperty(containerId)) {\n questionManager.questions[containerId].isPrinting = isPrinting;\n questionManager.questions[containerId].handleResize();\n }\n }\n },\n\n /**\n * Given an event, work out which question it effects.\n *\n * @param {Event} e the event.\n * @returns {DrawlinesQuestion|undefined} The question, or undefined.\n */\n getQuestionForEvent: function(e) {\n var containerId = $(e.currentTarget).closest('.que.drawlines').attr('id');\n return questionManager.questions[containerId];\n },\n };\n\n /**\n * @alias module:qtype_drawlines/question\n */\n return {\n /**\n * Initialise one drag-drop markers question.\n *\n * @param {String} containerId id of the outer div for this question.\n * @param {boolean} readOnly whether the question is being displayed read-only.\n * @param {String[]} visibleDropZones the geometry of any drop-zones to show.\n * @param {Object[]} questionLines\n */\n init: questionManager.init,\n };\n});\n"],"names":["define","$","dragDrop","Line","DrawlinesQuestion","containerId","readOnly","visibleDropZones","questionLines","thisQ","this","lineSVGs","lines","svgEl","getRoot","classList","add","allImagesLoaded","waitForAllImagesToBeLoaded","then","drawDropzone","catch","error","prototype","updateCoordinates","line","length","coordinates","getSVGLineCoordinates","parse","updateSvgEl","parseCoordinates","lineType","bits","split","slice","Error","bgImage","svg","querySelector","rootElement","style","position","top","height","innerHTML","width","drawSVGLines","startcoordinates","endcoordinates","draginitialcoords","draghomeSvg","dropzoneSvg","undefined","coords","type","make","labelstart","labelend","addToSvg","document","getElementById","childNodes","getAttribute","bgRatio","bgImg","bgImgNaturalWidth","get","naturalWidth","lineNumber","makeSvg","setAttribute","dropzoneNo","updateSvg","handleCircleMove","e","whichHandle","info","prepare","start","movingDropZone","lastX","x","lastY","y","dragProxy","makeDragProxy","maxX","baseVal","value","maxY","pageX","pageY","move","parseInt","saveCoordsForChoice","body","removeChild","handleLineMove","isMoveFromDragsToDropzones","isMoveFromDropzonesToDrags","svgClass","movingDrag","whichSVG","selectedElement","dropX","clientX","dropY","clientY","closestSVGs","getSvgsClosestToElement","closeTo","closest","centre1","addToDropZone","svgDropZone","svgDragsHome","dimensions","getSvgDimensionsByClass","moveDrags","createElement","left","appendChild","choiceNo","imageCoords","items","gEleClassAttributes","includes","handleKeyPress","drag","activeElement","dropzoneElement","question","questionManager","getQuestionForEvent","code","preventDefault","focus","dragSVG","className","dragElement","svgElement","Promise","resolve","images","querySelectorAll","promises","Array","from","map","img","reject","complete","onload","onerror","src","all","getNotYetLoadedImages","filter","imgNode","imageIsLoaded","imgElement","naturalHeight","eventHandlersInitialised","lineEventHandlersInitialised","isPrinting","isKeyboardNavigation","questions","noOfLines","dropZones","init","hasOwnProperty","questionContainer","contains","dropArea","addEventListener","handleDropZoneEventMove","drags","handleDragHomeEventMove","event","target","dataset","handleWindowResize","handleResize","currentTarget","attr"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/question.js b/amd/src/question.js index 44aa89a..698f72a 100644 --- a/amd/src/question.js +++ b/amd/src/question.js @@ -605,7 +605,7 @@ define([ bgImage = this.bgImage(), isMoveFromDragsToDropzones, isMoveFromDropzonesToDrags, - svgClass = ''; + svgClass; var selectedElement = this.lineSVGs[dropzoneNo]; const dropX = e.clientX; @@ -716,6 +716,7 @@ define([ /** * Handle key down / press events on svg lines. + * * @param {KeyboardEvent} e * @param {SVGElement} drag SVG element being dragged. * @param {int} dropzoneNo @@ -818,6 +819,7 @@ define([ var svgElement = dragElement.closest('svg'); var svgElementClass = svgElement.getAttribute('class'); var svgDragsHome, svgDropZone, parent; + if (svgElementClass === "dragshome") { svgDragsHome = svgElement; parent = svgElement.closest('.ddarea');