weekly release 4.1dev
[moodle.git] / question / type / ddmarker / amd / build / question.min.js.map
CommitLineData
0c33dbb2 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 <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/copyleft/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], function(\n $,\n dragDrop,\n Shapes,\n keys,\n FormChangeChecker\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.isPrinting = false;\n this.questionAnswer = {};\n if (readOnly) {\n this.getRoot().addClass('qtype_ddmarker-readonly');\n }\n thisQ.cloneDrags();\n thisQ.repositionDrags();\n thisQ.drawDropzones();\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 dropZoneNo = 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 } 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 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 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 coords = thisQ.getCoords(input);\n if (coords.length) {\n var drag = thisQ.getRoot().find('.draghomes' + ' span.marker' + '.choice' + choiceNo).not('.dragplaceholder');\n drag.remove();\n for (var i = 0; i < coords.length; i++) {\n var dragInDrop = drag.clone();\n dragInDrop.data('pagex', coords[i].x).data('pagey', coords[i].y);\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[]} coordinates of however many copies of the drag item should be shown.\n */\n DragDropMarkersQuestion.prototype.getCoords = function(inputNode) {\n var coords = [],\n val = $(inputNode).val();\n if (val !== '') {\n var coordsStrings = val.split(';');\n for (var i = 0; i < coordsStrings.length; i++) {\n coords[i] = this.convertToWindowXY(Shapes.Point.parse(coordsStrings[i]));\n }\n }\n return coords;\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 = dragDrop.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\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 var coords = [],\n 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')) {\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 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 coords[coords.length] = bgImgXY;\n }\n }\n });\n }\n\n this.getRoot().find('input.choice' + choiceNo).val(coords.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.getQuestionAnsweredValues();\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 default:\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 } else {\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 originCoords = 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('beingdragged')\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('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 * 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 DragDropMarkersQuestion.prototype.isInfiniteDrag = function(drag) {\n return drag.hasClass('infinite');\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('keydown 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 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 * 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","DragDropMarkersQuestion","containerId","readOnly","visibleDropZones","shapes","shapeSVGs","isPrinting","questionAnswer","getRoot","addClass","this","cloneDrags","repositionDrags","drawDropzones","prototype","length","bgImage","find","html","outerWidth","outerHeight","svg","nextColourIndex","dropZoneNo","colourClass","addDropzone","existingmarkertext","dropZone","shape","make","bgRatio","parse","coords","markertext","remove","classnames","append","markerspan","handles","getHandlePositions","positionLeft","moveHandle","x","positionTop","y","css","data","position","left","top","handleElementScale","shapeSVG","makeSvg","setAttribute","root","thisQ","not","each","key","item","input","choiceNo","getChoiceNoFromElement","getCoords","drag","i","dragInDrop","clone","sendDragToDrop","getDragClone","cloneDragIfNeeded","getQuestionAnsweredValues","result","inputNode","id","value","isQuestionInteracted","oldAnswer","newAnswer","isInteracted","JSON","stringify","Object","forEach","val","coordsStrings","split","convertToWindowXY","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","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","eventHandlersInitialised","markerEventHandlersInitialised","isKeyboardNavigation","questions","init","setupEventHandlers","hasOwnProperty","questionContainer","classList","contains","window","on","handleWindowResize","addEventListener","setTimeout","fixLayoutIfThingsMoved","focusin","handleKeyboardFocus","focusout","question","getQuestionForEvent","isNavigating","currentTarget","responseForm","markFormAsDirty"],"mappings":"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"}