weekly release 4.1dev
[moodle.git] / question / type / ddwtos / amd / build / ddwtos.min.js.map
CommitLineData
0c33dbb2 1{"version":3,"file":"ddwtos.min.js","sources":["../src/ddwtos.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 * JavaScript to make drag-drop into text questions work.\n *\n * Some vocabulary to help understand this code:\n *\n * The question text contains 'drops' - blanks into which the 'drags', the missing\n * words, can be put.\n *\n * The thing that can be moved into the drops are called 'drags'. There may be\n * multiple copies of the 'same' drag which does not really cause problems.\n * Each drag has a 'choice' number which is the value set on the drop's hidden\n * input when this drag is placed in a drop.\n *\n * These may be in separate 'groups', distinguished by colour.\n * Things can only interact with other things in the same group.\n * The groups are numbered from 1.\n *\n * The place where a given drag started from is called its 'home'.\n *\n * @module qtype_ddwtos/ddwtos\n * @copyright 2018 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.6\n */\ndefine([\n 'jquery',\n 'core/dragdrop',\n 'core/key_codes',\n 'core_form/changechecker'\n], function(\n $,\n dragDrop,\n keys,\n FormChangeChecker\n) {\n\n \"use strict\";\n\n /**\n * Object to handle one drag-drop into text 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 * @constructor\n */\n function DragDropToTextQuestion(containerId, readOnly) {\n this.containerId = containerId;\n this.questionAnswer = {};\n if (readOnly) {\n this.getRoot().addClass('qtype_ddwtos-readonly');\n }\n this.resizeAllDragsAndDrops();\n this.cloneDrags();\n this.positionDrags();\n }\n\n /**\n * In each group, resize all the items to be the same size.\n */\n DragDropToTextQuestion.prototype.resizeAllDragsAndDrops = function() {\n var thisQ = this;\n this.getRoot().find('.answercontainer > div').each(function(i, node) {\n thisQ.resizeAllDragsAndDropsInGroup(\n thisQ.getClassnameNumericSuffix($(node), 'draggrouphomes'));\n });\n };\n\n /**\n * In a given group, set all the drags and drops to be the same size.\n *\n * @param {int} group the group number.\n */\n DragDropToTextQuestion.prototype.resizeAllDragsAndDropsInGroup = function(group) {\n var thisQ = this,\n dragHomes = this.getRoot().find('.draggrouphomes' + group + ' span.draghome'),\n maxWidth = 0,\n maxHeight = 0;\n\n // Find the maximum size of any drag in this groups.\n dragHomes.each(function(i, drag) {\n maxWidth = Math.max(maxWidth, Math.ceil(drag.offsetWidth));\n maxHeight = Math.max(maxHeight, Math.ceil(0 + drag.offsetHeight));\n });\n\n // The size we will want to set is a bit bigger than this.\n maxWidth += 8;\n maxHeight += 2;\n\n // Set each drag home to that size.\n dragHomes.each(function(i, drag) {\n thisQ.setElementSize(drag, maxWidth, maxHeight);\n });\n\n // Set each drop to that size.\n this.getRoot().find('span.drop.group' + group).each(function(i, drop) {\n thisQ.setElementSize(drop, maxWidth, maxHeight);\n });\n };\n\n /**\n * Set a given DOM element to be a particular size.\n *\n * @param {HTMLElement} element\n * @param {int} width\n * @param {int} height\n */\n DragDropToTextQuestion.prototype.setElementSize = function(element, width, height) {\n $(element).width(width).height(height).css('lineHeight', height + 'px');\n };\n\n /**\n * Invisible 'drag homes' are output by the renderer. These have the same properties\n * as the drag items but are invisible. We clone these invisible elements to make the\n * actual drag items.\n */\n DragDropToTextQuestion.prototype.cloneDrags = function() {\n var thisQ = this;\n thisQ.getRoot().find('span.draghome').each(function(index, draghome) {\n var drag = $(draghome);\n var placeHolder = drag.clone();\n placeHolder.removeClass();\n placeHolder.addClass('draghome choice' +\n thisQ.getChoice(drag) + ' group' +\n thisQ.getGroup(drag) + ' dragplaceholder');\n drag.before(placeHolder);\n });\n };\n\n /**\n * Update the position of drags.\n */\n DragDropToTextQuestion.prototype.positionDrags = function() {\n var thisQ = this,\n root = this.getRoot();\n\n // First move all items back home.\n root.find('span.draghome').not('.dragplaceholder').each(function(i, dragNode) {\n var drag = $(dragNode),\n currentPlace = thisQ.getClassnameNumericSuffix(drag, 'inplace');\n drag.addClass('unplaced')\n .removeClass('placed');\n drag.removeAttr('tabindex');\n if (currentPlace !== null) {\n drag.removeClass('inplace' + currentPlace);\n }\n });\n\n // Then place the once that should be placed.\n root.find('input.placeinput').each(function(i, inputNode) {\n var input = $(inputNode),\n choice = input.val(),\n place = thisQ.getPlace(input);\n\n // Record the last known position of the drop.\n var drop = root.find('.drop.place' + place),\n dropPosition = drop.offset();\n drop.data('prev-top', dropPosition.top).data('prev-left', dropPosition.left);\n\n if (choice === '0') {\n // No item in this place.\n return;\n }\n\n // Get the unplaced drag.\n var unplacedDrag = thisQ.getUnplacedChoice(thisQ.getGroup(input), choice);\n // Get the clone of the drag.\n var hiddenDrag = thisQ.getDragClone(unplacedDrag);\n if (hiddenDrag.length) {\n if (unplacedDrag.hasClass('infinite')) {\n var noOfDrags = thisQ.noOfDropsInGroup(thisQ.getGroup(unplacedDrag));\n var cloneDrags = thisQ.getInfiniteDragClones(unplacedDrag, false);\n if (cloneDrags.length < noOfDrags) {\n var cloneDrag = unplacedDrag.clone();\n hiddenDrag.after(cloneDrag);\n questionManager.addEventHandlersToDrag(cloneDrag);\n } else {\n hiddenDrag.addClass('active');\n }\n } else {\n hiddenDrag.addClass('active');\n }\n }\n // Send the drag to drop.\n thisQ.sendDragToDrop(thisQ.getUnplacedChoice(thisQ.getGroup(input), choice), drop);\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 DragDropToTextQuestion.prototype.getQuestionAnsweredValues = function() {\n let result = {};\n this.getRoot().find('input.placeinput').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 DragDropToTextQuestion.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 * Handles the start of dragging an item.\n *\n * @param {Event} e the touch start or mouse down event.\n */\n DragDropToTextQuestion.prototype.handleDragStart = function(e) {\n var thisQ = this,\n drag = $(e.target).closest('.draghome');\n\n var info = dragDrop.prepare(e);\n if (!info.start || drag.hasClass('beingdragged')) {\n return;\n }\n\n drag.addClass('beingdragged');\n var currentPlace = this.getClassnameNumericSuffix(drag, 'inplace');\n if (currentPlace !== null) {\n this.setInputValue(currentPlace, 0);\n drag.removeClass('inplace' + currentPlace);\n var hiddenDrop = thisQ.getDrop(drag, currentPlace);\n if (hiddenDrop.length) {\n hiddenDrop.addClass('active');\n drag.offset(hiddenDrop.offset());\n }\n } else {\n var hiddenDrag = thisQ.getDragClone(drag);\n if (hiddenDrag.length) {\n if (drag.hasClass('infinite')) {\n var noOfDrags = this.noOfDropsInGroup(this.getGroup(drag));\n var cloneDrags = this.getInfiniteDragClones(drag, false);\n if (cloneDrags.length < noOfDrags) {\n var cloneDrag = drag.clone();\n cloneDrag.removeClass('beingdragged');\n hiddenDrag.after(cloneDrag);\n questionManager.addEventHandlersToDrag(cloneDrag);\n drag.offset(cloneDrag.offset());\n } else {\n hiddenDrag.addClass('active');\n drag.offset(hiddenDrag.offset());\n }\n } else {\n hiddenDrag.addClass('active');\n drag.offset(hiddenDrag.offset());\n }\n }\n }\n\n dragDrop.start(e, drag, function(x, y, drag) {\n thisQ.dragMove(x, y, drag);\n }, function(x, y, drag) {\n thisQ.dragEnd(x, y, drag);\n });\n };\n\n /**\n * Called whenever the currently dragged items moves.\n *\n * @param {Number} pageX the x position.\n * @param {Number} pageY the y position.\n * @param {jQuery} drag the item being moved.\n */\n DragDropToTextQuestion.prototype.dragMove = function(pageX, pageY, drag) {\n var thisQ = this;\n this.getRoot().find('span.drop.group' + this.getGroup(drag)).each(function(i, dropNode) {\n var drop = $(dropNode);\n if (thisQ.isPointInDrop(pageX, pageY, drop)) {\n drop.addClass('valid-drag-over-drop');\n } else {\n drop.removeClass('valid-drag-over-drop');\n }\n });\n this.getRoot().find('span.draghome.placed.group' + this.getGroup(drag)).not('.beingdragged').each(function(i, dropNode) {\n var drop = $(dropNode);\n if (thisQ.isPointInDrop(pageX, pageY, drop) && !thisQ.isDragSameAsDrop(drag, drop)) {\n drop.addClass('valid-drag-over-drop');\n } else {\n drop.removeClass('valid-drag-over-drop');\n }\n });\n };\n\n /**\n * Called when user drops a drag item.\n *\n * @param {Number} pageX the x position.\n * @param {Number} pageY the y position.\n * @param {jQuery} drag the item being moved.\n */\n DragDropToTextQuestion.prototype.dragEnd = function(pageX, pageY, drag) {\n var thisQ = this,\n root = this.getRoot(),\n placed = false;\n root.find('span.drop.group' + this.getGroup(drag)).each(function(i, dropNode) {\n var drop = $(dropNode);\n if (!thisQ.isPointInDrop(pageX, pageY, drop)) {\n // Not this drop.\n return true;\n }\n\n // Now put this drag into the drop.\n drop.removeClass('valid-drag-over-drop');\n thisQ.sendDragToDrop(drag, drop);\n placed = true;\n return false; // Stop the each() here.\n });\n\n root.find('span.draghome.placed.group' + this.getGroup(drag)).not('.beingdragged').each(function(i, placedNode) {\n var placedDrag = $(placedNode);\n if (!thisQ.isPointInDrop(pageX, pageY, placedDrag) || thisQ.isDragSameAsDrop(drag, placedDrag)) {\n // Not this placed drag.\n return true;\n }\n\n // Now put this drag into the drop.\n placedDrag.removeClass('valid-drag-over-drop');\n var currentPlace = thisQ.getClassnameNumericSuffix(placedDrag, 'inplace');\n var drop = thisQ.getDrop(drag, currentPlace);\n thisQ.sendDragToDrop(drag, drop);\n placed = true;\n return false; // Stop the each() here.\n });\n\n if (!placed) {\n this.sendDragHome(drag);\n }\n };\n\n /**\n * Animate a drag item into a given place (or back home).\n *\n * @param {jQuery|null} drag the item to place. If null, clear the place.\n * @param {jQuery} drop the place to put it.\n */\n DragDropToTextQuestion.prototype.sendDragToDrop = function(drag, drop) {\n // Is there already a drag in this drop? if so, evict it.\n var oldDrag = this.getCurrentDragInPlace(this.getPlace(drop));\n if (oldDrag.length !== 0) {\n var currentPlace = this.getClassnameNumericSuffix(oldDrag, 'inplace');\n var hiddenDrop = this.getDrop(oldDrag, currentPlace);\n hiddenDrop.addClass('active');\n oldDrag.addClass('beingdragged');\n oldDrag.offset(hiddenDrop.offset());\n this.sendDragHome(oldDrag);\n }\n\n if (drag.length === 0) {\n this.setInputValue(this.getPlace(drop), 0);\n if (drop.data('isfocus')) {\n drop.focus();\n }\n } else {\n this.setInputValue(this.getPlace(drop), this.getChoice(drag));\n drag.removeClass('unplaced')\n .addClass('placed inplace' + this.getPlace(drop));\n drag.attr('tabindex', 0);\n this.animateTo(drag, drop);\n }\n };\n\n /**\n * Animate a drag back to its home.\n *\n * @param {jQuery} drag the item being moved.\n */\n DragDropToTextQuestion.prototype.sendDragHome = function(drag) {\n var currentPlace = this.getClassnameNumericSuffix(drag, 'inplace');\n if (currentPlace !== null) {\n drag.removeClass('inplace' + currentPlace);\n }\n drag.data('unplaced', true);\n\n this.animateTo(drag, this.getDragHome(this.getGroup(drag), this.getChoice(drag)));\n };\n\n /**\n * Handles keyboard events on drops.\n *\n * Drops are focusable. Once focused, right/down/space switches to the next choice, and\n * left/up switches to the previous. Escape clear.\n *\n * @param {KeyboardEvent} e\n */\n DragDropToTextQuestion.prototype.handleKeyPress = function(e) {\n var drop = $(e.target).closest('.drop');\n if (drop.length === 0) {\n var placedDrag = $(e.target);\n var currentPlace = this.getClassnameNumericSuffix(placedDrag, 'inplace');\n if (currentPlace !== null) {\n drop = this.getDrop(placedDrag, currentPlace);\n }\n }\n var currentDrag = this.getCurrentDragInPlace(this.getPlace(drop)),\n nextDrag = $();\n\n switch (e.keyCode) {\n case keys.space:\n case keys.arrowRight:\n case keys.arrowDown:\n nextDrag = this.getNextDrag(this.getGroup(drop), currentDrag);\n break;\n\n case keys.arrowLeft:\n case keys.arrowUp:\n nextDrag = this.getPreviousDrag(this.getGroup(drop), currentDrag);\n break;\n\n case keys.escape:\n break;\n\n default:\n questionManager.isKeyboardNavigation = false;\n return; // To avoid the preventDefault below.\n }\n\n if (nextDrag.length) {\n nextDrag.data('isfocus', true);\n nextDrag.addClass('beingdragged');\n var hiddenDrag = this.getDragClone(nextDrag);\n if (hiddenDrag.length) {\n if (nextDrag.hasClass('infinite')) {\n var noOfDrags = this.noOfDropsInGroup(this.getGroup(nextDrag));\n var cloneDrags = this.getInfiniteDragClones(nextDrag, false);\n if (cloneDrags.length < noOfDrags) {\n var cloneDrag = nextDrag.clone();\n cloneDrag.removeClass('beingdragged');\n cloneDrag.removeAttr('tabindex');\n hiddenDrag.after(cloneDrag);\n questionManager.addEventHandlersToDrag(cloneDrag);\n nextDrag.offset(cloneDrag.offset());\n } else {\n hiddenDrag.addClass('active');\n nextDrag.offset(hiddenDrag.offset());\n }\n } else {\n hiddenDrag.addClass('active');\n nextDrag.offset(hiddenDrag.offset());\n }\n }\n } else {\n drop.data('isfocus', true);\n }\n\n e.preventDefault();\n this.sendDragToDrop(nextDrag, drop);\n };\n\n /**\n * Choose the next drag in a group.\n *\n * @param {int} group which group.\n * @param {jQuery} drag current choice (empty jQuery if there isn't one).\n * @return {jQuery} the next drag in that group, or null if there wasn't one.\n */\n DragDropToTextQuestion.prototype.getNextDrag = function(group, drag) {\n var choice,\n numChoices = this.noOfChoicesInGroup(group);\n\n if (drag.length === 0) {\n choice = 1; // Was empty, so we want to select the first choice.\n } else {\n choice = this.getChoice(drag) + 1;\n }\n\n var next = this.getUnplacedChoice(group, choice);\n while (next.length === 0 && choice < numChoices) {\n choice++;\n next = this.getUnplacedChoice(group, choice);\n }\n\n return next;\n };\n\n /**\n * Choose the previous drag in a group.\n *\n * @param {int} group which group.\n * @param {jQuery} drag current choice (empty jQuery if there isn't one).\n * @return {jQuery} the next drag in that group, or null if there wasn't one.\n */\n DragDropToTextQuestion.prototype.getPreviousDrag = function(group, drag) {\n var choice;\n\n if (drag.length === 0) {\n choice = this.noOfChoicesInGroup(group);\n } else {\n choice = this.getChoice(drag) - 1;\n }\n\n var previous = this.getUnplacedChoice(group, choice);\n while (previous.length === 0 && choice > 1) {\n choice--;\n previous = this.getUnplacedChoice(group, choice);\n }\n\n // Does this choice exist?\n return previous;\n };\n\n /**\n * Animate an object to the given destination.\n *\n * @param {jQuery} drag the element to be animated.\n * @param {jQuery} target element marking the place to move it to.\n */\n DragDropToTextQuestion.prototype.animateTo = function(drag, target) {\n var currentPos = drag.offset(),\n targetPos = target.offset(),\n thisQ = this;\n\n M.util.js_pending('qtype_ddwtos-animate-' + thisQ.containerId);\n // Animate works in terms of CSS position, whereas locating an object\n // on the page works best with jQuery offset() function. So, to get\n // the right target position, we work out the required change in\n // offset() and then add that to the current CSS position.\n drag.animate(\n {\n left: parseInt(drag.css('left')) + targetPos.left - currentPos.left,\n top: parseInt(drag.css('top')) + targetPos.top - currentPos.top\n },\n {\n duration: 'fast',\n done: function() {\n $('body').trigger('qtype_ddwtos-dragmoved', [drag, target, thisQ]);\n M.util.js_complete('qtype_ddwtos-animate-' + thisQ.containerId);\n }\n }\n );\n };\n\n /**\n * Detect if a point is inside a given DOM node.\n *\n * @param {Number} pageX the x position.\n * @param {Number} pageY the y position.\n * @param {jQuery} drop the node to check (typically a drop).\n * @return {boolean} whether the point is inside the node.\n */\n DragDropToTextQuestion.prototype.isPointInDrop = function(pageX, pageY, drop) {\n var position = drop.offset();\n return pageX >= position.left && pageX < position.left + drop.width()\n && pageY >= position.top && pageY < position.top + drop.height();\n };\n\n /**\n * Set the value of the hidden input for a place, to record what is currently there.\n *\n * @param {int} place which place to set the input value for.\n * @param {int} choice the value to set.\n */\n DragDropToTextQuestion.prototype.setInputValue = function(place, choice) {\n this.getRoot().find('input.placeinput.place' + place).val(choice);\n };\n\n /**\n * Get the outer div for this question.\n *\n * @returns {jQuery} containing that div.\n */\n DragDropToTextQuestion.prototype.getRoot = function() {\n return $(document.getElementById(this.containerId));\n };\n\n /**\n * Get drag home for a given choice.\n *\n * @param {int} group the group.\n * @param {int} choice the choice number.\n * @returns {jQuery} containing that div.\n */\n DragDropToTextQuestion.prototype.getDragHome = function(group, choice) {\n if (!this.getRoot().find('.draghome.dragplaceholder.group' + group + '.choice' + choice).is(':visible')) {\n return this.getRoot().find('.draggrouphomes' + group +\n ' span.draghome.infinite' +\n '.choice' + choice +\n '.group' + group);\n }\n return this.getRoot().find('.draghome.dragplaceholder.group' + group + '.choice' + choice);\n };\n\n /**\n * Get an unplaced choice for a particular group.\n *\n * @param {int} group the group.\n * @param {int} choice the choice number.\n * @returns {jQuery} jQuery wrapping the unplaced choice. If there isn't one, the jQuery will be empty.\n */\n DragDropToTextQuestion.prototype.getUnplacedChoice = function(group, choice) {\n return this.getRoot().find('.draghome.group' + group + '.choice' + choice + '.unplaced').slice(0, 1);\n };\n\n /**\n * Get the drag that is currently in a given place.\n *\n * @param {int} place the place number.\n * @return {jQuery} the current drag (or an empty jQuery if none).\n */\n DragDropToTextQuestion.prototype.getCurrentDragInPlace = function(place) {\n return this.getRoot().find('span.draghome.inplace' + place);\n };\n\n /**\n * Return the number of blanks in a given group.\n *\n * @param {int} group the group number.\n * @returns {int} the number of drops.\n */\n DragDropToTextQuestion.prototype.noOfDropsInGroup = function(group) {\n return this.getRoot().find('.drop.group' + group).length;\n };\n\n /**\n * Return the number of choices in a given group.\n *\n * @param {int} group the group number.\n * @returns {int} the number of choices.\n */\n DragDropToTextQuestion.prototype.noOfChoicesInGroup = function(group) {\n return this.getRoot().find('.draghome.group' + group).length;\n };\n\n /**\n * Return the number at the end of the CSS class name with the given prefix.\n *\n * @param {jQuery} node\n * @param {String} prefix name prefix\n * @returns {Number|null} the suffix if found, else null.\n */\n DragDropToTextQuestion.prototype.getClassnameNumericSuffix = function(node, prefix) {\n var classes = node.attr('class');\n if (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 * Get the choice number of a drag.\n *\n * @param {jQuery} drag the drag.\n * @returns {Number} the choice number.\n */\n DragDropToTextQuestion.prototype.getChoice = function(drag) {\n return this.getClassnameNumericSuffix(drag, 'choice');\n };\n\n /**\n * Given a DOM node that is significant to this question\n * (drag, drop, ...) get the group it belongs to.\n *\n * @param {jQuery} node a DOM node.\n * @returns {Number} the group it belongs to.\n */\n DragDropToTextQuestion.prototype.getGroup = function(node) {\n return this.getClassnameNumericSuffix(node, 'group');\n };\n\n /**\n * Get the place number of a drop, or its corresponding hidden input.\n *\n * @param {jQuery} node the DOM node.\n * @returns {Number} the place number.\n */\n DragDropToTextQuestion.prototype.getPlace = function(node) {\n return this.getClassnameNumericSuffix(node, 'place');\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 DragDropToTextQuestion.prototype.getDragClone = function(drag) {\n return this.getRoot().find('.draggrouphomes' +\n this.getGroup(drag) +\n ' span.draghome' +\n '.choice' + this.getChoice(drag) +\n '.group' + this.getGroup(drag) +\n '.dragplaceholder');\n };\n\n /**\n * Get infinite drag clones for given drag.\n *\n * @param {jQuery} drag the drag.\n * @param {Boolean} inHome in the home area or not.\n * @returns {jQuery} the drag's clones.\n */\n DragDropToTextQuestion.prototype.getInfiniteDragClones = function(drag, inHome) {\n if (inHome) {\n return this.getRoot().find('.draggrouphomes' +\n this.getGroup(drag) +\n ' span.draghome' +\n '.choice' + this.getChoice(drag) +\n '.group' + this.getGroup(drag) +\n '.infinite').not('.dragplaceholder');\n }\n return this.getRoot().find('span.draghome' +\n '.choice' + this.getChoice(drag) +\n '.group' + this.getGroup(drag) +\n '.infinite').not('.dragplaceholder');\n };\n\n /**\n * Get drop for a given drag and place.\n *\n * @param {jQuery} drag the drag.\n * @param {Integer} currentPlace the current place of drag.\n * @returns {jQuery} the drop's clone.\n */\n DragDropToTextQuestion.prototype.getDrop = function(drag, currentPlace) {\n return this.getRoot().find('.drop.group' + this.getGroup(drag) + '.place' + currentPlace);\n };\n\n /**\n * Check that the drag is drop to it's clone.\n *\n * @param {jQuery} drag The drag.\n * @param {jQuery} drop The drop.\n * @returns {boolean}\n */\n DragDropToTextQuestion.prototype.isDragSameAsDrop = function(drag, drop) {\n return this.getChoice(drag) === this.getChoice(drop) && this.getGroup(drag) === this.getGroup(drop);\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 * {boolean} used to ensure the event handlers are only initialised once per page.\n */\n eventHandlersInitialised: false,\n\n /**\n * {Object} ensures that the drag event handlers are only initialised once per question,\n * indexed by containerId (id on the .que div).\n */\n dragEventHandlersInitialised: {},\n\n /**\n * {boolean} is keyboard navigation or not.\n */\n isKeyboardNavigation: false,\n\n /**\n * {DragDropToTextQuestion[]} all the questions on this page, indexed by containerId (id on the .que div).\n */\n questions: {},\n\n /**\n * Initialise questions.\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 */\n init: function(containerId, readOnly) {\n questionManager.questions[containerId] = new DragDropToTextQuestion(containerId, readOnly);\n if (!questionManager.eventHandlersInitialised) {\n questionManager.setupEventHandlers();\n questionManager.eventHandlersInitialised = true;\n }\n if (!questionManager.dragEventHandlersInitialised.hasOwnProperty(containerId)) {\n questionManager.dragEventHandlersInitialised[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('ddwtos') &&\n !questionContainer.classList.contains('qtype_ddwtos-readonly')) {\n // TODO: Convert all the jQuery selectors and events to native Javascript.\n questionManager.addEventHandlersToDrag($(questionContainer).find('span.draghome'));\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 $('body')\n .on('keydown',\n '.que.ddwtos:not(.qtype_ddwtos-readonly) span.drop',\n questionManager.handleKeyPress)\n .on('keydown',\n '.que.ddwtos:not(.qtype_ddwtos-readonly) span.draghome.placed:not(.beingdragged)',\n questionManager.handleKeyPress)\n .on('qtype_ddwtos-dragmoved', questionManager.handleDragMoved);\n },\n\n /**\n * Binding the drag/touch event again for newly created element.\n *\n * @param {jQuery} element Element to bind the event\n */\n addEventHandlersToDrag: function(element) {\n // Unbind all the mousedown and touchstart events to prevent double binding.\n element.unbind('mousedown touchstart');\n element.on('mousedown touchstart', questionManager.handleDragStart);\n },\n\n /**\n * Handle mouse down / touch start on drags.\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 on drops.\n * @param {KeyboardEvent} e\n */\n handleKeyPress: function(e) {\n if (questionManager.isKeyboardNavigation) {\n return;\n }\n questionManager.isKeyboardNavigation = true;\n var question = questionManager.getQuestionForEvent(e);\n if (question) {\n question.handleKeyPress(e);\n }\n },\n\n /**\n * Given an event, work out which question it affects.\n *\n * @param {Event} e the event.\n * @returns {DragDropToTextQuestion|undefined} The question, or undefined.\n */\n getQuestionForEvent: function(e) {\n var containerId = $(e.currentTarget).closest('.que.ddwtos').attr('id');\n return questionManager.questions[containerId];\n },\n\n /**\n * Handle when drag moved.\n *\n * @param {Event} e the event.\n * @param {jQuery} drag the drag\n * @param {jQuery} target the target\n * @param {DragDropToTextQuestion} thisQ the question.\n */\n handleDragMoved: function(e, drag, target, thisQ) {\n drag.removeClass('beingdragged');\n drag.css('top', '').css('left', '');\n target.after(drag);\n target.removeClass('active');\n if (typeof drag.data('unplaced') !== 'undefined' && drag.data('unplaced') === true) {\n drag.removeClass('placed').addClass('unplaced');\n drag.removeAttr('tabindex');\n drag.removeData('unplaced');\n if (drag.hasClass('infinite') && thisQ.getInfiniteDragClones(drag, true).length > 1) {\n thisQ.getInfiniteDragClones(drag, true).first().remove();\n }\n }\n if (typeof drag.data('isfocus') !== 'undefined' && drag.data('isfocus') === true) {\n drag.focus();\n drag.removeData('isfocus');\n }\n if (typeof target.data('isfocus') !== 'undefined' && target.data('isfocus') === true) {\n target.removeData('isfocus');\n }\n if (questionManager.isKeyboardNavigation) {\n questionManager.isKeyboardNavigation = false;\n }\n if (thisQ.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 thisQ.questionAnswer = thisQ.getQuestionAnsweredValues();\n }\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_ddwtos/ddwtos\n */\n return {\n /**\n * Initialise one drag-drop into text 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 */\n init: questionManager.init\n };\n});\n"],"names":["define","$","dragDrop","keys","FormChangeChecker","DragDropToTextQuestion","containerId","readOnly","questionAnswer","getRoot","addClass","resizeAllDragsAndDrops","cloneDrags","positionDrags","prototype","thisQ","this","find","each","i","node","resizeAllDragsAndDropsInGroup","getClassnameNumericSuffix","group","dragHomes","maxWidth","maxHeight","drag","Math","max","ceil","offsetWidth","offsetHeight","setElementSize","drop","element","width","height","css","index","draghome","placeHolder","clone","removeClass","getChoice","getGroup","before","root","not","dragNode","currentPlace","removeAttr","inputNode","input","choice","val","place","getPlace","dropPosition","offset","data","top","left","unplacedDrag","getUnplacedChoice","hiddenDrag","getDragClone","length","hasClass","noOfDrags","noOfDropsInGroup","getInfiniteDragClones","cloneDrag","after","questionManager","addEventHandlersToDrag","sendDragToDrop","getQuestionAnsweredValues","result","id","value","isQuestionInteracted","oldAnswer","newAnswer","isInteracted","JSON","stringify","Object","forEach","key","handleDragStart","e","target","closest","prepare","start","setInputValue","hiddenDrop","getDrop","x","y","dragMove","dragEnd","pageX","pageY","dropNode","isPointInDrop","isDragSameAsDrop","placed","placedNode","placedDrag","sendDragHome","oldDrag","getCurrentDragInPlace","focus","attr","animateTo","getDragHome","handleKeyPress","currentDrag","nextDrag","keyCode","space","arrowRight","arrowDown","getNextDrag","arrowLeft","arrowUp","getPreviousDrag","escape","isKeyboardNavigation","preventDefault","numChoices","noOfChoicesInGroup","next","previous","currentPos","targetPos","M","util","js_pending","animate","parseInt","duration","done","trigger","js_complete","position","document","getElementById","is","slice","prefix","classes","classesArr","split","RegExp","test","match","exec","Number","inHome","eventHandlersInitialised","dragEventHandlersInitialised","questions","init","setupEventHandlers","hasOwnProperty","questionContainer","classList","contains","on","handleDragMoved","unbind","question","getQuestionForEvent","currentTarget","removeData","first","remove","handleFormDirty","responseForm","markFormAsDirty"],"mappings":"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"}