diff --git a/question/type/ddwtos/amd/build/ddwtos.min.js b/question/type/ddwtos/amd/build/ddwtos.min.js new file mode 100644 index 00000000000..3c4d47542cb --- /dev/null +++ b/question/type/ddwtos/amd/build/ddwtos.min.js @@ -0,0 +1 @@ +define(["jquery","core/dragdrop"],function(a,b){"use strict";var c={DRAG_PROXIMITY:10,CONTAINER_BORDER_TOLERANCE:10,ANIMATION_DURATION:300,params:{},nextDragItemNo:1,cssSelectors:function(a){return{topNode:function(){return a},dragContainer:function(){return a+" div.drags"},drags:function(){return this.dragContainer()+" span.drag"},drag:function(a){return this.drags()+".no"+a},dragsInGroup:function(a){return this.drags()+".group"+a},dragsForChoiceInGroup:function(a,b){return this.dragsInGroup(b)+".choice"+a},placedDragsInGroup:function(a){return this.dragsInGroup(a)+".placed"},unplacedDragsInGroup:function(a){return this.dragsInGroup(a)+".unplaced"},unplacedDragsForChoiceInGroup:function(a,b){return this.unplacedDragsInGroup(b)+".choice"+a},dragHomes:function(){return a+" span.draghome"},dragHomesGroup:function(b){return a+" .draggrouphomes"+b+" span.draghome"},dragHome:function(b,c){return a+" .draggrouphomes"+b+" span.draghome.choice"+c},drops:function(){return a+" span.drop"},dropForPlace:function(a){return this.drops()+".place"+a},dropsInGroup:function(a){return this.drops()+".group"+a},dropsGroup:function(b){return a+" span.drop.group"+b}}},getInputIds:function(b){var c=[],d=0;return a(b).find("input[type=hidden]").each(function(e,f){var g=parseInt(b.match(/\d/g));a(f)[0].id.indexOf("_"+g+"_p")!==-1&&(c[++d]=a(f)[0].id)}),c},getInputId:function(a,b){return c.getInputIds(a)[b]},init:function(a){var b="qtype_ddwtos-"+Math.random().toString(36).slice(2);M.util.js_pending(b),c.params=a,c.initQuestion(a.topnode),M.util.js_complete(b)},updateOnResize:function(b){a(window).on("resize",function(){c.updateDragsPositions(b)}).resize()},initQuestion:function(b){1!==a(b).data("initialised")&&(c.setPaddingSizesAll(b),c.cloneDragItems(b),c.updateDragsPositions(b),c.updateOnResize(b),c.makeDropZones(b),a(b).data("initialised",1))},putDragInDrop:function(b,d,e){var f=a("input#"+c.getInputId(b,c.getPlace(e)));if(f.val()>0){var g=a(c.cssSelectors(b).dragsForChoiceInGroup(f.val(),c.getGroup(e)));g.length&&c.putBackToOrigin(b,g)}d&&d.length?(f.val(c.getChoice(d)),c.setDragOffset(d,e),d.css("z-index",Number(d.css("z-index"))-1)):(f.val(0),d.removeClass("placed"),d.addClass("unplaced"))},updateDragsPositions:function(b){var d=a(c.cssSelectors(b).drags());d.addClass("unplaced");for(var e=0;ef.rt.X?(a(b).addClass("outside-container"),!0):e>f.lb.Y||df.rb.Y||d>f.rb.X?(a(b).addClass("outside-container"),!0):(a(b).removeClass("outside-container"),!1)},animateEl:function(a,b,c,d){var e=Math.abs(a.css("left")-b.offset().left)+Math.abs(a.css("top")-b.offset().top);e=e>1?e:1,a.animate({opacity:.5,width:1.2*parseInt(a.css("width"))+"px",height:1.2*parseInt(a.css("height"))+"px",top:b.offset().top,left:b.offset().left},{duration:c/e,easing:d}),a.animate({opacity:1,width:parseInt(a.css("width"))+"px",height:parseInt(a.css("height"))+"px",top:b.offset().top,left:b.offset().left},{duration:c/e,easing:d})},isDragCloseToTarget:function(a,b){var d=a.offset().left+a.outerWidth()/2,e=a.offset().top+a.outerHeight()/2,f=b.offset().left+b.outerWidth()/2,g=b.offset().top+b.outerHeight()/2;return d>f-c.DRAG_PROXIMITY&&dg-c.DRAG_PROXIMITY&&e. + +/* + * JavaScript to allow dragging options to slots (using mouse down or touch) or tab through slots using keyboard. + * + * @package qtype + * @subpackage ddwtos + * @copyright 2018 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define(['jquery', 'core/dragdrop'], function ($, dd) { + + "use strict"; + + var t = { + /** + * The accuracy of the drag item in brink of a slot to be accepted. + * @private + + + */ + DRAG_PROXIMITY: 10, + + /** + * Tolerance in px for highlighting the borders of question container + * when a drag is about to leave the containe's boundaries. + * @private + */ + CONTAINER_BORDER_TOLERANCE: 10, + + /** + * Animation time in milliseconds + * @private + */ + ANIMATION_DURATION: 300, + + /** + * Params object, Todo: I only need to know whether the question is 'readonly' + * @private + */ + params: {}, + + nextDragItemNo : 1, + + /** + * Put all our selectors in the same place so we can quickly find and change them later + * if the structure of the document changes. + */ + cssSelectors: function (topnode) { + return { + topNode: function () { + return topnode; + }, + dragContainer: function () { + return topnode + ' div.drags'; + }, + drags: function () { + return this.dragContainer() + ' span.drag'; + }, + drag: function (no) { + return this.drags() + '.no' + no; + }, + dragsInGroup: function (groupno) { + return this.drags() + '.group' + groupno; + }, + dragsForChoiceInGroup: function (choiceno, groupno) { + return this.dragsInGroup(groupno) + '.choice' + choiceno; + }, + placedDragsInGroup: function (groupno) { + return this.dragsInGroup(groupno) + '.placed'; + }, + unplacedDragsInGroup: function (groupno) { + return this.dragsInGroup(groupno) + '.unplaced'; + }, + unplacedDragsForChoiceInGroup: function (choiceno, groupno) { + return this.unplacedDragsInGroup(groupno) + '.choice' + choiceno; + }, + dragHomes: function () { + return topnode + ' span.draghome'; + }, + dragHomesGroup: function (groupno) { + return topnode + ' .draggrouphomes' + groupno + ' span.draghome'; + }, + dragHome: function (groupno, choiceno) { + return topnode + ' .draggrouphomes' + groupno + ' span.draghome.choice' + choiceno; + }, + // List of selectors related to drops. + drops: function () { + return topnode + ' span.drop'; + }, + dropForPlace: function (placeno) { + return this.drops() + '.place' + placeno; + }, + dropsInGroup: function (groupno) { + return this.drops() + '.group' + groupno; + }, + dropsGroup: function (groupno) { + return topnode + ' span.drop.group' + groupno; + } + }; + }, + + /** + * Return an array of inputids. + * @param topNode + * @returns {Array} + */ + getInputIds : function (topNode) { + var inputIds = []; + var count = 0; + $(topNode).find('input[type=hidden]').each(function(index, input) { + var questionIdNumber = parseInt(topNode.match(/\d/g)); + if ($(input)[0].id.indexOf('_' + questionIdNumber + '_p') !== -1) { + inputIds[++count] = $(input)[0].id; + } + }); + return inputIds; + }, + + /** + * Return a given inputid + * @param topNode + * @param no + * @returns {*} + */ + getInputId : function(topNode, no) { + return t.getInputIds(topNode)[no]; + }, + + /** + * Initialise questions + * @param params + */ + init : function (params) { + var pendingId = 'qtype_ddwtos-' + Math.random().toString(36).slice(2); // Random string. + M.util.js_pending(pendingId); + t.params = params; + t.initQuestion(params['topnode']); + M.util.js_complete(pendingId); + }, + + /** + * Update the position on the drags on resize + * @param string topNode, unique question id + */ + updateOnResize : function(topNode) { + $(window).on('resize', function () { + t.updateDragsPositions(topNode); + }).resize(); + }, + + /** + * Initialise question. + * @param topNode + */ + initQuestion: function (topNode) { + // Return if question has been initialised. + if ($(topNode).data('initialised') === 1 ) { + return; + } + t.setPaddingSizesAll(topNode); + t.cloneDragItems(topNode); + t.updateDragsPositions(topNode); + t.updateOnResize(topNode); + t.makeDropZones(topNode); + // Remember that the question has been initialised. + $(topNode).data('initialised', 1); + }, + + /** + * Set the position of the drag to the position of the drop item. + * @param topNode + * @param drag + * @param drop + */ + putDragInDrop: function (topNode, drag, drop) { + var inputNode = $('input#' + t.getInputId(topNode, t.getPlace(drop))); + if (inputNode.val() > 0) { + var prevDrag = $(t.cssSelectors(topNode).dragsForChoiceInGroup(inputNode.val(), t.getGroup(drop))); + if (prevDrag.length) { + t.putBackToOrigin(topNode, prevDrag); + } + } + if (drag && drag.length) { + inputNode.val(t.getChoice(drag)); + t.setDragOffset(drag, drop); + drag.css('z-index', (Number(drag.css('z-index')) - 1)); + } else { + inputNode.val(0); + drag.removeClass('placed'); + drag.addClass('unplaced'); + } + }, + + /** + * Update the position of drags + * @param topNode + */ + updateDragsPositions : function (topNode) { + var unplacedDrags = $(t.cssSelectors(topNode).drags()); + unplacedDrags.addClass('unplaced'); + for (var i = 0; i < unplacedDrags.length; i++) { + var unplaceDrag = $(unplacedDrags[i]); + var groupNo = t.getGroup(unplaceDrag); + var choiceNo = t.getChoice(unplaceDrag); + unplaceDrag.offset($(topNode + ' .draggrouphomes' + groupNo + ' span:nth-child(' + choiceNo + ')').offset()); + } + // Let the drag in target follow the position of the target after resize. + for (var pn in t.getInputIds(topNode)) { + //var drop = $(t.cssSelectors(topNode).dropForPlace(pn)); + var inputNode = $('input#' + t.getInputId(topNode, pn)); + var choiceNo = Number(inputNode.val()); + if (choiceNo !== 0) { + var drop = $(t.cssSelectors(topNode).dropForPlace(pn)); + var groupNo = t.getGroup(drop); + var drag = $(t.cssSelectors(topNode).unplacedDragsForChoiceInGroup(choiceNo, groupNo)); + if (drag.hasClass('infinite')) { + drag = $(t.cssSelectors(topNode).drag(t.getNo(drag))); + } + t.setDragOffset(drag, drop); + } + } + }, + + /** + * Invisible 'drag homes' are output by the renderer. These have the same properties + * as the drag items but are invisible. We clone these invisible elements to make the + * actual drag items. + * @param topNode + */ + cloneDragItems: function(topNode) { + t.nextDragItemNo = 1; + $(t.cssSelectors(topNode).dragHomes()).each(function(index, draghome) { + t.cloneDragItemsForOneChoice(topNode, $(draghome)); + }); + }, + /** + * Clone drag item for one choice. + * @param topNode + * @param draghome + */ + cloneDragItemsForOneChoice: function(topNode, draghome) { + if (draghome.hasClass('infinite')) { + var groupno = t.getGroup(draghome); + var noofdrags = $(t.cssSelectors(topNode).dropsInGroup(groupno)).length; + for (var i = 0; i < noofdrags; i++) { + t.cloneDragItem(topNode, draghome); + } + } else { + t.cloneDragItem(topNode, draghome); + } + }, + /** + * Clone drag item. + * @param topNode + * @param draghome + */ + cloneDragItem: function(topNode, draghome) { + var drag = draghome.clone(); + drag.removeClass('draghome'); + drag.addClass('drag'); + drag.addClass('no' + t.nextDragItemNo); + t.nextDragItemNo++; + drag.css({ + 'top': draghome.offset().top, + 'left': draghome.offset().left, + 'visibility': 'visible', + 'position': 'absolute' + }); + $(t.cssSelectors(topNode).dragContainer()).append(drag); + if (!t.params['readonly']) { + drag.addClass('unplaced'); + drag.on('mousedown touchstart', t.mouseDownOrTouchStart(topNode, drag)); + } + }, + + /** + * Return a function on mousedown or touchstart. + * @param dragProxy + * @returns {Function} + */ + mouseDownOrTouchStart: function(topNode, dragProxy) { + return function (e) { + if (dd.prepare(e).start === true) { + e.preventDefault(); + dragProxy.css('cursor', 'move'); + dragProxy.addClass('moodle-has-zindex'); + var choice = t.getChoice(dragProxy); + var group = t.getGroup(dragProxy); + var draginfo = {}; + draginfo.id = 'g' + group + 'c' + choice; + draginfo.group = group; + draginfo.choice = choice; + draginfo.drag = dragProxy; + draginfo.origin = dragProxy.position(); + var om = function (x, y) { + t.onMove(topNode, x, y, draginfo, dragProxy); + }; + var od = function () { + t.onDrop(topNode, draginfo, dragProxy); + }; + dragProxy.css('z-index', (Number(dragProxy.css('z-index')) + 1)); + dd.start(e, dragProxy, om, od); + } + }; + }, + + /** + * Called when user drags a drag item. + * + * @param topNode + * @param x + * @param y + * @param draginfo + * @param dragProxy + */ + onMove: function (topNode, x, y, draginfo, dragProxy) { + var container = $(topNode).find('.formulation'); + if (t.isDragOutsideQuestionContainer(container, x, y)) { + // Highlight the border around the question container. + container.addClass('outside-container'); + + dragProxy.on('mouseup', function() { + // Remove the highlighted border around the question container. + container.removeClass('outside-container'); + }); + return; + } + var drops = $(t.cssSelectors(topNode).dropsInGroup(draginfo.group)); + drops.each(function(index, drop){ + if (t.isDragCloseToTarget(dragProxy, $(drop))) { + $(drop).addClass('valid-drag-over-drop'); + } else { + $(drop).removeClass('valid-drag-over-drop'); + } + }); + }, + + /** + * Called when user drops a drag item and applies the change. + * + * @param topNode + * @param draginfo + * @param dragProxy + */ + onDrop: function (topNode, draginfo, dragProxy) { + var drops = $(t.cssSelectors(topNode).dropsInGroup(draginfo.group)); + var breakOut = false; + drops.each(function(index, drop){ + // If dropping the same drag inside the drop break out. + if (t.isDragInTheSameDrop(dragProxy, $(drop))) { + breakOut = true; + return false; + } + if (t.isDragCloseToTarget(dragProxy, $(drop))) { + $(drop).removeClass('valid-drag-over-drop'); + t.putDragInDrop(topNode, $(dragProxy), $(drop)); + + breakOut = true; + return false; + } + }); + if(breakOut) { + //breakOut = false; + return false; + } else { + t.putBackToOrigin(topNode, dragProxy); + } + }, + + /** + * Make drop zones for keyboard access. + * @param topNode + */ + makeDropZones: function(topNode) { + $(t.cssSelectors(topNode).drops()).each(function(index, drop){ + t.makeDropZone(topNode, $(drop)); + }); + }, + + /** + * Make a drop dropn zone to accept key press. + * @param topNode + * @param drop + */ + makeDropZone: function(topNode, drop) { + if (!t.params['readonly']) { + drop.on('keydown', function(e) { + t.dropZoneKeyPress(topNode, e); + }); + } + }, + + /** + * It put back the drag in position + * @param drag + */ + putBackToOrigin: function (topNode, drag) { + drag.removeClass('placed'); + drag.addClass('unplaced'); + var groupNo = t.getGroup(drag); + var choiceNo = t.getChoice(drag); + // Find the drag in the list of this draggrouphomes, make the original visible and destroy the cloned drag. + var dragHome = $(topNode + ' .draggrouphomes' + groupNo + ' span:nth-child(' + choiceNo + ')'); + // Animate the option back to the original position. + t.animateEl(drag, dragHome, t.ANIMATION_DURATION, 'swing'); + }, + + /** + * Check whether a slot contains an option and returns the option. + * + * @param slot the slot object which may contain an option object + * @returns option object if slot contains an option or null if it doesn't + */ + isDragInDrop: function(topNode, drop) { + if (!drop.length) { + return false; + } + // If there is a drag in this drop put it back to origin. + var dragInDrop = drop.children(topNode + ' span.placed'); + if (dragInDrop.length) { + //t.putBackToOrigin(dragInDrop); + return dragInDrop; + } + return false; + }, + + isDragOutsideQuestionContainer : function (container, x, y) { + var containerEdges = { + 'lt': { 'Y': container.offset().top + t.CONTAINER_BORDER_TOLERANCE, + 'X': container.offset().left + t.CONTAINER_BORDER_TOLERANCE}, + 'rt': { 'Y' : container.offset().top + t.CONTAINER_BORDER_TOLERANCE, + 'X' : container.offset().left + container[0].clientWidth - t.CONTAINER_BORDER_TOLERANCE}, + 'lb': { 'Y' : container.offset().top + container[0].clientHeight - t.CONTAINER_BORDER_TOLERANCE, + 'X' : container.offset().left}, + 'rb': { 'Y' : container.offset().top + container[0].clientHeight - t.CONTAINER_BORDER_TOLERANCE, + 'X' : container.offset().left + container[0].clientWidth - t.CONTAINER_BORDER_TOLERANCE} + }; + + // Check boundaries (I know that these ifs could be simplified as one if, bit i think this is more readable). + if (y < containerEdges.lt.Y || x < containerEdges.lt.X) { + $(container).addClass('outside-container'); + return true; + } + if (y < containerEdges.rt.Y || x > containerEdges.rt.X) { + $(container).addClass('outside-container'); + return true; + } + if (y > containerEdges.lb.Y || x < containerEdges.lb.X) { + $(container).addClass('outside-container'); + return true; + } + if (y > containerEdges.rb.Y || x > containerEdges.rb.X) { + $(container).addClass('outside-container'); + return true; + } + $(container).removeClass('outside-container'); + return false; + }, + + /** + * Animate the object to the given destination. + * + * @param el object: the object to be animated + * @param duration int: duration on animation in miliseconds + * @param easing string: the easing type + * @param ctop css value of current top + * @param cleft css value of current left + * @param top css value of origin top + * @param left css value of origin left + */ + animateEl: function (dragBack, dragHome, duration, easing) { + var durationFactor = (Math.abs(dragBack.css('left') - dragHome.offset().left) + + Math.abs(dragBack.css('top') - dragHome.offset().top)); + durationFactor = (durationFactor > 1) ? durationFactor : 1; + + dragBack.animate( + { + opacity: 0.5, + width: (parseInt(dragBack.css('width')) * 1.2) + 'px', + height: (parseInt(dragBack.css('height')) * 1.2) + 'px', + top: dragHome.offset().top, + left: dragHome.offset().left + }, + { + duration: duration / durationFactor, + easing: easing + } + ); + dragBack.animate( + { + opacity: 1, + width: parseInt(dragBack.css('width')) +'px', + height: parseInt(dragBack.css('height')) + 'px', + top: dragHome.offset().top, + left: dragHome.offset().left + }, + { + duration: duration / durationFactor, + easing: easing + } + ); + }, + + /** + * isDragCloseToTarget checks whether the drag item is close enough to the target + * + * @param drag + * @param target + * @returns {boolean} + */ + isDragCloseToTarget: function (drag, target) { + var midDragX = drag.offset().left + drag.outerWidth() / 2; + var midDragY = drag.offset().top + drag.outerHeight() / 2; + var midTargetX = target.offset().left + target.outerWidth() / 2; + var midTargetY = target.offset().top + target.outerHeight() / 2; + // Is the drag item close to target? + if ((midDragX > (midTargetX - t.DRAG_PROXIMITY) && + midDragX < (midTargetX + t.DRAG_PROXIMITY)) && + (midDragY > (midTargetY - t.DRAG_PROXIMITY) && + midDragY < (midTargetY + t.DRAG_PROXIMITY))) { + return true; + } + return false; + }, + + /** + * Set padding to all drags and drops. + */ + setPaddingSizesAll: function (topNode) { + for (var groupno = 1; groupno <= 8; groupno++) { + t.setPaddingSizeForGroup(topNode, groupno); + } + }, + + /** + * Set padding to drags and drops in a group. + * @param groupNo + */ + setPaddingSizeForGroup: function (topNode, groupNo) { + var groupItems = $(t.cssSelectors(topNode).dragHomesGroup(groupNo)); + if (groupItems.length != 0) { + var maxWidth = 0; + var maxHeight = 0; + // Find max height and width + $(groupItems).each(function (item, groupItem) { + maxWidth = Math.max(maxWidth, Math.ceil(groupItem.offsetWidth)); + maxHeight = Math.max(maxHeight, Math.ceil(groupItem.offsetHeight)); + }); + maxWidth += 8; + maxHeight += 2; + + $(groupItems).each(function (item, dragItem) { + t.padWidthHeight(dragItem, maxWidth, maxHeight); + + t.positionDrags(topNode, groupNo, dragItem); + }); + $(t.cssSelectors(topNode).dropsGroup(groupNo)).each(function (item, dropItem) { + t.padWidthHeight(dropItem, maxWidth + 2, maxHeight + 2); + }); + } + }, + + /** + * + * + * @param topNode + * @param groupNo + * @param dragItem + */ + positionDrags: function (topNode, groupNo, dragItem) { + var dragsingroup = $(t.cssSelectors(topNode).dragsInGroup(groupNo)); + $(t.cssSelectors(topNode).dragContainer()).append($(dragsingroup).append($(dragItem))); + }, + + padWidthHeight: function (node, width, height) { + $(node).css( + { + 'width': width + 'px', + 'height': height + 'px', + 'lineHeight': height + 'px' + } + ); + }, + + /** + * Return the number at the end of the prefix. + * @param node + * @param prefix + * @returns {*|number} + */ + getClassnameNumericSuffix: function (node, prefix) { + var classes = node.attr('class'); + if (classes !== '') { + var classesArr = classes.split(' '); + for (var index = 0; index < classesArr.length; index++) { + var patt1 = new RegExp('^' + prefix + '([0-9])+$'); + if (patt1.test(classesArr[index])) { + var patt2 = new RegExp('([0-9])+$'); + var match = patt2.exec(classesArr[index]); + return Number(match[0]); + } + } + } + throw 'Prefix "' + prefix + '" not found in class names.'; + }, + getChoice: function (node) { + return t.getClassnameNumericSuffix(node, 'choice'); + }, + getGroup: function (node) { + return t.getClassnameNumericSuffix(node, 'group'); + }, + getPlace: function (node) { + return t.getClassnameNumericSuffix(node, 'place'); + }, + getNo: function (node) { + return t.getClassnameNumericSuffix(node, 'no'); + }, + + /** + * Set drag's offset to the appropriate drop's offset. + * + * @param drag + * @param drop + */ + setDragOffset : function(drag, drop) { + drag.removeClass('unplaced'); + drag.addClass('placed'); + drag.offset( + { + 'top' : Math.round(drop.offset().top) + 1, + 'left' : Math.round(drop.offset().left) + 1 + } + ); + }, + + /** + * Check whether user drop the current drag is in the same drop + * @param drag + * @param drop + * @returns {boolean} + */ + isDragInTheSameDrop: function (drag, drop) { + if (parseInt(drag.css('top')) === Math.round(drop.offset().top) + 1) { + return true; + } + return false; + }, + + /** + * Remove drag from drop. + * @param drop + */ + removeDragFromDrop: function (topNode, drop) { + t.putDragInDrop(topNode, null, drop); + }, + + /** + * Tab through drops and place drag in drop or remove drag from drop using keybord. + * @param e + */ + dropZoneKeyPress: function(topNode, e) { + var keys = { + '27': 'remove', // Escape + '32': 'next', // Space + '37': 'previous', // Left arrow + '38': 'previous', // Up arrow + '39': 'next', // Right arrow + '40': 'next' // Down arrow + }; + e.direction = keys[e.keyCode]; + //e.preventDefault(); + switch (e.direction) { + case 'next' : + e.preventDefault(); + t.placeNextDragIn(topNode, $(e.target)); + break; + case 'previous' : + e.preventDefault(); + t.placePreviousDragIn(topNode, $(e.target)); + break; + case 'remove' : + e.preventDefault(); + t.removeDragFromDrop(topNode, $(e.target)); + break; + } + }, + + /** + * Place next drag in drop. + * @param drop + */ + placeNextDragIn: function(topNode, drop) { + t.chooseNextChoiceForDrop(topNode, drop, 1); + }, + + /** + * Place previous drag in drop. + * @param drop + */ + placePreviousDragIn: function(topNode, drop) { + t.chooseNextChoiceForDrop(topNode, drop, -1); + }, + + /** + * Choose the next drag to put in drop. + * @param drop + * @param direction + */ + chooseNextChoiceForDrop: function(topNode, drop, direction) { + var groupNo = t.getGroup(drop); + var current = t.currentChoiceInDrop(topNode, $(drop)); + var unplacedDragsInGroup = $(t.cssSelectors(topNode).unplacedDragsInGroup(groupNo)); + var next = current; + var arrayOfDrags = unplacedDragsInGroup.toArray(); + if (0 === current) { + if (direction === 1) { + next = t.getChoice($(arrayOfDrags.shift())); + } else { + next = t.getChoice($(arrayOfDrags.pop())); + } + } else { + next = current + direction; + } + var drag = $(t.cssSelectors(topNode).unplacedDragsForChoiceInGroup(next, groupNo)); + if (drag.length === 0) { + drag = $(t.cssSelectors(topNode).unplacedDragsForChoiceInGroup(next + direction, groupNo)); + } + if ($(t.cssSelectors(topNode).dragsForChoiceInGroup(next, groupNo)) === null) { + t.removeDragFromDrop(topNode, drop); + return; + } else { + if (drag.hasClass('infinite')) { + var bOut = false; // Break out of each loop boolean variable. + drag.each(function (index, draginf) { + if ($(draginf).hasClass('unplaced')) { + t.putDragInDrop(topNode, $(draginf), drop); + bOut = true; + return false; + } + }); + if (bOut) { + bOut = false; + return false; + } + } else { + t.putDragInDrop(topNode, drag, drop); + } + } + }, + + /** + * Return curent drag choice in drop. + * + * @param drop + * @returns {*|number} + */ + currentChoiceInDrop: function(topNode, drop) { + var inputNode = $('input#' + t.getInputId(topNode, t.getPlace(drop))); + return Number($(inputNode).val()); + } + }; + return t; +}); diff --git a/question/type/ddwtos/renderer.php b/question/type/ddwtos/renderer.php index 7425082df2a..b59e1e41266 100644 --- a/question/type/ddwtos/renderer.php +++ b/question/type/ddwtos/renderer.php @@ -51,16 +51,12 @@ class qtype_ddwtos_renderer extends qtype_elements_embedded_in_question_text_ren foreach ($question->places as $placeno => $place) { $inputids[$placeno] = $this->box_id($qa, $question->field($placeno)); } - $params = array( 'inputids' => $inputids, 'topnode' => 'div.que.ddwtos#q' . $qa->get_slot(), 'readonly' => $options->readonly ); - - $PAGE->requires->yui_module('moodle-qtype_ddwtos-dd', - 'M.qtype_ddwtos.init_question', array($params)); - + $PAGE->requires->js_call_amd('qtype_ddwtos/ddwtos', 'init', array($params)); return $result; } diff --git a/question/type/ddwtos/styles.css b/question/type/ddwtos/styles.css index aa7b8ab3732..201f6c9a224 100644 --- a/question/type/ddwtos/styles.css +++ b/question/type/ddwtos/styles.css @@ -45,10 +45,14 @@ } .que.ddwtos .drop:focus, -.que.ddwtos .drop.yui3-dd-drop-over.yui3-dd-drop-active-valid { +.que.ddwtos .drop.valid-drag-over-drop { border-color: #0a0; box-shadow: 0 0 5px 5px rgba(255, 255, 150, 1); } +.que.ddwtos .outside-container { + border-color: #FFA500; + box-shadow: 0 0 5px 5px rgba(255, 200, 50, 1); +} .que.ddwtos .notreadonly .drag { cursor: move; diff --git a/question/type/ddwtos/yui/build/moodle-qtype_ddwtos-dd/moodle-qtype_ddwtos-dd-debug.js b/question/type/ddwtos/yui/build/moodle-qtype_ddwtos-dd/moodle-qtype_ddwtos-dd-debug.js deleted file mode 100644 index eaad12d19a3..00000000000 --- a/question/type/ddwtos/yui/build/moodle-qtype_ddwtos-dd/moodle-qtype_ddwtos-dd-debug.js +++ /dev/null @@ -1,445 +0,0 @@ -YUI.add('moodle-qtype_ddwtos-dd', function (Y, NAME) { - -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * JavaScript code for the ddwtos question type. - * - * @package qtype - * @subpackage ddwtos - * @copyright 2011 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -var DDWTOSDDNAME = 'ddwtos_dd'; -var DDWTOS_DD = function() { - DDWTOS_DD.superclass.constructor.apply(this, arguments); -}; -/** - * This is the class for ddwtos question rendering. - * A DDWTOS_DD class is created for each question. - */ -Y.extend(DDWTOS_DD, Y.Base, { - selectors: null, - passiveSupported: false, - initializer: function() { - var pendingid = 'qtype_ddwtos-' + Math.random().toString(36).slice(2); // Random string. - M.util.js_pending(pendingid); - this.selectors = this.css_selectors(this.get('topnode')); - this.set_padding_sizes_all(); - this.clone_drag_items(); - this.initial_place_of_drag_items(); - this.make_drop_zones(); - if (!this.get('readonly')) { - Y.later(500, this, this.position_drag_items, [pendingid, true]); - } else { - Y.later(500, this, this.position_drag_items, [pendingid, 3]); - Y.one('window').on('resize', function() { - this.position_drag_items(pendingid); - }, this); - } - this.checkPassiveSupported(); - }, - /** - * put all our selectors in the same place so we can quickly find and change them later - * if the structure of the document changes. - */ - css_selectors: function(topnode) { - return { - top_node: function() { - return topnode; - }, - drag_container: function() { - return topnode + ' div.drags'; - }, - drags: function() { - return this.drag_container() + ' span.drag'; - }, - drag: function(no) { - return this.drags() + '.no' + no; - }, - drags_in_group: function(groupno) { - return this.drags() + '.group' + groupno; - }, - unplaced_drags_in_group: function(groupno) { - return this.drags_in_group(groupno) + '.unplaced'; - }, - drags_for_choice_in_group: function(choiceno, groupno) { - return this.drags_in_group(groupno) + '.choice' + choiceno; - }, - unplaced_drags_for_choice_in_group: function(choiceno, groupno) { - return this.unplaced_drags_in_group(groupno) + '.choice' + choiceno; - }, - drops: function() { - return topnode + ' span.drop'; - }, - drop_for_place: function(placeno) { - return this.drops() + '.place' + placeno; - }, - drops_in_group: function(groupno) { - return this.drops() + '.group' + groupno; - }, - drag_homes: function() { - return topnode + ' span.draghome'; - }, - drag_homes_group: function(groupno) { - return topnode + ' .draggrouphomes' + groupno + ' span.draghome'; - }, - drag_home: function(groupno, choiceno) { - return topnode + ' .draggrouphomes' + groupno + ' span.draghome.choice' + choiceno; - }, - drops_group: function(groupno) { - return topnode + ' span.drop.group' + groupno; - } - }; - }, - set_padding_sizes_all: function() { - for (var groupno = 1; groupno <= 8; groupno++) { - this.set_padding_size_for_group(groupno); - } - }, - set_padding_size_for_group: function(groupno) { - var groupitems = Y.all(this.selectors.drag_homes_group(groupno)); - if (groupitems.size() !== 0) { - var maxwidth = 0; - var maxheight = 0; - // find max height and width - groupitems.each(function(item) { - maxwidth = Math.max(maxwidth, Math.ceil(item.get('offsetWidth'))); - maxheight = Math.max(maxheight, Math.ceil(item.get('offsetHeight'))); - }, this); - maxwidth += 8; - maxheight += 2; - groupitems.each(function(item) { - this.pad_to_width_height(item, maxwidth, maxheight); - }, this); - Y.all(this.selectors.drops_group(groupno)).each(function(item) { - this.pad_to_width_height(item, maxwidth + 2, maxheight + 2); - }, this); - } - }, - pad_to_width_height: function(node, width, height) { - node.setStyle('width', width + 'px').setStyle('height', height + 'px') - .setStyle('lineHeight', height + 'px'); - }, - - /** - * Invisible 'drag homes' are output by the renderer. These have the same properties - * as the drag items but are invisible. We clone these invisible elements to make the - * actual drag items. - */ - clone_drag_items: function() { - Y.all(this.selectors.drag_homes()).each(this.clone_drag_items_for_one_choice, this); - }, - clone_drag_items_for_one_choice: function(draghome) { - if (draghome.hasClass('infinite')) { - var groupno = this.get_group(draghome); - var noofdrags = Y.all(this.selectors.drops_in_group(groupno)).size(); - for (var i = 0; i < noofdrags; i++) { - this.clone_drag_item(draghome); - } - } else { - this.clone_drag_item(draghome); - } - }, - nextdragitemno: 1, - clone_drag_item: function(draghome) { - var drag = draghome.cloneNode(true); - drag.removeClass('draghome'); - drag.addClass('drag'); - drag.addClass('no' + this.nextdragitemno); - this.nextdragitemno++; - drag.setStyles({'visibility': 'visible', 'position': 'absolute'}); - Y.one(this.selectors.drag_container()).appendChild(drag); - if (!this.get('readonly')) { - this.make_draggable(drag); - } - }, - get_classname_numeric_suffix: function(node, prefix) { - var classes = node.getAttribute('class'); - if (classes !== '') { - var classesarr = classes.split(' '); - for (var index = 0; index < classesarr.length; index++) { - var patt1 = new RegExp('^' + prefix + '([0-9])+$'); - if (patt1.test(classesarr[index])) { - var patt2 = new RegExp('([0-9])+$'); - var match = patt2.exec(classesarr[index]); - return Number(match[0]); - } - } - } - throw 'Prefix "' + prefix + '" not found in class names.'; - }, - get_choice: function(node) { - return this.get_classname_numeric_suffix(node, 'choice'); - }, - get_group: function(node) { - return this.get_classname_numeric_suffix(node, 'group'); - }, - get_place: function(node) { - return this.get_classname_numeric_suffix(node, 'place'); - }, - get_no: function(node) { - return this.get_classname_numeric_suffix(node, 'no'); - }, - placed: null, - initial_place_of_drag_items: function() { - Y.all(this.selectors.drags()).addClass('unplaced'); - this.placed = []; - for (var placeno in this.get('inputids')) { - var inputid = this.get('inputids')[placeno]; - var inputnode = Y.one('input#' + inputid); - var choiceno = Number(inputnode.get('value')); - if (choiceno !== 0) { - var drop = Y.one(this.selectors.drop_for_place(placeno)); - var groupno = this.get_group(drop); - var drag = - Y.one(this.selectors.unplaced_drags_for_choice_in_group(choiceno, groupno)); - this.place_drag_in_drop(drag, drop); - this.position_drag_item(drag); - } - } - }, - make_draggable: function(drag) { - new Y.DD.Drag({ - node: drag, - groups: [this.get_group(drag)], - dragMode: 'point' - }).plug(Y.Plugin.DDConstrained, {constrain2node: this.selectors.top_node()}); - - // Prevent scrolling whilst dragging on Adroid devices. - this.prevent_touchmove_from_scrolling(drag); - }, - - /** - * prevent_touchmove_from_scrolling allows users of touch screen devices to - * use drag and drop and normal scrolling at the same time. I.e. when - * touching and dragging a draggable item, the screen does not scroll, but - * you can scroll by touching other area of the screen apart from the - * draggable items. - */ - prevent_touchmove_from_scrolling: function(drag) { - var touchmove = (Y.UA.ie) ? 'MSPointerMove' : 'touchmove'; - var eventHandler = function(event) { - event.preventDefault(); - }; - var dragId = drag.get('id'); - var el = document.getElementById(dragId); - // Note do not dynamically add events within another event, as this causes issues on iOS11.3. - // See https://github.com/atlassian/react-beautiful-dnd/issues/413 and - // https://bugs.webkit.org/show_bug.cgi?id=184250 for fuller explanation. - el.addEventListener(touchmove, eventHandler, this.passiveSupported ? {passive: false, capture: true} : false); - }, - - /** - * Some older browsers do not support passing an options object to addEventListener. - * This is a check from https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener. - */ - checkPassiveSupported: function() { - try { - var options = Object.defineProperty({}, 'passive', { - get: function() { - this.passiveSupported = true; - }.bind(this) - }); - window.addEventListener('test', options, options); - window.removeEventListener('test', options, options); - } catch (err) { - this.passiveSupported = false; - } - }, - - make_drop_zones: function() { - Y.all(this.selectors.drops()).each(this.make_drop_zone, this); - }, - make_drop_zone: function(drop) { - var dropdd = new Y.DD.Drop({ - node: drop, - groups: [this.get_group(drop)]}); - dropdd.on('drop:hit', function(e) { - var drag = e.drag.get('node'); - var drop = e.drop.get('node'); - if (this.get_group(drop) === this.get_group(drag)) { - this.place_drag_in_drop(drag, drop); - } - }, this); - if (!this.get('readonly')) { - drop.on('dragchange', this.drop_zone_key_press, this); - } - }, - place_drag_in_drop: function(drag, drop) { - var placeno = this.get_place(drop); - var inputid = this.get('inputids')[placeno]; - var inputnode = Y.one('input#' + inputid); - if (drag !== null) { - inputnode.set('value', this.get_choice(drag)); - } else { - inputnode.set('value', '0'); - } - for (var alreadytheredragno in this.placed) { - if (this.placed[alreadytheredragno] === placeno) { - delete this.placed[alreadytheredragno]; - var alreadytheredrag = Y.one(this.selectors.drag(alreadytheredragno)); - if (alreadytheredrag && alreadytheredrag.dd) { - alreadytheredrag.dd.detach('drag:start'); - } - } - } - if (drag !== null) { - this.placed[this.get_no(drag)] = placeno; - if (drag.dd) { - drag.dd.once('drag:start', function(e, inputnode, drag) { - inputnode.set('value', 0); - delete this.placed[this.get_no(drag)]; - drag.addClass('unplaced'); - }, this, inputnode, drag); - } - } - }, - remove_drag_from_drop: function(drop) { - this.place_drag_in_drop(null, drop); - }, - - /** - * Postition, or reposition, all the drag items. - * @param pendingid (optional) if given, then mark the js task complete after the - * items are all positioned. - * @param dotimeout (optional) if true, continually re-position the items so - * they stay in place. Else, if an integer, reposition this many times before stopping. - */ - position_drag_items: function(pendingid, dotimeout) { - Y.all(this.selectors.drags()).each(this.position_drag_item, this); - M.util.js_complete(pendingid); - if (dotimeout === true || dotimeout > 0) { - if (dotimeout !== true) { - dotimeout -= 1; - } - Y.later(500, this, this.position_drag_items, [pendingid, dotimeout]); - } - }, - position_drag_item: function(drag) { - if (!drag.hasClass('yui3-dd-dragging')) { - if (!this.placed[this.get_no(drag)]) { - var groupno = this.get_group(drag); - var choiceno = this.get_choice(drag); - var home = Y.one(this.selectors.drag_home(groupno, choiceno)); - drag.setXY(home.getXY()); - drag.addClass('unplaced'); - } else { - var placeno = this.placed[this.get_no(drag)]; - var drop = Y.one(this.selectors.drop_for_place(placeno)); - drag.setXY([drop.getX() + 2, drop.getY() + 2]); - drag.removeClass('unplaced'); - } - } - }, - - drop_zone_key_press: function(e) { - switch (e.direction) { - case 'next' : - this.place_next_drag_in(e.target); - break; - case 'previous' : - this.place_previous_drag_in(e.target); - break; - case 'remove' : - this.remove_drag_from_drop(e.target); - break; - } - e.preventDefault(); - }, - place_next_drag_in: function(drop) { - this.choose_next_choice_for_drop(drop, 1); - }, - place_previous_drag_in: function(drop) { - this.choose_next_choice_for_drop(drop, -1); - }, - choose_next_choice_for_drop: function(drop, direction) { - var next; - var groupno = this.get_group(drop); - var current = this.current_choice_in_drop(drop); - var unplaceddragsingroup = Y.all(this.selectors.unplaced_drags_in_group(groupno)); - if (0 === current) { - if (direction === 1) { - next = 1; - } else { - var lastdrag = unplaceddragsingroup.pop(); - next = this.get_choice(lastdrag); - } - } else { - next = current + direction; - } - var drag; - do { - drag = Y.one(this.selectors.unplaced_drags_for_choice_in_group(next, groupno)); - if (Y.one(this.selectors.drags_for_choice_in_group(next, groupno)) === null) { - this.remove_drag_from_drop(drop); - return; - } - next = next + direction; - } while (drag === null); - this.place_drag_in_drop(drag, drop); - }, - current_choice_in_drop: function(drop) { - var inputid = this.get('inputids')[this.get_place(drop)]; - var inputnode = Y.one('input#' + inputid); - return Number(inputnode.get('value')); - } -}, { - NAME: DDWTOSDDNAME, - ATTRS: { - readonly: {value: false}, - topnode: {value: null}, - inputids: {value: null} - } -}); - -Y.Event.define('dragchange', { - // Webkit and IE repeat keydown when you hold down arrow keys. - // Opera links keypress to page scroll; others keydown. - // Firefox prevents page scroll via preventDefault() on either - // keydown or keypress. - _event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress', - - _keys: { - '32': 'next', // Space - '37': 'previous', // Left arrow - '38': 'previous', // Up arrow - '39': 'next', // Right arrow - '40': 'next', // Down arrow - '27': 'remove' // Escape - }, - - _keyHandler: function(e, notifier) { - if (this._keys[e.keyCode]) { - e.direction = this._keys[e.keyCode]; - notifier.fire(e); - } - }, - - on: function(node, sub, notifier) { - sub._detacher = node.on(this._event, this._keyHandler, - this, notifier); - } -}); - -M.qtype_ddwtos = M.qtype_ddwtos || {}; -M.qtype_ddwtos.init_question = function(config) { - return new DDWTOS_DD(config); -}; - - -}, '@VERSION@', {"requires": ["node", "dd", "dd-drop", "dd-constrain"]}); diff --git a/question/type/ddwtos/yui/build/moodle-qtype_ddwtos-dd/moodle-qtype_ddwtos-dd-min.js b/question/type/ddwtos/yui/build/moodle-qtype_ddwtos-dd/moodle-qtype_ddwtos-dd-min.js deleted file mode 100644 index ff5bf8c1fde..00000000000 --- a/question/type/ddwtos/yui/build/moodle-qtype_ddwtos-dd/moodle-qtype_ddwtos-dd-min.js +++ /dev/null @@ -1,2 +0,0 @@ -YUI.add("moodle-qtype_ddwtos-dd",function(e,t){var n="ddwtos_dd",r=function(){r.superclass.constructor.apply(this,arguments)};e.extend(r,e.Base,{selectors:null,passiveSupported:!1,initializer:function(){var t="qtype_ddwtos-"+Math.random().toString(36).slice(2);M.util.js_pending(t),this.selectors=this.css_selectors(this.get("topnode")),this.set_padding_sizes_all(),this.clone_drag_items(),this.initial_place_of_drag_items(),this.make_drop_zones(),this.get("readonly")?(e.later(500,this,this.position_drag_items,[t,3]),e.one("window").on("resize",function(){this.position_drag_items(t)},this)):e.later(500,this,this.position_drag_items,[t,!0]),this.checkPassiveSupported()},css_selectors:function(e){return{top_node:function(){return e},drag_container:function(){return e+" div.drags"},drags:function(){return this.drag_container()+" span.drag"},drag:function(e){return this.drags()+".no"+e},drags_in_group:function(e){return this.drags()+".group"+e},unplaced_drags_in_group:function(e){return this.drags_in_group(e)+".unplaced"},drags_for_choice_in_group:function(e,t){return this.drags_in_group(t)+".choice"+e},unplaced_drags_for_choice_in_group:function(e,t){return this.unplaced_drags_in_group(t)+".choice"+e},drops:function(){return e+" span.drop"},drop_for_place:function(e){return this.drops()+".place"+e},drops_in_group:function(e){return this.drops()+".group"+e},drag_homes:function(){return e+" span.draghome"},drag_homes_group:function(t){return e+" .draggrouphomes"+t+" span.draghome"},drag_home:function(t,n){return e+" .draggrouphomes"+t+" span.draghome.choice"+n},drops_group:function(t){return e+" span.drop.group"+t}}},set_padding_sizes_all:function(){for(var e=1;e<=8;e++)this.set_padding_size_for_group(e)},set_padding_size_for_group:function(t){var n=e.all(this.selectors.drag_homes_group(t));if(n.size()!==0){var r=0,i=0;n.each(function(e){r=Math.max(r,Math.ceil(e.get("offsetWidth"))),i=Math.max(i,Math.ceil(e.get("offsetHeight")))},this),r+=8,i+=2,n.each(function(e){this.pad_to_width_height(e,r,i)},this),e.all(this.selectors.drops_group(t)).each(function(e){this.pad_to_width_height(e,r+2,i+2)},this)}},pad_to_width_height:function(e,t,n){e.setStyle("width",t+"px").setStyle("height",n+"px").setStyle("lineHeight",n+"px")},clone_drag_items:function(){e.all(this.selectors.drag_homes()).each(this.clone_drag_items_for_one_choice,this)},clone_drag_items_for_one_choice:function(t){if(t.hasClass("infinite")){var n=this.get_group(t),r=e.all(this.selectors.drops_in_group(n)).size();for(var i=0;i0)n!==!0&&(n-=1),e.later(500,this,this.position_drag_items,[t,n])},position_drag_item:function(t){if(!t.hasClass("yui3-dd-dragging"))if(!this.placed[this.get_no(t)]){var n=this.get_group(t),r=this.get_choice(t),i=e.one(this.selectors.drag_home(n,r));t.setXY(i.getXY()),t.addClass("unplaced")}else{var s=this -.placed[this.get_no(t)],o=e.one(this.selectors.drop_for_place(s));t.setXY([o.getX()+2,o.getY()+2]),t.removeClass("unplaced")}},drop_zone_key_press:function(e){switch(e.direction){case"next":this.place_next_drag_in(e.target);break;case"previous":this.place_previous_drag_in(e.target);break;case"remove":this.remove_drag_from_drop(e.target)}e.preventDefault()},place_next_drag_in:function(e){this.choose_next_choice_for_drop(e,1)},place_previous_drag_in:function(e){this.choose_next_choice_for_drop(e,-1)},choose_next_choice_for_drop:function(t,n){var r,i=this.get_group(t),s=this.current_choice_in_drop(t),o=e.all(this.selectors.unplaced_drags_in_group(i));if(0===s)if(n===1)r=1;else{var u=o.pop();r=this.get_choice(u)}else r=s+n;var a;do{a=e.one(this.selectors.unplaced_drags_for_choice_in_group(r,i));if(e.one(this.selectors.drags_for_choice_in_group(r,i))===null){this.remove_drag_from_drop(t);return}r+=n}while(a===null);this.place_drag_in_drop(a,t)},current_choice_in_drop:function(t){var n=this.get("inputids")[this.get_place(t)],r=e.one("input#"+n);return Number(r.get("value"))}},{NAME:n,ATTRS:{readonly:{value:!1},topnode:{value:null},inputids:{value:null}}}),e.Event.define("dragchange",{_event:e.UA.webkit||e.UA.ie?"keydown":"keypress",_keys:{32:"next",37:"previous",38:"previous",39:"next",40:"next",27:"remove"},_keyHandler:function(e,t){this._keys[e.keyCode]&&(e.direction=this._keys[e.keyCode],t.fire(e))},on:function(e,t,n){t._detacher=e.on(this._event,this._keyHandler,this,n)}}),M.qtype_ddwtos=M.qtype_ddwtos||{},M.qtype_ddwtos.init_question=function(e){return new r(e)}},"@VERSION@",{requires:["node","dd","dd-drop","dd-constrain"]}); diff --git a/question/type/ddwtos/yui/build/moodle-qtype_ddwtos-dd/moodle-qtype_ddwtos-dd.js b/question/type/ddwtos/yui/build/moodle-qtype_ddwtos-dd/moodle-qtype_ddwtos-dd.js deleted file mode 100644 index eaad12d19a3..00000000000 --- a/question/type/ddwtos/yui/build/moodle-qtype_ddwtos-dd/moodle-qtype_ddwtos-dd.js +++ /dev/null @@ -1,445 +0,0 @@ -YUI.add('moodle-qtype_ddwtos-dd', function (Y, NAME) { - -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * JavaScript code for the ddwtos question type. - * - * @package qtype - * @subpackage ddwtos - * @copyright 2011 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -var DDWTOSDDNAME = 'ddwtos_dd'; -var DDWTOS_DD = function() { - DDWTOS_DD.superclass.constructor.apply(this, arguments); -}; -/** - * This is the class for ddwtos question rendering. - * A DDWTOS_DD class is created for each question. - */ -Y.extend(DDWTOS_DD, Y.Base, { - selectors: null, - passiveSupported: false, - initializer: function() { - var pendingid = 'qtype_ddwtos-' + Math.random().toString(36).slice(2); // Random string. - M.util.js_pending(pendingid); - this.selectors = this.css_selectors(this.get('topnode')); - this.set_padding_sizes_all(); - this.clone_drag_items(); - this.initial_place_of_drag_items(); - this.make_drop_zones(); - if (!this.get('readonly')) { - Y.later(500, this, this.position_drag_items, [pendingid, true]); - } else { - Y.later(500, this, this.position_drag_items, [pendingid, 3]); - Y.one('window').on('resize', function() { - this.position_drag_items(pendingid); - }, this); - } - this.checkPassiveSupported(); - }, - /** - * put all our selectors in the same place so we can quickly find and change them later - * if the structure of the document changes. - */ - css_selectors: function(topnode) { - return { - top_node: function() { - return topnode; - }, - drag_container: function() { - return topnode + ' div.drags'; - }, - drags: function() { - return this.drag_container() + ' span.drag'; - }, - drag: function(no) { - return this.drags() + '.no' + no; - }, - drags_in_group: function(groupno) { - return this.drags() + '.group' + groupno; - }, - unplaced_drags_in_group: function(groupno) { - return this.drags_in_group(groupno) + '.unplaced'; - }, - drags_for_choice_in_group: function(choiceno, groupno) { - return this.drags_in_group(groupno) + '.choice' + choiceno; - }, - unplaced_drags_for_choice_in_group: function(choiceno, groupno) { - return this.unplaced_drags_in_group(groupno) + '.choice' + choiceno; - }, - drops: function() { - return topnode + ' span.drop'; - }, - drop_for_place: function(placeno) { - return this.drops() + '.place' + placeno; - }, - drops_in_group: function(groupno) { - return this.drops() + '.group' + groupno; - }, - drag_homes: function() { - return topnode + ' span.draghome'; - }, - drag_homes_group: function(groupno) { - return topnode + ' .draggrouphomes' + groupno + ' span.draghome'; - }, - drag_home: function(groupno, choiceno) { - return topnode + ' .draggrouphomes' + groupno + ' span.draghome.choice' + choiceno; - }, - drops_group: function(groupno) { - return topnode + ' span.drop.group' + groupno; - } - }; - }, - set_padding_sizes_all: function() { - for (var groupno = 1; groupno <= 8; groupno++) { - this.set_padding_size_for_group(groupno); - } - }, - set_padding_size_for_group: function(groupno) { - var groupitems = Y.all(this.selectors.drag_homes_group(groupno)); - if (groupitems.size() !== 0) { - var maxwidth = 0; - var maxheight = 0; - // find max height and width - groupitems.each(function(item) { - maxwidth = Math.max(maxwidth, Math.ceil(item.get('offsetWidth'))); - maxheight = Math.max(maxheight, Math.ceil(item.get('offsetHeight'))); - }, this); - maxwidth += 8; - maxheight += 2; - groupitems.each(function(item) { - this.pad_to_width_height(item, maxwidth, maxheight); - }, this); - Y.all(this.selectors.drops_group(groupno)).each(function(item) { - this.pad_to_width_height(item, maxwidth + 2, maxheight + 2); - }, this); - } - }, - pad_to_width_height: function(node, width, height) { - node.setStyle('width', width + 'px').setStyle('height', height + 'px') - .setStyle('lineHeight', height + 'px'); - }, - - /** - * Invisible 'drag homes' are output by the renderer. These have the same properties - * as the drag items but are invisible. We clone these invisible elements to make the - * actual drag items. - */ - clone_drag_items: function() { - Y.all(this.selectors.drag_homes()).each(this.clone_drag_items_for_one_choice, this); - }, - clone_drag_items_for_one_choice: function(draghome) { - if (draghome.hasClass('infinite')) { - var groupno = this.get_group(draghome); - var noofdrags = Y.all(this.selectors.drops_in_group(groupno)).size(); - for (var i = 0; i < noofdrags; i++) { - this.clone_drag_item(draghome); - } - } else { - this.clone_drag_item(draghome); - } - }, - nextdragitemno: 1, - clone_drag_item: function(draghome) { - var drag = draghome.cloneNode(true); - drag.removeClass('draghome'); - drag.addClass('drag'); - drag.addClass('no' + this.nextdragitemno); - this.nextdragitemno++; - drag.setStyles({'visibility': 'visible', 'position': 'absolute'}); - Y.one(this.selectors.drag_container()).appendChild(drag); - if (!this.get('readonly')) { - this.make_draggable(drag); - } - }, - get_classname_numeric_suffix: function(node, prefix) { - var classes = node.getAttribute('class'); - if (classes !== '') { - var classesarr = classes.split(' '); - for (var index = 0; index < classesarr.length; index++) { - var patt1 = new RegExp('^' + prefix + '([0-9])+$'); - if (patt1.test(classesarr[index])) { - var patt2 = new RegExp('([0-9])+$'); - var match = patt2.exec(classesarr[index]); - return Number(match[0]); - } - } - } - throw 'Prefix "' + prefix + '" not found in class names.'; - }, - get_choice: function(node) { - return this.get_classname_numeric_suffix(node, 'choice'); - }, - get_group: function(node) { - return this.get_classname_numeric_suffix(node, 'group'); - }, - get_place: function(node) { - return this.get_classname_numeric_suffix(node, 'place'); - }, - get_no: function(node) { - return this.get_classname_numeric_suffix(node, 'no'); - }, - placed: null, - initial_place_of_drag_items: function() { - Y.all(this.selectors.drags()).addClass('unplaced'); - this.placed = []; - for (var placeno in this.get('inputids')) { - var inputid = this.get('inputids')[placeno]; - var inputnode = Y.one('input#' + inputid); - var choiceno = Number(inputnode.get('value')); - if (choiceno !== 0) { - var drop = Y.one(this.selectors.drop_for_place(placeno)); - var groupno = this.get_group(drop); - var drag = - Y.one(this.selectors.unplaced_drags_for_choice_in_group(choiceno, groupno)); - this.place_drag_in_drop(drag, drop); - this.position_drag_item(drag); - } - } - }, - make_draggable: function(drag) { - new Y.DD.Drag({ - node: drag, - groups: [this.get_group(drag)], - dragMode: 'point' - }).plug(Y.Plugin.DDConstrained, {constrain2node: this.selectors.top_node()}); - - // Prevent scrolling whilst dragging on Adroid devices. - this.prevent_touchmove_from_scrolling(drag); - }, - - /** - * prevent_touchmove_from_scrolling allows users of touch screen devices to - * use drag and drop and normal scrolling at the same time. I.e. when - * touching and dragging a draggable item, the screen does not scroll, but - * you can scroll by touching other area of the screen apart from the - * draggable items. - */ - prevent_touchmove_from_scrolling: function(drag) { - var touchmove = (Y.UA.ie) ? 'MSPointerMove' : 'touchmove'; - var eventHandler = function(event) { - event.preventDefault(); - }; - var dragId = drag.get('id'); - var el = document.getElementById(dragId); - // Note do not dynamically add events within another event, as this causes issues on iOS11.3. - // See https://github.com/atlassian/react-beautiful-dnd/issues/413 and - // https://bugs.webkit.org/show_bug.cgi?id=184250 for fuller explanation. - el.addEventListener(touchmove, eventHandler, this.passiveSupported ? {passive: false, capture: true} : false); - }, - - /** - * Some older browsers do not support passing an options object to addEventListener. - * This is a check from https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener. - */ - checkPassiveSupported: function() { - try { - var options = Object.defineProperty({}, 'passive', { - get: function() { - this.passiveSupported = true; - }.bind(this) - }); - window.addEventListener('test', options, options); - window.removeEventListener('test', options, options); - } catch (err) { - this.passiveSupported = false; - } - }, - - make_drop_zones: function() { - Y.all(this.selectors.drops()).each(this.make_drop_zone, this); - }, - make_drop_zone: function(drop) { - var dropdd = new Y.DD.Drop({ - node: drop, - groups: [this.get_group(drop)]}); - dropdd.on('drop:hit', function(e) { - var drag = e.drag.get('node'); - var drop = e.drop.get('node'); - if (this.get_group(drop) === this.get_group(drag)) { - this.place_drag_in_drop(drag, drop); - } - }, this); - if (!this.get('readonly')) { - drop.on('dragchange', this.drop_zone_key_press, this); - } - }, - place_drag_in_drop: function(drag, drop) { - var placeno = this.get_place(drop); - var inputid = this.get('inputids')[placeno]; - var inputnode = Y.one('input#' + inputid); - if (drag !== null) { - inputnode.set('value', this.get_choice(drag)); - } else { - inputnode.set('value', '0'); - } - for (var alreadytheredragno in this.placed) { - if (this.placed[alreadytheredragno] === placeno) { - delete this.placed[alreadytheredragno]; - var alreadytheredrag = Y.one(this.selectors.drag(alreadytheredragno)); - if (alreadytheredrag && alreadytheredrag.dd) { - alreadytheredrag.dd.detach('drag:start'); - } - } - } - if (drag !== null) { - this.placed[this.get_no(drag)] = placeno; - if (drag.dd) { - drag.dd.once('drag:start', function(e, inputnode, drag) { - inputnode.set('value', 0); - delete this.placed[this.get_no(drag)]; - drag.addClass('unplaced'); - }, this, inputnode, drag); - } - } - }, - remove_drag_from_drop: function(drop) { - this.place_drag_in_drop(null, drop); - }, - - /** - * Postition, or reposition, all the drag items. - * @param pendingid (optional) if given, then mark the js task complete after the - * items are all positioned. - * @param dotimeout (optional) if true, continually re-position the items so - * they stay in place. Else, if an integer, reposition this many times before stopping. - */ - position_drag_items: function(pendingid, dotimeout) { - Y.all(this.selectors.drags()).each(this.position_drag_item, this); - M.util.js_complete(pendingid); - if (dotimeout === true || dotimeout > 0) { - if (dotimeout !== true) { - dotimeout -= 1; - } - Y.later(500, this, this.position_drag_items, [pendingid, dotimeout]); - } - }, - position_drag_item: function(drag) { - if (!drag.hasClass('yui3-dd-dragging')) { - if (!this.placed[this.get_no(drag)]) { - var groupno = this.get_group(drag); - var choiceno = this.get_choice(drag); - var home = Y.one(this.selectors.drag_home(groupno, choiceno)); - drag.setXY(home.getXY()); - drag.addClass('unplaced'); - } else { - var placeno = this.placed[this.get_no(drag)]; - var drop = Y.one(this.selectors.drop_for_place(placeno)); - drag.setXY([drop.getX() + 2, drop.getY() + 2]); - drag.removeClass('unplaced'); - } - } - }, - - drop_zone_key_press: function(e) { - switch (e.direction) { - case 'next' : - this.place_next_drag_in(e.target); - break; - case 'previous' : - this.place_previous_drag_in(e.target); - break; - case 'remove' : - this.remove_drag_from_drop(e.target); - break; - } - e.preventDefault(); - }, - place_next_drag_in: function(drop) { - this.choose_next_choice_for_drop(drop, 1); - }, - place_previous_drag_in: function(drop) { - this.choose_next_choice_for_drop(drop, -1); - }, - choose_next_choice_for_drop: function(drop, direction) { - var next; - var groupno = this.get_group(drop); - var current = this.current_choice_in_drop(drop); - var unplaceddragsingroup = Y.all(this.selectors.unplaced_drags_in_group(groupno)); - if (0 === current) { - if (direction === 1) { - next = 1; - } else { - var lastdrag = unplaceddragsingroup.pop(); - next = this.get_choice(lastdrag); - } - } else { - next = current + direction; - } - var drag; - do { - drag = Y.one(this.selectors.unplaced_drags_for_choice_in_group(next, groupno)); - if (Y.one(this.selectors.drags_for_choice_in_group(next, groupno)) === null) { - this.remove_drag_from_drop(drop); - return; - } - next = next + direction; - } while (drag === null); - this.place_drag_in_drop(drag, drop); - }, - current_choice_in_drop: function(drop) { - var inputid = this.get('inputids')[this.get_place(drop)]; - var inputnode = Y.one('input#' + inputid); - return Number(inputnode.get('value')); - } -}, { - NAME: DDWTOSDDNAME, - ATTRS: { - readonly: {value: false}, - topnode: {value: null}, - inputids: {value: null} - } -}); - -Y.Event.define('dragchange', { - // Webkit and IE repeat keydown when you hold down arrow keys. - // Opera links keypress to page scroll; others keydown. - // Firefox prevents page scroll via preventDefault() on either - // keydown or keypress. - _event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress', - - _keys: { - '32': 'next', // Space - '37': 'previous', // Left arrow - '38': 'previous', // Up arrow - '39': 'next', // Right arrow - '40': 'next', // Down arrow - '27': 'remove' // Escape - }, - - _keyHandler: function(e, notifier) { - if (this._keys[e.keyCode]) { - e.direction = this._keys[e.keyCode]; - notifier.fire(e); - } - }, - - on: function(node, sub, notifier) { - sub._detacher = node.on(this._event, this._keyHandler, - this, notifier); - } -}); - -M.qtype_ddwtos = M.qtype_ddwtos || {}; -M.qtype_ddwtos.init_question = function(config) { - return new DDWTOS_DD(config); -}; - - -}, '@VERSION@', {"requires": ["node", "dd", "dd-drop", "dd-constrain"]}); diff --git a/question/type/ddwtos/yui/src/ddwtos/build.json b/question/type/ddwtos/yui/src/ddwtos/build.json deleted file mode 100644 index d67dd9a123b..00000000000 --- a/question/type/ddwtos/yui/src/ddwtos/build.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "moodle-qtype_ddwtos-dd", - "builds": { - "moodle-qtype_ddwtos-dd": { - "jsfiles": [ - "ddwtos.js" - ] - } - } -} diff --git a/question/type/ddwtos/yui/src/ddwtos/js/ddwtos.js b/question/type/ddwtos/yui/src/ddwtos/js/ddwtos.js deleted file mode 100644 index 79fd922e000..00000000000 --- a/question/type/ddwtos/yui/src/ddwtos/js/ddwtos.js +++ /dev/null @@ -1,440 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * JavaScript code for the ddwtos question type. - * - * @package qtype - * @subpackage ddwtos - * @copyright 2011 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -var DDWTOSDDNAME = 'ddwtos_dd'; -var DDWTOS_DD = function() { - DDWTOS_DD.superclass.constructor.apply(this, arguments); -}; -/** - * This is the class for ddwtos question rendering. - * A DDWTOS_DD class is created for each question. - */ -Y.extend(DDWTOS_DD, Y.Base, { - selectors: null, - passiveSupported: false, - initializer: function() { - var pendingid = 'qtype_ddwtos-' + Math.random().toString(36).slice(2); // Random string. - M.util.js_pending(pendingid); - this.selectors = this.css_selectors(this.get('topnode')); - this.set_padding_sizes_all(); - this.clone_drag_items(); - this.initial_place_of_drag_items(); - this.make_drop_zones(); - if (!this.get('readonly')) { - Y.later(500, this, this.position_drag_items, [pendingid, true]); - } else { - Y.later(500, this, this.position_drag_items, [pendingid, 3]); - Y.one('window').on('resize', function() { - this.position_drag_items(pendingid); - }, this); - } - this.checkPassiveSupported(); - }, - /** - * put all our selectors in the same place so we can quickly find and change them later - * if the structure of the document changes. - */ - css_selectors: function(topnode) { - return { - top_node: function() { - return topnode; - }, - drag_container: function() { - return topnode + ' div.drags'; - }, - drags: function() { - return this.drag_container() + ' span.drag'; - }, - drag: function(no) { - return this.drags() + '.no' + no; - }, - drags_in_group: function(groupno) { - return this.drags() + '.group' + groupno; - }, - unplaced_drags_in_group: function(groupno) { - return this.drags_in_group(groupno) + '.unplaced'; - }, - drags_for_choice_in_group: function(choiceno, groupno) { - return this.drags_in_group(groupno) + '.choice' + choiceno; - }, - unplaced_drags_for_choice_in_group: function(choiceno, groupno) { - return this.unplaced_drags_in_group(groupno) + '.choice' + choiceno; - }, - drops: function() { - return topnode + ' span.drop'; - }, - drop_for_place: function(placeno) { - return this.drops() + '.place' + placeno; - }, - drops_in_group: function(groupno) { - return this.drops() + '.group' + groupno; - }, - drag_homes: function() { - return topnode + ' span.draghome'; - }, - drag_homes_group: function(groupno) { - return topnode + ' .draggrouphomes' + groupno + ' span.draghome'; - }, - drag_home: function(groupno, choiceno) { - return topnode + ' .draggrouphomes' + groupno + ' span.draghome.choice' + choiceno; - }, - drops_group: function(groupno) { - return topnode + ' span.drop.group' + groupno; - } - }; - }, - set_padding_sizes_all: function() { - for (var groupno = 1; groupno <= 8; groupno++) { - this.set_padding_size_for_group(groupno); - } - }, - set_padding_size_for_group: function(groupno) { - var groupitems = Y.all(this.selectors.drag_homes_group(groupno)); - if (groupitems.size() !== 0) { - var maxwidth = 0; - var maxheight = 0; - // find max height and width - groupitems.each(function(item) { - maxwidth = Math.max(maxwidth, Math.ceil(item.get('offsetWidth'))); - maxheight = Math.max(maxheight, Math.ceil(item.get('offsetHeight'))); - }, this); - maxwidth += 8; - maxheight += 2; - groupitems.each(function(item) { - this.pad_to_width_height(item, maxwidth, maxheight); - }, this); - Y.all(this.selectors.drops_group(groupno)).each(function(item) { - this.pad_to_width_height(item, maxwidth + 2, maxheight + 2); - }, this); - } - }, - pad_to_width_height: function(node, width, height) { - node.setStyle('width', width + 'px').setStyle('height', height + 'px') - .setStyle('lineHeight', height + 'px'); - }, - - /** - * Invisible 'drag homes' are output by the renderer. These have the same properties - * as the drag items but are invisible. We clone these invisible elements to make the - * actual drag items. - */ - clone_drag_items: function() { - Y.all(this.selectors.drag_homes()).each(this.clone_drag_items_for_one_choice, this); - }, - clone_drag_items_for_one_choice: function(draghome) { - if (draghome.hasClass('infinite')) { - var groupno = this.get_group(draghome); - var noofdrags = Y.all(this.selectors.drops_in_group(groupno)).size(); - for (var i = 0; i < noofdrags; i++) { - this.clone_drag_item(draghome); - } - } else { - this.clone_drag_item(draghome); - } - }, - nextdragitemno: 1, - clone_drag_item: function(draghome) { - var drag = draghome.cloneNode(true); - drag.removeClass('draghome'); - drag.addClass('drag'); - drag.addClass('no' + this.nextdragitemno); - this.nextdragitemno++; - drag.setStyles({'visibility': 'visible', 'position': 'absolute'}); - Y.one(this.selectors.drag_container()).appendChild(drag); - if (!this.get('readonly')) { - this.make_draggable(drag); - } - }, - get_classname_numeric_suffix: function(node, prefix) { - var classes = node.getAttribute('class'); - if (classes !== '') { - var classesarr = classes.split(' '); - for (var index = 0; index < classesarr.length; index++) { - var patt1 = new RegExp('^' + prefix + '([0-9])+$'); - if (patt1.test(classesarr[index])) { - var patt2 = new RegExp('([0-9])+$'); - var match = patt2.exec(classesarr[index]); - return Number(match[0]); - } - } - } - throw 'Prefix "' + prefix + '" not found in class names.'; - }, - get_choice: function(node) { - return this.get_classname_numeric_suffix(node, 'choice'); - }, - get_group: function(node) { - return this.get_classname_numeric_suffix(node, 'group'); - }, - get_place: function(node) { - return this.get_classname_numeric_suffix(node, 'place'); - }, - get_no: function(node) { - return this.get_classname_numeric_suffix(node, 'no'); - }, - placed: null, - initial_place_of_drag_items: function() { - Y.all(this.selectors.drags()).addClass('unplaced'); - this.placed = []; - for (var placeno in this.get('inputids')) { - var inputid = this.get('inputids')[placeno]; - var inputnode = Y.one('input#' + inputid); - var choiceno = Number(inputnode.get('value')); - if (choiceno !== 0) { - var drop = Y.one(this.selectors.drop_for_place(placeno)); - var groupno = this.get_group(drop); - var drag = - Y.one(this.selectors.unplaced_drags_for_choice_in_group(choiceno, groupno)); - this.place_drag_in_drop(drag, drop); - this.position_drag_item(drag); - } - } - }, - make_draggable: function(drag) { - new Y.DD.Drag({ - node: drag, - groups: [this.get_group(drag)], - dragMode: 'point' - }).plug(Y.Plugin.DDConstrained, {constrain2node: this.selectors.top_node()}); - - // Prevent scrolling whilst dragging on Adroid devices. - this.prevent_touchmove_from_scrolling(drag); - }, - - /** - * prevent_touchmove_from_scrolling allows users of touch screen devices to - * use drag and drop and normal scrolling at the same time. I.e. when - * touching and dragging a draggable item, the screen does not scroll, but - * you can scroll by touching other area of the screen apart from the - * draggable items. - */ - prevent_touchmove_from_scrolling: function(drag) { - var touchmove = (Y.UA.ie) ? 'MSPointerMove' : 'touchmove'; - var eventHandler = function(event) { - event.preventDefault(); - }; - var dragId = drag.get('id'); - var el = document.getElementById(dragId); - // Note do not dynamically add events within another event, as this causes issues on iOS11.3. - // See https://github.com/atlassian/react-beautiful-dnd/issues/413 and - // https://bugs.webkit.org/show_bug.cgi?id=184250 for fuller explanation. - el.addEventListener(touchmove, eventHandler, this.passiveSupported ? {passive: false, capture: true} : false); - }, - - /** - * Some older browsers do not support passing an options object to addEventListener. - * This is a check from https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener. - */ - checkPassiveSupported: function() { - try { - var options = Object.defineProperty({}, 'passive', { - get: function() { - this.passiveSupported = true; - }.bind(this) - }); - window.addEventListener('test', options, options); - window.removeEventListener('test', options, options); - } catch (err) { - this.passiveSupported = false; - } - }, - - make_drop_zones: function() { - Y.all(this.selectors.drops()).each(this.make_drop_zone, this); - }, - make_drop_zone: function(drop) { - var dropdd = new Y.DD.Drop({ - node: drop, - groups: [this.get_group(drop)]}); - dropdd.on('drop:hit', function(e) { - var drag = e.drag.get('node'); - var drop = e.drop.get('node'); - if (this.get_group(drop) === this.get_group(drag)) { - this.place_drag_in_drop(drag, drop); - } - }, this); - if (!this.get('readonly')) { - drop.on('dragchange', this.drop_zone_key_press, this); - } - }, - place_drag_in_drop: function(drag, drop) { - var placeno = this.get_place(drop); - var inputid = this.get('inputids')[placeno]; - var inputnode = Y.one('input#' + inputid); - if (drag !== null) { - inputnode.set('value', this.get_choice(drag)); - } else { - inputnode.set('value', '0'); - } - for (var alreadytheredragno in this.placed) { - if (this.placed[alreadytheredragno] === placeno) { - delete this.placed[alreadytheredragno]; - var alreadytheredrag = Y.one(this.selectors.drag(alreadytheredragno)); - if (alreadytheredrag && alreadytheredrag.dd) { - alreadytheredrag.dd.detach('drag:start'); - } - } - } - if (drag !== null) { - this.placed[this.get_no(drag)] = placeno; - if (drag.dd) { - drag.dd.once('drag:start', function(e, inputnode, drag) { - inputnode.set('value', 0); - delete this.placed[this.get_no(drag)]; - drag.addClass('unplaced'); - }, this, inputnode, drag); - } - } - }, - remove_drag_from_drop: function(drop) { - this.place_drag_in_drop(null, drop); - }, - - /** - * Postition, or reposition, all the drag items. - * @param pendingid (optional) if given, then mark the js task complete after the - * items are all positioned. - * @param dotimeout (optional) if true, continually re-position the items so - * they stay in place. Else, if an integer, reposition this many times before stopping. - */ - position_drag_items: function(pendingid, dotimeout) { - Y.all(this.selectors.drags()).each(this.position_drag_item, this); - M.util.js_complete(pendingid); - if (dotimeout === true || dotimeout > 0) { - if (dotimeout !== true) { - dotimeout -= 1; - } - Y.later(500, this, this.position_drag_items, [pendingid, dotimeout]); - } - }, - position_drag_item: function(drag) { - if (!drag.hasClass('yui3-dd-dragging')) { - if (!this.placed[this.get_no(drag)]) { - var groupno = this.get_group(drag); - var choiceno = this.get_choice(drag); - var home = Y.one(this.selectors.drag_home(groupno, choiceno)); - drag.setXY(home.getXY()); - drag.addClass('unplaced'); - } else { - var placeno = this.placed[this.get_no(drag)]; - var drop = Y.one(this.selectors.drop_for_place(placeno)); - drag.setXY([drop.getX() + 2, drop.getY() + 2]); - drag.removeClass('unplaced'); - } - } - }, - - drop_zone_key_press: function(e) { - switch (e.direction) { - case 'next' : - this.place_next_drag_in(e.target); - break; - case 'previous' : - this.place_previous_drag_in(e.target); - break; - case 'remove' : - this.remove_drag_from_drop(e.target); - break; - } - e.preventDefault(); - }, - place_next_drag_in: function(drop) { - this.choose_next_choice_for_drop(drop, 1); - }, - place_previous_drag_in: function(drop) { - this.choose_next_choice_for_drop(drop, -1); - }, - choose_next_choice_for_drop: function(drop, direction) { - var next; - var groupno = this.get_group(drop); - var current = this.current_choice_in_drop(drop); - var unplaceddragsingroup = Y.all(this.selectors.unplaced_drags_in_group(groupno)); - if (0 === current) { - if (direction === 1) { - next = 1; - } else { - var lastdrag = unplaceddragsingroup.pop(); - next = this.get_choice(lastdrag); - } - } else { - next = current + direction; - } - var drag; - do { - drag = Y.one(this.selectors.unplaced_drags_for_choice_in_group(next, groupno)); - if (Y.one(this.selectors.drags_for_choice_in_group(next, groupno)) === null) { - this.remove_drag_from_drop(drop); - return; - } - next = next + direction; - } while (drag === null); - this.place_drag_in_drop(drag, drop); - }, - current_choice_in_drop: function(drop) { - var inputid = this.get('inputids')[this.get_place(drop)]; - var inputnode = Y.one('input#' + inputid); - return Number(inputnode.get('value')); - } -}, { - NAME: DDWTOSDDNAME, - ATTRS: { - readonly: {value: false}, - topnode: {value: null}, - inputids: {value: null} - } -}); - -Y.Event.define('dragchange', { - // Webkit and IE repeat keydown when you hold down arrow keys. - // Opera links keypress to page scroll; others keydown. - // Firefox prevents page scroll via preventDefault() on either - // keydown or keypress. - _event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress', - - _keys: { - '32': 'next', // Space - '37': 'previous', // Left arrow - '38': 'previous', // Up arrow - '39': 'next', // Right arrow - '40': 'next', // Down arrow - '27': 'remove' // Escape - }, - - _keyHandler: function(e, notifier) { - if (this._keys[e.keyCode]) { - e.direction = this._keys[e.keyCode]; - notifier.fire(e); - } - }, - - on: function(node, sub, notifier) { - sub._detacher = node.on(this._event, this._keyHandler, - this, notifier); - } -}); - -M.qtype_ddwtos = M.qtype_ddwtos || {}; -M.qtype_ddwtos.init_question = function(config) { - return new DDWTOS_DD(config); -}; diff --git a/question/type/ddwtos/yui/src/ddwtos/meta/ddwtos.json b/question/type/ddwtos/yui/src/ddwtos/meta/ddwtos.json deleted file mode 100644 index 6e7953d7c73..00000000000 --- a/question/type/ddwtos/yui/src/ddwtos/meta/ddwtos.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "moodle-qtype_ddwtos-dd": { - "requires": [ - "node", - "dd", - "dd-drop", - "dd-constrain" - ] - } -}