Rev 1 | AutorÃa | Comparar con el anterior | Ultima modificación | Ver Log |
{"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 <http://www.gnu.org/licenses/>.\n\n/**\n * Question class for drag and drop marker question type, used to support the question and preview pages.\n *\n * @module qtype_ddmarker/question\n * @copyright 2018 The Open University\n * @license http://www.gnu.org/cop
yleft/gpl.html GNU GPL v3 or later\n */\n\ndefine([\n 'jquery',\n 'core/dragdrop',\n 'qtype_ddmarker/shapes',\n 'core/key_codes',\n 'core_form/changechecker',\n 'core_filters/events',\n], function(\n $,\n dragDrop,\n Shapes,\n keys,\n FormChangeChecker,\n filterEvent\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 shape, coords and markertext.\n * @constructor\n */\n function DragDropMarkersQuestion(containerId, readOnly, visibleDropZones) {\n var thisQ = this;\n this.containerId = containerId;\n this.visibleDropZones = visibleDropZones;\n this.shapes = [];\n this.shapeSVGs = [];\n this.isPrinti
ng = false;\n this.questionAnswer = {};\n if (readOnly) {\n this.getRoot().addClass('qtype_ddmarker-readonly');\n }\n thisQ.allImagesLoaded = false;\n thisQ.getNotYetLoadedImages().one('load', function() {\n thisQ.waitForAllImagesToBeLoaded();\n });\n thisQ.waitForAllImagesToBeLoaded();\n }\n\n /**\n * Draws the svg shapes of any drop zones that should be visible for feedback purposes.\n */\n DragDropMarkersQuestion.prototype.drawDropzones = function() {\n if (this.visibleDropZones.length > 0) {\n var bgImage = this.bgImage();\n\n this.getRoot().find('div.dropzones').html('<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"dropzones\" ' +\n 'width=\"' + bgImage.outerWidth() + '\" ' +\n 'height=\"' + bgImage.outerHeight() + '\"></svg>');\n var svg = this.getRoot().find('svg.dropzones');\n\n var nextColourIndex = 0;\n for (var dropZon
eNo = 0; dropZoneNo < this.visibleDropZones.length; dropZoneNo++) {\n var colourClass = 'color' + nextColourIndex;\n nextColourIndex = (nextColourIndex + 1) % 8;\n this.addDropzone(svg, dropZoneNo, colourClass);\n }\n }\n };\n\n /**\n * Adds a dropzone shape with colour, coords and link provided to the array of shapes.\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 DragDropMarkersQuestion.prototype.addDropzone = function(svg, dropZoneNo, colourClass) {\n var dropZone = this.visibleDropZones[dropZoneNo],\n shape = Shapes.make(dropZone.shape, ''),\n existingmarkertext,\n bgRatio = this.bgRatio();\n if (!shape.parse(dropZone.coords, bgRatio)) {\n return;\n }\n\n existingmarkertext = this.getRoot().find('div.markertexts
span.markertext' + dropZoneNo);\n if (existingmarkertext.length) {\n if (dropZone.markertext !== '') {\n existingmarkertext.html(dropZone.markertext);\n filterEvent.notifyFilterContentUpdated(existingmarkertext);\n } else {\n existingmarkertext.remove();\n }\n } else if (dropZone.markertext !== '') {\n var classnames = 'markertext markertext' + dropZoneNo;\n this.getRoot().find('div.markertexts').append('<span class=\"' + classnames + '\">' +\n dropZone.markertext + '</span>');\n var markerspan = this.getRoot().find('div.ddarea div.markertexts span.markertext' + dropZoneNo);\n if (markerspan.length) {\n var handles = shape.getHandlePositions();\n var positionLeft = handles.moveHandle.x - (markerspan.outerWidth() / 2) - 4;\n var positionTop = handles.moveHandle.y - (markerspan.outerHeight() / 2);\n mar
kerspan\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 filterEvent.notifyFilterContentUpdated(markerspan);\n }\n\n var shapeSVG = shape.makeSvg(svg[0]);\n shapeSVG.setAttribute('class', 'dropzone ' + colourClass);\n\n this.shapes[this.shapes.length] = shape;\n this.shapeSVGs[this.shapeSVGs.length] = shapeSVG;\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 DragDropMarkersQuestion.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 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 dragInD
rop.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 * Get the question answered values.\n *\n * @return {Object} Contain key-value with key is the input id and value is the input value.\n */\n DragDropMarkersQuestion.prototype.getQuestionAnsweredValues = function() {\n let result = {};\n this.getRoot().find('input.choices').each((i, inputNode) => {\n result[inputNode.id] = inputNode.value;\n });\n\n return
result;\n };\n\n /**\n * Check if the question is being interacted or not.\n *\n * @return {boolean} Return true if the user has changed the question-answer.\n */\n DragDropMarkersQuestion.prototype.isQuestionInteracted = function() {\n const oldAnswer = this.questionAnswer;\n const newAnswer = this.getQuestionAnsweredValues();\n let isInteracted = false;\n\n // First, check both answers have the same structure or not.\n if (JSON.stringify(newAnswer) !== JSON.stringify(oldAnswer)) {\n isInteracted = true;\n return isInteracted;\n }\n // Check the values.\n Object.keys(newAnswer).forEach(key => {\n if (newAnswer[key] !== oldAnswer[key]) {\n isInteracted = true;\n }\n });\n\n return isInteracted;\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 DragDropMarkersQuestion.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] = Shapes.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 DragDropMarkersQuestion.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 DragDropMarkersQuestion.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 * Is the point within the background image?\n *\n * @param {Point} point relative to the BG image.\n * @return {boolean} true it they are.\n
*/\n DragDropMarkersQuestion.prototype.coordsInBgImg = function(point) {\n var bgImage = this.bgImage();\n var bgPosition = bgImage.offset();\n\n return point.x >= bgPosition.left && point.x < bgPosition.left + bgImage.width()\n && point.y >= bgPosition.top && point.y < bgPosition.top + bgImage.height();\n };\n\n /**\n * Get the outer div for this question.\n * @returns {jQuery} containing that div.\n */\n DragDropMarkersQuestion.prototype.getRoot = function() {\n return $(document.getElementById(this.containerId));\n };\n\n /**\n * Get the img that is the background image.\n * @returns {jQuery} containing that img.\n */\n DragDropMarkersQuestion.prototype.bgImage = function() {\n return this.getRoot().find('img.dropbackground');\n };\n\n DragDropMarkersQuestion.prototype.handleDragStart = function(e) {\n var thisQ = this,\n dragged = $(e.target).closest('.marker');\n\n var info = dra
gDrop.prepare(e);\n if (!info.start) {\n return;\n }\n\n dragged.addClass('beingdragged').css('transform', '');\n\n var placed = !dragged.hasClass('unneeded');\n if (!placed) {\n var hiddenDrag = thisQ.getDragClone(dragged);\n if (hiddenDrag.length) {\n hiddenDrag.addClass('active');\n dragged.offset(hiddenDrag.offset());\n }\n }\n\n dragDrop.start(e, dragged, function() {\n void (1);\n }, function(x, y, dragged) {\n thisQ.dragEnd(dragged);\n });\n };\n\n /**\n * Functionality at the end of a drag drop.\n * @param {jQuery} dragged the marker that was dragged.\n */\n DragDropMarkersQuestion.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 Shapes.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 Shapes.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 * Save the coordinates for a dropped item in the form field.\n * @param {Number} choiceNo which copy of the choice this was.\n */\n DragDropMarkersQuestion.prototype.saveCoordsForChoice = function(choiceNo) {\n let imageCoords = [];\n var items = this.getRoot().find('div.droparea span.marker.choice' + choiceNo),\n thiQ = this,\n bgRatio = this.bgRatio();\n\n if (items.length) {\n items.each(function() {\n var drag = $(this);\n if (!drag.hasClass('beingdragged') && !drag.data('imageCoords')) {\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).da
ta('pagey', drag.offset().top);\n }\n var dragXY = new Shapes.Point(drag.data('pagex'), drag.data('pagey'));\n if (thiQ.coordsInBgImg(dragXY)) {\n var bgImgXY = thiQ.convertToBgImgXY(dragXY);\n bgImgXY = new Shapes.Point(bgImgXY.x / bgRatio, bgImgXY.y / bgRatio);\n imageCoords[imageCoords.length] = bgImgXY;\n }\n } else if (drag.data('imageCoords')) {\n imageCoords[imageCoords.length] = drag.data('imageCoords');\n }\n });\n }\n\n this.getRoot().find('input.choice' + choiceNo).val(imageCoords.join(';'));\n if (this.isQuestionInteracted()) {\n // The user has interacted with the draggable items. We need to mark the form as dirty.\n questionManager.handleFormDirty();\n // Save the new answered value.\n this.questionAnswer = this.getQuestionAnswer
edValues();\n }\n };\n\n /**\n * Handle key down / press events on markers.\n * @param {KeyboardEvent} e\n */\n DragDropMarkersQuestion.prototype.handleKeyPress = function(e) {\n var drag = $(e.target).closest('.marker'),\n point = new Shapes.Point(drag.offset().left, drag.offset().top),\n choiceNo = this.getChoiceNoFromElement(drag);\n\n switch (e.keyCode) {\n case keys.arrowLeft:\n case 65: // A.\n point.x -= 1;\n break;\n case keys.arrowRight:\n case 68: // D.\n point.x += 1;\n break;\n case keys.arrowDown:\n case 83: // S.\n point.y += 1;\n break;\n case keys.arrowUp:\n case 87: // W.\n point.y -= 1;\n break;\n case keys.space:\n case keys.escape:\n point = null;\n break;\n d
efault:\n return; // Ingore other keys.\n }\n e.preventDefault();\n\n if (point !== null) {\n point = this.constrainToBgImg(point);\n drag.offset({'left': point.x, 'top': point.y});\n drag.data('pagex', drag.offset().left).data('pagey', drag.offset().top);\n var dragXY = this.convertToBgImgXY(new Shapes.Point(drag.data('pagex'), drag.data('pagey')));\n drag.data('originX', dragXY.x / this.bgRatio()).data('originY', dragXY.y / this.bgRatio());\n if (this.coordsInBgImg(new Shapes.Point(drag.offset().left, drag.offset().top))) {\n if (drag.hasClass('unneeded')) {\n this.sendDragToDrop(drag, true);\n var hiddenDrag = this.getDragClone(drag);\n if (hiddenDrag.length) {\n hiddenDrag.addClass('active');\n }\n this.cloneDragIfNeeded(drag);\n }\n }\n } e
lse {\n drag.css('left', '').css('top', '');\n drag.data('pagex', drag.offset().left).data('pagey', drag.offset().top);\n this.sendDragHome(drag);\n this.removeDragIfNeeded(drag);\n }\n drag.focus();\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 DragDropMarkersQuestion.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 * Returns the choice number for a node.\n *\n * @param {Element|jQuery}
node\n * @returns {Number}\n */\n DragDropMarkersQuestion.prototype.getChoiceNoFromElement = function(node) {\n return Number(this.getClassnameNumericSuffix(node, 'choice'));\n };\n\n /**\n * Returns the numeric part of a class with the given prefix.\n *\n * @param {Element|jQuery} node\n * @param {String} prefix\n * @returns {Number|null}\n */\n DragDropMarkersQuestion.prototype.getClassnameNumericSuffix = function(node, prefix) {\n var classes = $(node).attr('class');\n if (classes !== undefined && classes !== '') {\n var classesarr = classes.split(' ');\n for (var index = 0; index < classesarr.length; index++) {\n var patt1 = new RegExp('^' + prefix + '([0-9])+$');\n if (patt1.test(classesarr[index])) {\n var patt2 = new RegExp('([0-9])+$');\n var match = patt2.exec(classesarr[index]);\n return Number(match[0]);\n }\n
}\n }\n return null;\n };\n\n /**\n * Handle when the window is resized.\n */\n DragDropMarkersQuestion.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 origi
nCoords = dropZone.coords;\n var shape = thisQ.shapes[dropZoneNo];\n var shapeSVG = thisQ.shapeSVGs[dropZoneNo];\n shape.parse(originCoords, bgRatio);\n shape.updateSvg(shapeSVG);\n\n var handles = shape.getHandlePositions();\n var markerSpan = this.getRoot().find('div.ddarea div.markertexts span.markertext' + dropZoneNo);\n markerSpan\n .css('left', handles.moveHandle.x - (markerSpan.outerWidth() / 2) - 4)\n .css('top', handles.moveHandle.y - (markerSpan.outerHeight() / 2));\n thisQ.handleElementScale(markerSpan, 'center');\n }\n };\n\n /**\n * Clone the drag.\n */\n DragDropMarkersQuestion.prototype.cloneDrags = function() {\n var thisQ = this;\n this.getRoot().find('div.draghomes span.marker').each(function(index, draghome) {\n var drag = $(draghome);\n var placeHolder = drag.clone();\n placeHolder.removeClass();\n
placeHolder.addClass('marker');\n placeHolder.addClass('choice' + thisQ.getChoiceNoFromElement(drag));\n placeHolder.addClass(thisQ.getDragNoClass(drag, false));\n placeHolder.addClass('dragplaceholder');\n drag.before(placeHolder);\n });\n };\n\n /**\n * Get the drag number of a drag.\n *\n * @param {jQuery} drag the drag.\n * @returns {Number} the drag number.\n */\n DragDropMarkersQuestion.prototype.getDragNo = function(drag) {\n return this.getClassnameNumericSuffix(drag, 'dragno');\n };\n\n /**\n * Get the drag number prefix of a drag.\n *\n * @param {jQuery} drag the drag.\n * @param {Boolean} includeSelector include the CSS selector prefix or not.\n * @return {String} Class name\n */\n DragDropMarkersQuestion.prototype.getDragNoClass = function(drag, includeSelector) {\n var className = 'dragno' + this.getDragNo(drag);\n if (this.isInfiniteDrag(drag)) {\n
className = 'infinite';\n }\n\n if (includeSelector) {\n return '.' + className;\n }\n\n return className;\n };\n\n /**\n * Get drag clone for a given drag.\n *\n * @param {jQuery} drag the drag.\n * @returns {jQuery} the drag's clone.\n */\n DragDropMarkersQuestion.prototype.getDragClone = function(drag) {\n return this.getRoot().find('.draghomes' + ' span.marker' +\n '.choice' + this.getChoiceNoFromElement(drag) + this.getDragNoClass(drag, true) + '.dragplaceholder');\n };\n\n /**\n * Get the drop area element.\n * @returns {jQuery} droparea element.\n */\n DragDropMarkersQuestion.prototype.dropArea = function() {\n return this.getRoot().find('div.droparea');\n };\n\n /**\n * Animate a drag back to its home.\n *\n * @param {jQuery} drag the item being moved.\n */\n DragDropMarkersQuestion.prototype.sendDragHome = function(drag) {\n drag.removeClass('beingdrag
ged')\n .addClass('unneeded')\n .css('top', '')\n .css('left', '')\n .css('transform', '');\n var placeHolder = this.getDragClone(drag);\n placeHolder.after(drag);\n placeHolder.removeClass('active');\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 DragDropMarkersQuestion.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 Shapes.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('lef
t', 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 * Clone the drag at the draghome area if needed.\n *\n * @param {jQuery} drag the item to place.\n */\n DragDropMarkersQuestion.prototype.cloneDragIfNeeded = function(drag) {\n var inputNode = this.getInput(drag),\n noOfDrags = Number(this.getClassnameNumericSuffix(inputNode, 'noofdrags')),\n displayedDragsInDropArea = this.getRoot().find('div.droparea .marker.choice' +\n
this.getChoiceNoFromElement(drag) + this.getDragNoClass(drag, true)).length,\n displayedDragsInDragHomes = this.getRoot().find('div.draghomes .marker.choice' +\n this.getChoiceNoFromElement(drag) + this.getDragNoClass(drag, true)).not('.dragplaceholder').length;\n\n if ((this.isInfiniteDrag(drag) ||\n !this.isInfiniteDrag(drag) && displayedDragsInDropArea < noOfDrags) && displayedDragsInDragHomes === 0) {\n var dragClone = drag.clone();\n dragClone.addClass('unneeded')\n .css('top', '')\n .css('left', '')\n .css('transform', '');\n this.getDragClone(drag)\n .removeClass('active')\n .after(dragClone);\n questionManager.addEventHandlersToMarker(dragClone);\n }\n };\n\n /**\n * Remove the clone drag at the draghome area if needed.\n *\n * @param {jQuery} drag the item to place.\n */\n DragDropMarkersQuestion.
prototype.removeDragIfNeeded = function(drag) {\n var dragsInHome = this.getRoot().find('div.draghomes .marker.choice' +\n this.getChoiceNoFromElement(drag) + this.getDragNoClass(drag, true)).not('.dragplaceholder');\n var displayedDrags = dragsInHome.length;\n while (displayedDrags > 1) {\n dragsInHome.first().remove();\n displayedDrags--;\n }\n };\n\n /**\n * Get the input belong to drag.\n *\n * @param {jQuery} drag the item to place.\n * @returns {jQuery} input element.\n */\n DragDropMarkersQuestion.prototype.getInput = function(drag) {\n var choiceNo = this.getChoiceNoFromElement(drag);\n return this.getRoot().find('input.choices.choice' + choiceNo);\n };\n\n /**\n * Return the background ratio.\n *\n * @returns {number} Background ratio.\n */\n DragDropMarkersQuestion.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 * Scale the drag if needed.\n *\n * @param {jQuery} element the item to place.\n * @param {String} type scaling type\n */\n DragDropMarkersQuestion.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 * Check if the given drag is in infinite mode or not.\n *\n * @param {jQuery} drag The drag item need to check.\n */\n DragDropMarke
rsQuestion.prototype.isInfiniteDrag = function(drag) {\n return drag.hasClass('infinite');\n };\n\n /**\n * Waits until all images are loaded before calling setupQuestion().\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 DragDropMarkersQuestion.prototype.waitForAllImagesToBeLoaded = function() {\n var thisQ = this;\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;\n }\n\n // Clear any current timeout, if set.\n if (this.imageLoadingTimeoutId !== null) {\n clearTimeout(this.imageLoadingTimeoutId);\n }\n\n // If we have not yet loaded all images, set a timeout to\n // call ourselves again, since apparently images on-load\n // events are flakey.\n if
(this.getNotYetLoadedImages().length > 0) {\n this.imageLoadingTimeoutId = setTimeout(function() {\n this.waitForAllImagesToBeLoaded();\n }, 100);\n return;\n }\n\n // We now have all images. Carry on, but only after giving the layout a chance to settle down.\n this.allImagesLoaded = true;\n this.cloneDrags();\n this.repositionDrags();\n this.drawDropzones();\n // Wait for all dynamic content loaded by filter to be completed.\n document.addEventListener(filterEvent.eventTypes.filterContentRenderingComplete, (elements) => {\n elements.detail.nodes.forEach((element) => {\n thisQ.changeAllMakerToFilteredContent(element);\n });\n });\n };\n\n /**\n * Change all the maker related to the item that has been changed by filter to correct size and content.\n *\n * @param {object} filteredElement the element has been modified by filter.\n */\n Dra
gDropMarkersQuestion.prototype.changeAllMakerToFilteredContent = function(filteredElement) {\n let currentFilteredItem = $(filteredElement);\n const parentIsMarker = currentFilteredItem.parent().closest('span.marker');\n const isMarker = currentFilteredItem.hasClass('marker');\n const root = this.getRoot();\n // The filtered element or parent element should a drag or drop item.\n if (!parentIsMarker && !isMarker) {\n return;\n }\n if (parentIsMarker) {\n currentFilteredItem = currentFilteredItem.parent().closest('span.marker');\n }\n if (root.find(currentFilteredItem).length <= 0) {\n // If the maker doesn't belong to this question\n // In case we have multiple questions in the same page.\n return;\n }\n const dragNo = this.getDragNo(currentFilteredItem);\n const choiceNo = this.getChoiceNoFromElement(currentFilteredItem);\n const listOfContainerToBeModifed
= [\n 'div.draghomes .marker:not(.dragplaceholder).dragno' + dragNo + '.choice' + choiceNo,\n 'div.droparea .marker:not(.dragplaceholder).dragno' + dragNo + '.choice' + choiceNo,\n 'div.draghomes .marker:not(.dragplaceholder).infinite.choice' + choiceNo,\n 'div.droparea .marker:not(.dragplaceholder).infinite.choice' + choiceNo\n ];\n let listOfModifiedDragDrop = [];\n // We want to keep all the handler and event for filtered item, so using clone is the only choice.\n const filteredDragDropClone = currentFilteredItem.clone();\n listOfContainerToBeModifed.forEach(function(selector) {\n root.find(selector).each(function(i, node) {\n const originalClass = $(node).attr('class');\n const originalStyle = $(node).attr('style');\n // Replace the class and style of the maker we want to replace for the clone.\n filteredDragDropClone.attr('class', originalClass);\n
filteredDragDropClone.attr('style', originalStyle);\n // Add event for the clone.\n questionManager.addEventHandlersToMarker(filteredDragDropClone);\n // Insert into DOM.\n $(node).before(filteredDragDropClone);\n listOfModifiedDragDrop.push(node);\n });\n });\n listOfModifiedDragDrop.forEach(function(node) {\n $(node).remove();\n });\n };\n\n /**\n * Get any of the images in the drag-drop area that are not yet fully loaded.\n *\n * @returns {jQuery} those images.\n */\n DragDropMarkersQuestion.prototype.getNotYetLoadedImages = function() {\n return this.getRoot().find('.ddmarker img.dropbackground').not(function(i, 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 i
f this image has loaded without errors.\n */\n DragDropMarkersQuestion.prototype.imageIsLoaded = function(imgElement) {\n return imgElement.complete && imgElement.naturalHeight !== 0;\n };\n\n /**\n * Singleton that tracks all the DragDropToTextQuestions 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 markerEventHandlersInitialised: {},\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 * 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 */\n init: function(containerId, readOnly, visibleDropZones) {\n questionManager.questions[containerId] =\n new DragDropMarkersQuestion(containerId, readOnly, visibleDropZones);\n if (!questionManager.eventHandlersInitialised) {\n questionManager.setupEventHandlers();\n questionManager.eventHandlersInitialised = true;\n }\n if (!questionManager.markerEventHandlersInitialised.
hasOwnProperty(containerId)) {\n questionManager.markerEventHandlersInitialised[containerId] = true;\n // We do not use the body event here to prevent the other event on Mobile device, such as scroll event.\n var questionContainer = document.getElementById(containerId);\n if (questionContainer.classList.contains('ddmarker') &&\n !questionContainer.classList.contains('qtype_ddmarker-readonly')) {\n // TODO: Convert all the jQuery selectors and events to native Javascript.\n questionManager.addEventHandlersToMarker($(questionContainer).find('div.draghomes .marker'));\n questionManager.addEventHandlersToMarker($(questionContainer).find('div.droparea .marker'));\n }\n }\n },\n\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 * Binding the event again for newly created element.\n *\n * @param {jQuery} element Element to bind the event\n */\n addEventHandlersToMarker: function(element) {\n element\n .on('mousedown touchstart', questionManager.handleDragStart)\n .on('keydo
wn keypress', questionManager.handleKeyPress)\n .focusin(function(e) {\n questionManager.handleKeyboardFocus(e, true);\n })\n .focusout(function(e) {\n questionManager.handleKeyboardFocus(e, false);\n });\n },\n\n /**\n * Handle mouse down / touch start events on markers.\n * @param {Event} e the DOM event.\n */\n handleDragStart: function(e) {\n e.preventDefault();\n var question = questionManager.getQuestionForEvent(e);\n if (question) {\n question.handleDragStart(e);\n }\n },\n\n /**\n * Handle key down / press events on markers.\n * @param {Event} e\n */\n handleKeyPress: function(e) {\n var question = questionManager.getQuestionForEvent(e);\n if (question) {\n question.handleKeyPress(e);\n }\n },\n\n
/**\n * Handle when the window is resized.\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 * Handle focus lost events on markers.\n * @param {Event} e\n * @param {boolean} isNavigating\n */\n handleKeyboardFocus: function(e, isNavigating) {\n questionManager.isKeyboardNavigation = isNavigating;\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 chec
k 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 * Given an event, work out which question it effects.\n * @param {Event} e the event.\n * @returns {DragDropMarkersQuestion|undefined} The question, or undefined.\n */\n getQuestionForEvent: function(e) {\n var containerId = $(e.currentTarget).closest('.que.ddmarker').attr('id');\n return questionManager.questions[containerId];\n
},\n\n /**\n * Handle when the form is dirty.\n */\n handleFormDirty: function() {\n const responseForm = document.getElementById('responseform');\n FormChangeChecker.markFormAsDirty(responseForm);\n }\n };\n\n /**\n * @alias module:qtype_ddmarker/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 {String} bgImgUrl the URL of the background image.\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 */\n init: questionManager.init\n };\n});\n"],"names":["define","$","dragDrop","Shapes","keys","FormChangeChecker","filterEvent","DragDropMarkersQuestion","containerId","readOnly","visibleDropZones","thisQ","this","shapes","shapeSVGs","isPrint
ing","questionAnswer","getRoot","addClass","allImagesLoaded","getNotYetLoadedImages","one","waitForAllImagesToBeLoaded","prototype","drawDropzones","length","bgImage","find","html","outerWidth","outerHeight","svg","nextColourIndex","dropZoneNo","colourClass","addDropzone","existingmarkertext","dropZone","shape","make","bgRatio","parse","coords","markertext","notifyFilterContentUpdated","remove","classnames","append","markerspan","handles","getHandlePositions","positionLeft","moveHandle","x","positionTop","y","css","data","position","left","top","handleElementScale","shapeSVG","makeSvg","setAttribute","repositionDrags","root","not","each","key","item","input","choiceNo","getChoiceNoFromElement","imageCoords","getImageCoords","drag","i","dragInDrop","clone","screenCoords","convertToWindowXY","sendDragToDrop","getDragClone","cloneDragIfNeeded","getQuestionAnsweredValues","result","inputNode","id","value","isQuestionInteracted","oldAnswer","newAnswer","isInteracted","JSON","stringify","Object","forEach","val","c
oordsStrings","split","Point","point","offset","convertToBgImgXY","coordsInBgImg","bgPosition","width","height","document","getElementById","handleDragStart","e","dragged","target","closest","prepare","start","hasClass","hiddenDrag","dragEnd","dragXY","placed","bgImgXY","sendDragHome","removeDragIfNeeded","saveCoordsForChoice","items","thiQ","join","questionManager","handleFormDirty","handleKeyPress","keyCode","arrowLeft","arrowRight","arrowDown","arrowUp","space","escape","preventDefault","constrainToBgImg","focus","windowxy","bgImg","Math","max","min","node","Number","getClassnameNumericSuffix","prefix","classes","attr","undefined","classesarr","index","RegExp","test","match","exec","handleResize","parseFloat","originCoords","updateSvg","markerSpan","cloneDrags","draghome","placeHolder","removeClass","getDragNoClass","before","getDragNo","includeSelector","className","isInfiniteDrag","dropArea","after","isScaling","initialLoad","getInput","noOfDrags","displayedDragsInDropArea","displayedDragsInDragHomes","
dragClone","addEventHandlersToMarker","dragsInHome","displayedDrags","first","bgImgNaturalWidth","get","naturalWidth","element","type","imageLoadingTimeoutId","clearTimeout","setTimeout","addEventListener","eventTypes","filterContentRenderingComplete","elements","detail","nodes","changeAllMakerToFilteredContent","filteredElement","currentFilteredItem","parentIsMarker","parent","isMarker","dragNo","listOfContainerToBeModifed","listOfModifiedDragDrop","filteredDragDropClone","selector","originalClass","originalStyle","push","imgNode","imageIsLoaded","imgElement","complete","naturalHeight","eventHandlersInitialised","markerEventHandlersInitialised","isKeyboardNavigation","questions","init","setupEventHandlers","hasOwnProperty","questionContainer","classList","contains","window","on","handleWindowResize","fixLayoutIfThingsMoved","focusin","handleKeyboardFocus","focusout","question","getQuestionForEvent","isNavigating","currentTarget","responseForm","markFormAsDirty"],"mappings":";;;;;;;AAuBAA,iCAAO,CACH,SACA,gBA
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"}