diff --git a/question/type/ddwtos/amd/build/ddwtos.min.js b/question/type/ddwtos/amd/build/ddwtos.min.js index 03e96e424b5..e212c72d39e 100644 --- a/question/type/ddwtos/amd/build/ddwtos.min.js +++ b/question/type/ddwtos/amd/build/ddwtos.min.js @@ -1,2 +1,2 @@ -define ("qtype_ddwtos/ddwtos",["jquery","core/dragdrop","core/key_codes"],function(a,b,c){"use strict";function d(a,b){this.containerId=a;if(b){this.getRoot().addClass("qtype_ddwtos-readonly")}this.resizeAllDragsAndDrops();this.cloneDrags();this.positionDrags()}d.prototype.resizeAllDragsAndDrops=function(){var b=this;this.getRoot().find(".answercontainer > div").each(function(c,d){b.resizeAllDragsAndDropsInGroup(b.getClassnameNumericSuffix(a(d),"draggrouphomes"))})};d.prototype.resizeAllDragsAndDropsInGroup=function(a){var b=this,c=this.getRoot().find(".draggrouphomes"+a+" span.draghome"),d=0,e=0;c.each(function(a,b){d=Math.max(d,Math.ceil(b.offsetWidth));e=Math.max(e,Math.ceil(0+b.offsetHeight))});d+=8;e+=2;c.each(function(a,c){b.setElementSize(c,d,e)});this.getRoot().find("span.drop.group"+a).each(function(a,c){b.setElementSize(c,d,e)})};d.prototype.setElementSize=function(b,c,d){a(b).width(c).height(d).css("lineHeight",d+"px")};d.prototype.cloneDrags=function(){var b=this;b.getRoot().find("span.draghome").each(function(c,d){var e=a(d),f=e.clone();f.removeClass();f.addClass("draghome choice"+b.getChoice(e)+" group"+b.getGroup(e)+" dragplaceholder");e.before(f)})};d.prototype.positionDrags=function(){var b=this,c=this.getRoot();c.find("span.draghome").not(".dragplaceholder").each(function(c,d){var e=a(d),f=b.getClassnameNumericSuffix(e,"inplace");e.addClass("unplaced").removeClass("placed");e.removeAttr("tabindex");if(null!==f){e.removeClass("inplace"+f)}});c.find("input.placeinput").each(function(d,e){var f=a(e),g=f.val(),h=b.getPlace(f),i=c.find(".drop.place"+h),j=i.offset();i.data("prev-top",j.top).data("prev-left",j.left);if("0"===g){return}var k=b.getUnplacedChoice(b.getGroup(f),g),l=b.getDragClone(k);if(l.length){l.addClass("active")}b.sendDragToDrop(b.getUnplacedChoice(b.getGroup(f),g),i)})};d.prototype.handleDragStart=function(c){var d=this,e=a(c.target).closest(".draghome"),f=b.prepare(c);if(!f.start){return}e.addClass("beingdragged");var g=this.getClassnameNumericSuffix(e,"inplace");if(null!==g){this.setInputValue(g,0);e.removeClass("inplace"+g);var h=d.getDrop(e,g);if(h.length){h.addClass("active");e.offset(h.offset())}}else{var i=d.getDragClone(e);if(i.length){if(e.hasClass("infinite")){var j=this.noOfDropsInGroup(this.getGroup(e)),k=this.getInfiniteDragClones(e,!1);if(k.length=d.left&&a=d.top&&b div").each(function(c,d){b.resizeAllDragsAndDropsInGroup(b.getClassnameNumericSuffix(a(d),"draggrouphomes"))})};d.prototype.resizeAllDragsAndDropsInGroup=function(a){var b=this,c=this.getRoot().find(".draggrouphomes"+a+" span.draghome"),d=0,e=0;c.each(function(a,b){d=Math.max(d,Math.ceil(b.offsetWidth));e=Math.max(e,Math.ceil(0+b.offsetHeight))});d+=8;e+=2;c.each(function(a,c){b.setElementSize(c,d,e)});this.getRoot().find("span.drop.group"+a).each(function(a,c){b.setElementSize(c,d,e)})};d.prototype.setElementSize=function(b,c,d){a(b).width(c).height(d).css("lineHeight",d+"px")};d.prototype.cloneDrags=function(){var b=this;b.getRoot().find("span.draghome").each(function(c,d){var e=a(d),f=e.clone();f.removeClass();f.addClass("draghome choice"+b.getChoice(e)+" group"+b.getGroup(e)+" dragplaceholder");e.before(f)})};d.prototype.positionDrags=function(){var b=this,c=this.getRoot();c.find("span.draghome").not(".dragplaceholder").each(function(c,d){var e=a(d),f=b.getClassnameNumericSuffix(e,"inplace");e.addClass("unplaced").removeClass("placed");e.removeAttr("tabindex");if(null!==f){e.removeClass("inplace"+f)}});c.find("input.placeinput").each(function(d,e){var f=a(e),g=f.val(),h=b.getPlace(f),i=c.find(".drop.place"+h),j=i.offset();i.data("prev-top",j.top).data("prev-left",j.left);if("0"===g){return}var k=b.getUnplacedChoice(b.getGroup(f),g),l=b.getDragClone(k);if(l.length){l.addClass("active")}b.sendDragToDrop(b.getUnplacedChoice(b.getGroup(f),g),i)})};d.prototype.handleDragStart=function(c){var d=this,e=a(c.target).closest(".draghome"),f=b.prepare(c);if(!f.start){return}e.addClass("beingdragged");var g=this.getClassnameNumericSuffix(e,"inplace");if(null!==g){this.setInputValue(g,0);e.removeClass("inplace"+g);var h=d.getDrop(e,g);if(h.length){h.addClass("active");e.offset(h.offset())}}else{var i=d.getDragClone(e);if(i.length){if(e.hasClass("infinite")){var j=this.noOfDropsInGroup(this.getGroup(e)),k=this.getInfiniteDragClones(e,!1);if(k.length=d.left&&a=d.top&&b.\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 * @package qtype_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(['jquery', 'core/dragdrop', 'core/key_codes'], function($, dragDrop, keys) {\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 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 hiddenDrag.addClass('active');\n }\n // Send the drag to drop.\n thisQ.sendDragToDrop(thisQ.getUnplacedChoice(thisQ.getGroup(input), choice), drop);\n });\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) {\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 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)) {\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)) {\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 oldDrag.addClass('beingdragged');\n oldDrag.offset(oldDrag.offset());\n var currentPlace = this.getClassnameNumericSuffix(oldDrag, 'inplace');\n var hiddenDrop = this.getDrop(oldDrag, currentPlace);\n hiddenDrop.addClass('active');\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 return; // To avoid the preventDefault below.\n }\n\n if (nextDrag.length) {\n nextDrag.data('isfocus', true);\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('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 * 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 * {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 },\n\n /**\n * Set up the event handlers that make this question type work. (Done once per page.)\n */\n setupEventHandlers: function() {\n $('body').on('mousedown touchstart',\n '.que.ddwtos:not(.qtype_ddwtos-readonly) span.draghome',\n questionManager.handleDragStart)\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('dragmoved', questionManager.handleDragMoved);\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 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 setTimeout(function() {\n thisQ.getInfiniteDragClones(drag, true).first().remove();\n });\n }\n }\n if (typeof drag.data('isfocus') !== 'undefined' && drag.data('isfocus') === true) {\n var hiddenDrag = thisQ.getDragClone(drag);\n if (hiddenDrag.length) {\n if (drag.hasClass('infinite')) {\n var noOfDrags = thisQ.noOfDropsInGroup(thisQ.getGroup(drag));\n var cloneDrags = thisQ.getInfiniteDragClones(drag, false);\n if (cloneDrags.length < noOfDrags) {\n var cloneDrag = drag.clone();\n cloneDrag.removeClass('beingdragged');\n cloneDrag.removeAttr('tabindex');\n hiddenDrag.after(cloneDrag);\n } else {\n hiddenDrag.addClass('active');\n }\n } else {\n hiddenDrag.addClass('active');\n }\n }\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 }\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"],"file":"ddwtos.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/ddwtos.js"],"names":["define","$","dragDrop","keys","DragDropToTextQuestion","containerId","readOnly","getRoot","addClass","resizeAllDragsAndDrops","cloneDrags","positionDrags","prototype","thisQ","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","sendDragToDrop","handleDragStart","e","target","closest","info","prepare","start","setInputValue","hiddenDrop","getDrop","hasClass","noOfDrags","noOfDropsInGroup","getInfiniteDragClones","cloneDrag","after","x","y","dragMove","dragEnd","pageX","pageY","dropNode","isPointInDrop","placed","placedNode","placedDrag","sendDragHome","oldDrag","getCurrentDragInPlace","focus","attr","animateTo","getDragHome","handleKeyPress","currentDrag","nextDrag","keyCode","space","arrowRight","arrowDown","getNextDrag","arrowLeft","arrowUp","getPreviousDrag","escape","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","patt1","RegExp","test","match","exec","inHome","questionManager","eventHandlersInitialised","questions","init","setupEventHandlers","on","handleDragMoved","question","getQuestionForEvent","currentTarget","removeData","setTimeout","first","remove"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * JavaScript to 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 * @package qtype_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(['jquery', 'core/dragdrop', 'core/key_codes'], function($, dragDrop, keys) {\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 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 hiddenDrag.addClass('active');\n }\n // Send the drag to drop.\n thisQ.sendDragToDrop(thisQ.getUnplacedChoice(thisQ.getGroup(input), choice), drop);\n });\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) {\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 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)) {\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)) {\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 return; // To avoid the preventDefault below.\n }\n\n if (nextDrag.length) {\n nextDrag.data('isfocus', true);\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('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 * 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 * {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 },\n\n /**\n * Set up the event handlers that make this question type work. (Done once per page.)\n */\n setupEventHandlers: function() {\n $('body').on('mousedown touchstart',\n '.que.ddwtos:not(.qtype_ddwtos-readonly) span.draghome',\n questionManager.handleDragStart)\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('dragmoved', questionManager.handleDragMoved);\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 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 setTimeout(function() {\n thisQ.getInfiniteDragClones(drag, true).first().remove();\n });\n }\n }\n if (typeof drag.data('isfocus') !== 'undefined' && drag.data('isfocus') === true) {\n var hiddenDrag = thisQ.getDragClone(drag);\n if (hiddenDrag.length) {\n if (drag.hasClass('infinite')) {\n var noOfDrags = thisQ.noOfDropsInGroup(thisQ.getGroup(drag));\n var cloneDrags = thisQ.getInfiniteDragClones(drag, false);\n if (cloneDrags.length < noOfDrags) {\n var cloneDrag = drag.clone();\n cloneDrag.removeClass('beingdragged');\n cloneDrag.removeAttr('tabindex');\n hiddenDrag.after(cloneDrag);\n } else {\n hiddenDrag.addClass('active');\n }\n } else {\n hiddenDrag.addClass('active');\n }\n }\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 }\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"],"file":"ddwtos.min.js"} \ No newline at end of file diff --git a/question/type/ddwtos/amd/src/ddwtos.js b/question/type/ddwtos/amd/src/ddwtos.js index 3e9cee5b88e..3369db280f0 100644 --- a/question/type/ddwtos/amd/src/ddwtos.js +++ b/question/type/ddwtos/amd/src/ddwtos.js @@ -314,11 +314,11 @@ define(['jquery', 'core/dragdrop', 'core/key_codes'], function($, dragDrop, keys // Is there already a drag in this drop? if so, evict it. var oldDrag = this.getCurrentDragInPlace(this.getPlace(drop)); if (oldDrag.length !== 0) { - oldDrag.addClass('beingdragged'); - oldDrag.offset(oldDrag.offset()); var currentPlace = this.getClassnameNumericSuffix(oldDrag, 'inplace'); var hiddenDrop = this.getDrop(oldDrag, currentPlace); hiddenDrop.addClass('active'); + oldDrag.addClass('beingdragged'); + oldDrag.offset(hiddenDrop.offset()); this.sendDragHome(oldDrag); } diff --git a/question/type/ddwtos/styles.css b/question/type/ddwtos/styles.css index fc1067f2b17..35fb15b0947 100644 --- a/question/type/ddwtos/styles.css +++ b/question/type/ddwtos/styles.css @@ -34,7 +34,7 @@ cursor: move; } -.que.ddwtos .readonly .draghome { +.que.ddwtos.qtype_ddwtos-readonly .draghome { cursor: default; }