From dc2d6f879adec9b5e72cc8b2c8c9e935a853e7d3 Mon Sep 17 00:00:00 2001 From: Anupama Sarjoshi Date: Thu, 11 May 2023 18:14:33 +0100 Subject: [PATCH] MDL-78176 Question: Fix D&D onto image and D&D markers theme issue --- .../type/ddimageortext/amd/build/form.min.js | 2 +- .../ddimageortext/amd/build/form.min.js.map | 2 +- question/type/ddimageortext/amd/src/form.js | 25 +++++++++++++------ question/type/ddmarker/amd/build/form.min.js | 2 +- .../type/ddmarker/amd/build/form.min.js.map | 2 +- question/type/ddmarker/amd/src/form.js | 9 +++++-- question/type/ddmarker/styles.css | 4 --- 7 files changed, 28 insertions(+), 18 deletions(-) diff --git a/question/type/ddimageortext/amd/build/form.min.js b/question/type/ddimageortext/amd/build/form.min.js index d78b1f255ae1a..0a26d4981391b 100644 --- a/question/type/ddimageortext/amd/build/form.min.js +++ b/question/type/ddimageortext/amd/build/form.min.js @@ -5,6 +5,6 @@ * @copyright 2018 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("qtype_ddimageortext/form",["jquery","core/dragdrop"],(function($,dragDrop){var dragDropToImageForm={maxBgImageSize:null,maxDragImageSize:null,fp:null,init:function(){dragDropToImageForm.fp=dragDropToImageForm.filePickers(),$("#id_previewareaheader").append('
'),dragDropToImageForm.updateVisibilityOfFilePickers(),dragDropToImageForm.setOptionsForDragItemSelectors(),dragDropToImageForm.setupEventHandlers(),dragDropToImageForm.waitForFilePickerToInitialise()},waitForFilePickerToInitialise:function(){null!==dragDropToImageForm.fp.file("bgimage").href?(M.util.js_pending("dragDropToImageForm"),$('form.mform[data-qtype="ddimageortext"]').on("change",".filepickerhidden",(function(){M.util.js_pending("dragDropToImageForm"),dragDropToImageForm.loadPreviewImage()})),dragDropToImageForm.loadPreviewImage()):setTimeout(dragDropToImageForm.waitForFilePickerToInitialise,1e3)},loadPreviewImage:function(){$("fieldset#id_previewareaheader .dropbackground").one("load",dragDropToImageForm.afterPreviewImageLoaded).attr("src",dragDropToImageForm.fp.file("bgimage").href)},afterPreviewImageLoaded:function(){dragDropToImageForm.createDropZones(),M.util.js_complete("dragDropToImageForm")},createDropZones:function(){var dropZoneHolder=$(".dropzones");if(dropZoneHolder.empty(),null!==dragDropToImageForm.fp.file("bgimage").href){for(var numDrops=dragDropToImageForm.form.getFormValue("nodropzone",[]),dropNo=0;dropNo')}else""!==label&&dropZoneHolder.append('
'+label+"
")}}dragDropToImageForm.waitForAllDropImagesToBeLoaded()}},waitForAllDropImagesToBeLoaded:function(){$(".dropzones img").not((function(i,imgNode){return dragDropToImageForm.imageIsLoaded(imgNode)})).length>0?setTimeout((function(){dragDropToImageForm.waitForAllDropImagesToBeLoaded()}),100):dragDropToImageForm.updateDropZones()},imageIsLoaded:function(imgElement){return imgElement.complete&&0!==imgElement.naturalHeight},updateDropZones:function(){if(null!==dragDropToImageForm.fp.file("bgimage").href){for(var dropBackgroundPosition=$("fieldset#id_previewareaheader .dropbackground").offset(),numDrops=dragDropToImageForm.form.getFormValue("nodropzone",[]),dropNo=0;dropNo'+dragItemOptions[value]+"");var optionnode=selector.find('option[value="'+value+'"]');parseInt(value)===parseInt(selectedvalue)?optionnode.attr("selected",!0):dragDropToImageForm.isItemUsed(parseInt(value))&&optionnode.attr("disabled",!0)}}},isItemUsed:function(value){return 0!==value&&(!dragDropToImageForm.form.getFormValue("drags",[value-1,"infinite"])&&0!==$("fieldset#id_dropzoneheader select").filter((function(i,selectNode){return parseInt($(selectNode).val())===value})).length)},dragStart:function(e){var drop=$(e.target).closest(".droppreview");dragDrop.prepare(e).start&&dragDrop.start(e,drop,(function(x,y,drop){dragDropToImageForm.dragMove(drop)}),(function(){dragDropToImageForm.dragEnd()}))},dragMove:function(drop){var backgroundImage=$("fieldset#id_previewareaheader .dropbackground"),backgroundPosition=backgroundImage.offset(),dropNo=drop.data("dropNo"),dropPosition=drop.offset(),left=Math.round(dropPosition.left-backgroundPosition.left),top=Math.round(dropPosition.top-backgroundPosition.top);left=Math.round(Math.max(0,Math.min(left,backgroundImage.outerWidth()-drop.outerWidth()))),top=Math.round(Math.max(0,Math.min(top,backgroundImage.outerHeight()-drop.outerHeight()))),dragDropToImageForm.form.setFormValue("drops",[dropNo,"xleft"],left),dragDropToImageForm.form.setFormValue("drops",[dropNo,"ytop"],top)},dragEnd:function(){dragDropToImageForm.updateDropZones()},form:{toNameWithIndex:function(name,indexes){for(var indexString=name,i=0;i
')},waitForFilePickerToInitialise:function(){null!==dragDropToImageForm.fp.file("bgimage").href?(M.util.js_pending("dragDropToImageForm"),$('form.mform[data-qtype="ddimageortext"]').on("change",".filepickerhidden",(function(){M.util.js_pending("dragDropToImageForm"),dragDropToImageForm.loadPreviewImage()})),$("#id_droparea").length||dragDropToImageForm.setupPreviewArea(),dragDropToImageForm.loadPreviewImage()):setTimeout(dragDropToImageForm.waitForFilePickerToInitialise,1e3)},loadPreviewImage:function(){$("fieldset#id_previewareaheader .dropbackground").one("load",dragDropToImageForm.afterPreviewImageLoaded).attr("src",dragDropToImageForm.fp.file("bgimage").href)},afterPreviewImageLoaded:function(){dragDropToImageForm.createDropZones(),M.util.js_complete("dragDropToImageForm")},createDropZones:function(){var dropZoneHolder=$(".dropzones");if(dropZoneHolder.empty(),null!==dragDropToImageForm.fp.file("bgimage").href){for(var numDrops=dragDropToImageForm.form.getFormValue("nodropzone",[]),dropNo=0;dropNo')}else""!==label&&dropZoneHolder.append('
'+label+"
")}}dragDropToImageForm.waitForAllDropImagesToBeLoaded()}},waitForAllDropImagesToBeLoaded:function(){$(".dropzones img").not((function(i,imgNode){return dragDropToImageForm.imageIsLoaded(imgNode)})).length>0?setTimeout((function(){dragDropToImageForm.waitForAllDropImagesToBeLoaded()}),100):dragDropToImageForm.updateDropZones()},imageIsLoaded:function(imgElement){return imgElement.complete&&0!==imgElement.naturalHeight},updateDropZones:function(){if(null!==dragDropToImageForm.fp.file("bgimage").href){for(var dropBackgroundPosition=$("fieldset#id_previewareaheader .dropbackground").offset(),numDrops=dragDropToImageForm.form.getFormValue("nodropzone",[]),dropNo=0;dropNo'+dragItemOptions[value]+"");var optionnode=selector.find('option[value="'+value+'"]');parseInt(value)===parseInt(selectedvalue)?optionnode.attr("selected",!0):dragDropToImageForm.isItemUsed(parseInt(value))&&optionnode.attr("disabled",!0)}}},isItemUsed:function(value){return 0!==value&&(!dragDropToImageForm.form.getFormValue("drags",[value-1,"infinite"])&&0!==$("fieldset#id_dropzoneheader select").filter((function(i,selectNode){return parseInt($(selectNode).val())===value})).length)},dragStart:function(e){var drop=$(e.target).closest(".droppreview");dragDrop.prepare(e).start&&dragDrop.start(e,drop,(function(x,y,drop){dragDropToImageForm.dragMove(drop)}),(function(){dragDropToImageForm.dragEnd()}))},dragMove:function(drop){var backgroundImage=$("fieldset#id_previewareaheader .dropbackground"),backgroundPosition=backgroundImage.offset(),dropNo=drop.data("dropNo"),dropPosition=drop.offset(),left=Math.round(dropPosition.left-backgroundPosition.left),top=Math.round(dropPosition.top-backgroundPosition.top);left=Math.round(Math.max(0,Math.min(left,backgroundImage.outerWidth()-drop.outerWidth()))),top=Math.round(Math.max(0,Math.min(top,backgroundImage.outerHeight()-drop.outerHeight()))),dragDropToImageForm.form.setFormValue("drops",[dropNo,"xleft"],left),dragDropToImageForm.form.setFormValue("drops",[dropNo,"ytop"],top)},dragEnd:function(){dragDropToImageForm.updateDropZones()},form:{toNameWithIndex:function(name,indexes){for(var indexString=name,i=0;i.\n\n/*\n * JavaScript to allow dragging options to slots (using mouse down or touch) or tab through slots using keyboard.\n *\n * @module qtype_ddimageortext/form\n * @copyright 2018 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/dragdrop'], function($, dragDrop) {\n\n \"use strict\";\n\n /**\n * Singleton object to handle progressive enhancement of the\n * drag-drop onto image question editing form.\n * @type {Object}\n */\n var dragDropToImageForm = {\n /**\n * @var {Object} maxBgImageSize Properties width and height.\n * @private\n */\n maxBgImageSize: null,\n\n /**\n * @var {Object} maxDragImageSize with properties width and height.\n * @private\n */\n maxDragImageSize: null,\n\n /**\n * @property {object} fp for interacting with the file pickers.\n * @private\n */\n fp: null, // Object containing functions associated with the file picker.\n\n /**\n * Initialise the form javascript features.\n *\n * @method\n */\n init: function() {\n dragDropToImageForm.fp = dragDropToImageForm.filePickers();\n\n $('#id_previewareaheader').append(\n '
' +\n '
' +\n ' ' +\n '
' +\n '
' +\n '
' +\n '
');\n\n dragDropToImageForm.updateVisibilityOfFilePickers();\n dragDropToImageForm.setOptionsForDragItemSelectors();\n dragDropToImageForm.setupEventHandlers();\n dragDropToImageForm.waitForFilePickerToInitialise();\n },\n\n /**\n * Waits for the file-pickers to be sufficiently ready before initialising the preview.\n */\n waitForFilePickerToInitialise: function() {\n if (dragDropToImageForm.fp.file('bgimage').href === null) {\n // It would be better to use an onload or onchange event rather than this timeout.\n // Unfortunately attempts to do this early are overwritten by filepicker during its loading.\n setTimeout(dragDropToImageForm.waitForFilePickerToInitialise, 1000);\n return;\n }\n M.util.js_pending('dragDropToImageForm');\n\n // From now on, when a new file gets loaded into the filepicker, update the preview.\n // This is not in the setupEventHandlers section as it needs to be delayed until\n // after filepicker's javascript has finished.\n $('form.mform[data-qtype=\"ddimageortext\"]').on('change', '.filepickerhidden', function() {\n M.util.js_pending('dragDropToImageForm');\n dragDropToImageForm.loadPreviewImage();\n });\n\n dragDropToImageForm.loadPreviewImage();\n },\n\n /**\n * Loads the preview background image.\n */\n loadPreviewImage: function() {\n $('fieldset#id_previewareaheader .dropbackground')\n .one('load', dragDropToImageForm.afterPreviewImageLoaded)\n .attr('src', dragDropToImageForm.fp.file('bgimage').href);\n },\n\n /**\n * After the background image is loaded, continue setting up the preview.\n */\n afterPreviewImageLoaded: function() {\n dragDropToImageForm.createDropZones();\n M.util.js_complete('dragDropToImageForm');\n },\n\n /**\n * Create, or recreate all the drop zones.\n */\n createDropZones: function() {\n var dropZoneHolder = $('.dropzones');\n dropZoneHolder.empty();\n\n var bgimageurl = dragDropToImageForm.fp.file('bgimage').href;\n if (bgimageurl === null) {\n return; // There is not currently a valid preview to update.\n }\n\n var numDrops = dragDropToImageForm.form.getFormValue('nodropzone', []);\n for (var dropNo = 0; dropNo < numDrops; dropNo++) {\n var dragNo = dragDropToImageForm.form.getFormValue('drops', [dropNo, 'choice']);\n if (dragNo === '0') {\n continue;\n }\n dragNo = dragNo - 1;\n var group = dragDropToImageForm.form.getFormValue('drags', [dragNo, 'draggroup']),\n label = dragDropToImageForm.form.getFormValue('draglabel', [dragNo]);\n if ('image' === dragDropToImageForm.form.getFormValue('drags', [dragNo, 'dragitemtype'])) {\n var imgUrl = dragDropToImageForm.fp.file('dragitem[' + dragNo + ']').href;\n if (imgUrl === null) {\n continue;\n }\n // Althoug these are previews of drops, we also add the class name 'drag',\n dropZoneHolder.append('\"'');\n\n } else if (label !== '') {\n dropZoneHolder.append('
' + label + '
');\n }\n }\n\n dragDropToImageForm.waitForAllDropImagesToBeLoaded();\n },\n\n /**\n * This polls until all the drop-zone images have loaded, and then calls updateDropZones().\n */\n waitForAllDropImagesToBeLoaded: function() {\n var notYetLoadedImages = $('.dropzones img').not(function(i, imgNode) {\n return dragDropToImageForm.imageIsLoaded(imgNode);\n });\n\n if (notYetLoadedImages.length > 0) {\n setTimeout(function() {\n dragDropToImageForm.waitForAllDropImagesToBeLoaded();\n }, 100);\n return;\n }\n\n dragDropToImageForm.updateDropZones();\n },\n\n /**\n * Check if an image has loaded without errors.\n *\n * @param {HTMLImageElement} imgElement an image.\n * @returns {boolean} true if this image has loaded without errors.\n */\n imageIsLoaded: function(imgElement) {\n return imgElement.complete && imgElement.naturalHeight !== 0;\n },\n\n /**\n * Set the size and position of all the drop zones.\n */\n updateDropZones: function() {\n var bgimageurl = dragDropToImageForm.fp.file('bgimage').href;\n if (bgimageurl === null) {\n return; // There is not currently a valid preview to update.\n }\n\n var dropBackgroundPosition = $('fieldset#id_previewareaheader .dropbackground').offset(),\n numDrops = dragDropToImageForm.form.getFormValue('nodropzone', []);\n\n // Move each drop to the right position and update the text.\n for (var dropNo = 0; dropNo < numDrops; dropNo++) {\n var drop = $('.dropzones .drop' + dropNo);\n if (drop.length === 0) {\n continue;\n }\n var dragNo = dragDropToImageForm.form.getFormValue('drops', [dropNo, 'choice']) - 1;\n\n drop.offset({\n left: dropBackgroundPosition.left +\n parseInt(dragDropToImageForm.form.getFormValue('drops', [dropNo, 'xleft'])),\n top: dropBackgroundPosition.top +\n parseInt(dragDropToImageForm.form.getFormValue('drops', [dropNo, 'ytop']))\n });\n\n var label = dragDropToImageForm.form.getFormValue('draglabel', [dragNo]);\n if (drop.is('img')) {\n drop.attr('alt', label);\n } else {\n drop.html(label);\n }\n }\n\n // Resize them to the same size.\n $('.dropzones .droppreview').css('padding', '0');\n var numGroups = $('.draggroup select').first().find('option').length;\n for (var group = 1; group <= numGroups; group++) {\n dragDropToImageForm.resizeAllDragsAndDropsInGroup(group);\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 resizeAllDragsAndDropsInGroup: function(group) {\n var drops = $('.dropzones .droppreview.group' + group),\n maxWidth = 0,\n maxHeight = 0;\n\n // Find the maximum size of any drag in this groups.\n drops.each(function(i, drop) {\n maxWidth = Math.max(maxWidth, Math.ceil(drop.offsetWidth));\n maxHeight = Math.max(maxHeight, Math.ceil(drop.offsetHeight));\n });\n\n // The size we will want to set is a bit bigger than this.\n maxWidth += 10;\n maxHeight += 10;\n\n // Set each drag home to that size.\n drops.each(function(i, drop) {\n var left = Math.round((maxWidth - drop.offsetWidth) / 2),\n top = Math.floor((maxHeight - drop.offsetHeight) / 2);\n // Set top and left padding so the item is centred.\n $(drop).css({\n 'padding-left': left + 'px',\n 'padding-right': (maxWidth - drop.offsetWidth - left) + 'px',\n 'padding-top': top + 'px',\n 'padding-bottom': (maxHeight - drop.offsetHeight - top) + 'px'\n });\n });\n },\n\n /**\n * Events linked to form actions.\n */\n setupEventHandlers: function() {\n // Changes to settings in the draggable items section.\n $('fieldset#id_draggableitemheader')\n .on('change input', 'input, select', function(e) {\n var input = $(e.target).closest('select, input');\n if (input.hasClass('dragitemtype')) {\n dragDropToImageForm.updateVisibilityOfFilePickers();\n }\n\n dragDropToImageForm.setOptionsForDragItemSelectors();\n\n if (input.is('.dragitemtype, .draggroup')) {\n dragDropToImageForm.createDropZones();\n } else if (input.is('.draglabel')) {\n dragDropToImageForm.updateDropZones();\n }\n });\n\n // Changes to Drop zones section: left, top and drag item.\n $('fieldset#id_dropzoneheader').on('change input', 'input, select', function(e) {\n var input = $(e.target).closest('select, input');\n if (input.is('select')) {\n dragDropToImageForm.createDropZones();\n } else {\n dragDropToImageForm.updateDropZones();\n }\n });\n\n // Moving drop zones in the preview.\n $('fieldset#id_previewareaheader').on('mousedown touchstart', '.droppreview', function(e) {\n dragDropToImageForm.dragStart(e);\n });\n\n $(window).on('resize', function() {\n dragDropToImageForm.updateDropZones();\n });\n },\n\n /**\n * Update all the drag item filepickers, so they are only shown for\n */\n updateVisibilityOfFilePickers: function() {\n var numDrags = dragDropToImageForm.form.getFormValue('noitems', []);\n for (var dragNo = 0; dragNo < numDrags; dragNo++) {\n var picker = $('input#id_dragitem_' + dragNo).closest('.fitem_ffilepicker');\n if ('image' === dragDropToImageForm.form.getFormValue('drags', [dragNo, 'dragitemtype'])) {\n picker.show();\n } else {\n picker.hide();\n }\n }\n },\n\n\n setOptionsForDragItemSelectors: function() {\n var dragItemOptions = {'0': ''},\n numDrags = dragDropToImageForm.form.getFormValue('noitems', []),\n numDrops = dragDropToImageForm.form.getFormValue('nodropzone', []);\n\n // Work out the list of options.\n for (var dragNo = 0; dragNo < numDrags; dragNo++) {\n var label = dragDropToImageForm.form.getFormValue('draglabel', [dragNo]);\n var file = dragDropToImageForm.fp.file(dragDropToImageForm.form.toNameWithIndex('dragitem', [dragNo]));\n if ('image' === dragDropToImageForm.form.getFormValue('drags', [dragNo, 'dragitemtype']) && file.name !== null) {\n dragItemOptions[dragNo + 1] = (dragNo + 1) + '. ' + label + ' (' + file.name + ')';\n } else if (label !== '') {\n dragItemOptions[dragNo + 1] = (dragNo + 1) + '. ' + label;\n }\n }\n\n // Initialise each select.\n for (var dropNo = 0; dropNo < numDrops; dropNo++) {\n var selector = $('#id_drops_' + dropNo + '_choice');\n\n var selectedvalue = selector.val();\n selector.find('option').remove();\n for (var value in dragItemOptions) {\n if (!dragItemOptions.hasOwnProperty(value)) {\n continue;\n }\n selector.append('');\n var optionnode = selector.find('option[value=\"' + value + '\"]');\n if (parseInt(value) === parseInt(selectedvalue)) {\n optionnode.attr('selected', true);\n } else if (dragDropToImageForm.isItemUsed(parseInt(value))) {\n optionnode.attr('disabled', true);\n }\n }\n }\n },\n\n /**\n * Checks if the specified drag option is already used somewhere.\n *\n * @param {Number} value of the drag item to check\n * @return {Boolean} true if item is allocated to dropzone\n */\n isItemUsed: function(value) {\n if (value === 0) {\n return false; // None option can always be selected.\n }\n\n if (dragDropToImageForm.form.getFormValue('drags', [value - 1, 'infinite'])) {\n return false; // Infinite, so can't be used up.\n }\n\n return $('fieldset#id_dropzoneheader select').filter(function(i, selectNode) {\n return parseInt($(selectNode).val()) === value;\n }).length !== 0;\n },\n\n /**\n * Handles when a dropzone in dragged in the preview.\n * @param {Object} e Event object\n */\n dragStart: function(e) {\n var drop = $(e.target).closest('.droppreview');\n\n var info = dragDrop.prepare(e);\n if (!info.start) {\n return;\n }\n\n dragDrop.start(e, drop, function(x, y, drop) {\n dragDropToImageForm.dragMove(drop);\n }, function() {\n dragDropToImageForm.dragEnd();\n });\n },\n\n /**\n * Handles update while a drop is being dragged.\n *\n * @param {jQuery} drop the drop preview being moved.\n */\n dragMove: function(drop) {\n var backgroundImage = $('fieldset#id_previewareaheader .dropbackground'),\n backgroundPosition = backgroundImage.offset(),\n dropNo = drop.data('dropNo'),\n dropPosition = drop.offset(),\n left = Math.round(dropPosition.left - backgroundPosition.left),\n top = Math.round(dropPosition.top - backgroundPosition.top);\n\n // Constrain coordinates to be inside the background.\n left = Math.round(Math.max(0, Math.min(left, backgroundImage.outerWidth() - drop.outerWidth())));\n top = Math.round(Math.max(0, Math.min(top, backgroundImage.outerHeight() - drop.outerHeight())));\n\n // Update the form.\n dragDropToImageForm.form.setFormValue('drops', [dropNo, 'xleft'], left);\n dragDropToImageForm.form.setFormValue('drops', [dropNo, 'ytop'], top);\n },\n\n /**\n * Handles when the drag ends.\n */\n dragEnd: function() {\n // Redraw, in case the position was constrained.\n dragDropToImageForm.updateDropZones();\n },\n\n /**\n * Low level operations on form.\n */\n form: {\n toNameWithIndex: function(name, indexes) {\n var indexString = name;\n for (var i = 0; i < indexes.length; i++) {\n indexString = indexString + '[' + indexes[i] + ']';\n }\n return indexString;\n },\n\n getEl: function(name, indexes) {\n var form = $('form.mform[data-qtype=\"ddimageortext\"]')[0];\n return form.elements[this.toNameWithIndex(name, indexes)];\n },\n\n /**\n * Helper to get the value of a form elements with name like \"drops[0][xleft]\".\n *\n * @param {String} name the base name, e.g. 'drops'.\n * @param {String[]} indexes the indexes, e.g. ['0', 'xleft'].\n * @return {String} the value of that field.\n */\n getFormValue: function(name, indexes) {\n var el = this.getEl(name, indexes);\n if (!el.type) {\n el = el[el.length - 1];\n }\n if (el.type === 'checkbox') {\n return el.checked;\n } else {\n return el.value;\n }\n },\n\n /**\n * Helper to get the value of a form elements with name like \"drops[0][xleft]\".\n *\n * @param {String} name the base name, e.g. 'drops'.\n * @param {String[]} indexes the indexes, e.g. ['0', 'xleft'].\n * @param {String|Number} value the value to set.\n */\n setFormValue: function(name, indexes, value) {\n var el = this.getEl(name, indexes);\n if (el.type === 'checkbox') {\n el.checked = value;\n } else {\n el.value = value;\n }\n }\n },\n\n /**\n * Utility to get the file name and url from the filepicker.\n * @returns {Object} object containing functions {file, name}\n */\n filePickers: function() {\n var draftItemIdsToName;\n var nameToParentNode;\n\n if (draftItemIdsToName === undefined) {\n draftItemIdsToName = {};\n nameToParentNode = {};\n var fp = $('form.mform[data-qtype=\"ddimageortext\"] input.filepickerhidden');\n fp.each(function(index, filepicker) {\n draftItemIdsToName[filepicker.value] = filepicker.name;\n nameToParentNode[filepicker.name] = filepicker.parentNode;\n });\n }\n\n return {\n file: function(name) {\n var parentNode = $(nameToParentNode[name]);\n var fileAnchor = parentNode.find('div.filepicker-filelist a');\n if (fileAnchor.length) {\n return {href: fileAnchor.get(0).href, name: fileAnchor.get(0).innerHTML};\n } else {\n return {href: null, name: null};\n }\n },\n\n name: function(draftitemid) {\n return draftItemIdsToName[draftitemid];\n }\n };\n }\n };\n\n return {\n init: dragDropToImageForm.init\n };\n});\n"],"names":["define","$","dragDrop","dragDropToImageForm","maxBgImageSize","maxDragImageSize","fp","init","filePickers","append","updateVisibilityOfFilePickers","setOptionsForDragItemSelectors","setupEventHandlers","waitForFilePickerToInitialise","file","href","M","util","js_pending","on","loadPreviewImage","setTimeout","one","afterPreviewImageLoaded","attr","createDropZones","js_complete","dropZoneHolder","empty","numDrops","form","getFormValue","dropNo","dragNo","group","label","imgUrl","waitForAllDropImagesToBeLoaded","not","i","imgNode","imageIsLoaded","length","updateDropZones","imgElement","complete","naturalHeight","dropBackgroundPosition","offset","drop","left","parseInt","top","is","html","css","numGroups","first","find","resizeAllDragsAndDropsInGroup","drops","maxWidth","maxHeight","each","Math","max","ceil","offsetWidth","offsetHeight","round","floor","e","input","target","closest","hasClass","dragStart","window","numDrags","picker","show","hide","dragItemOptions","toNameWithIndex","name","selector","selectedvalue","val","value","remove","hasOwnProperty","optionnode","isItemUsed","filter","selectNode","prepare","start","x","y","dragMove","dragEnd","backgroundImage","backgroundPosition","data","dropPosition","min","outerWidth","outerHeight","setFormValue","indexes","indexString","getEl","elements","this","el","type","checked","draftItemIdsToName","nameToParentNode","undefined","index","filepicker","parentNode","fileAnchor","get","innerHTML","draftitemid"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"form.min.js","sources":["../src/form.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/*\n * JavaScript to allow dragging options to slots (using mouse down or touch) or tab through slots using keyboard.\n *\n * @module qtype_ddimageortext/form\n * @copyright 2018 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/dragdrop'], function($, dragDrop) {\n\n \"use strict\";\n\n /**\n * Singleton object to handle progressive enhancement of the\n * drag-drop onto image question editing form.\n * @type {Object}\n */\n var dragDropToImageForm = {\n /**\n * @var {Object} maxBgImageSize Properties width and height.\n * @private\n */\n maxBgImageSize: null,\n\n /**\n * @var {Object} maxDragImageSize with properties width and height.\n * @private\n */\n maxDragImageSize: null,\n\n /**\n * @property {object} fp for interacting with the file pickers.\n * @private\n */\n fp: null, // Object containing functions associated with the file picker.\n\n /**\n * Initialise the form javascript features.\n *\n * @method\n */\n init: function() {\n dragDropToImageForm.fp = dragDropToImageForm.filePickers();\n dragDropToImageForm.updateVisibilityOfFilePickers();\n dragDropToImageForm.setOptionsForDragItemSelectors();\n dragDropToImageForm.setupEventHandlers();\n dragDropToImageForm.waitForFilePickerToInitialise();\n },\n\n /**\n * Add html for the preview area.\n */\n setupPreviewArea: function() {\n $('#id_previewareaheader').append(\n '
' +\n '
' +\n ' ' +\n '
' +\n '
' +\n '
' +\n '
');\n },\n\n /**\n * Waits for the file-pickers to be sufficiently ready before initialising the preview.\n */\n waitForFilePickerToInitialise: function() {\n if (dragDropToImageForm.fp.file('bgimage').href === null) {\n // It would be better to use an onload or onchange event rather than this timeout.\n // Unfortunately attempts to do this early are overwritten by filepicker during its loading.\n setTimeout(dragDropToImageForm.waitForFilePickerToInitialise, 1000);\n return;\n }\n M.util.js_pending('dragDropToImageForm');\n\n // From now on, when a new file gets loaded into the filepicker, update the preview.\n // This is not in the setupEventHandlers section as it needs to be delayed until\n // after filepicker's javascript has finished.\n $('form.mform[data-qtype=\"ddimageortext\"]').on('change', '.filepickerhidden', function() {\n M.util.js_pending('dragDropToImageForm');\n dragDropToImageForm.loadPreviewImage();\n });\n if ($('#id_droparea').length) {\n dragDropToImageForm.loadPreviewImage();\n } else {\n // Setup preview area when the background image is uploaded the first time.\n dragDropToImageForm.setupPreviewArea();\n dragDropToImageForm.loadPreviewImage();\n }\n },\n\n /**\n * Loads the preview background image.\n */\n loadPreviewImage: function() {\n $('fieldset#id_previewareaheader .dropbackground')\n .one('load', dragDropToImageForm.afterPreviewImageLoaded)\n .attr('src', dragDropToImageForm.fp.file('bgimage').href);\n },\n\n /**\n * After the background image is loaded, continue setting up the preview.\n */\n afterPreviewImageLoaded: function() {\n dragDropToImageForm.createDropZones();\n M.util.js_complete('dragDropToImageForm');\n },\n\n /**\n * Create, or recreate all the drop zones.\n */\n createDropZones: function() {\n var dropZoneHolder = $('.dropzones');\n dropZoneHolder.empty();\n\n var bgimageurl = dragDropToImageForm.fp.file('bgimage').href;\n if (bgimageurl === null) {\n return; // There is not currently a valid preview to update.\n }\n\n var numDrops = dragDropToImageForm.form.getFormValue('nodropzone', []);\n for (var dropNo = 0; dropNo < numDrops; dropNo++) {\n var dragNo = dragDropToImageForm.form.getFormValue('drops', [dropNo, 'choice']);\n if (dragNo === '0') {\n continue;\n }\n dragNo = dragNo - 1;\n var group = dragDropToImageForm.form.getFormValue('drags', [dragNo, 'draggroup']),\n label = dragDropToImageForm.form.getFormValue('draglabel', [dragNo]);\n if ('image' === dragDropToImageForm.form.getFormValue('drags', [dragNo, 'dragitemtype'])) {\n var imgUrl = dragDropToImageForm.fp.file('dragitem[' + dragNo + ']').href;\n if (imgUrl === null) {\n continue;\n }\n // Althoug these are previews of drops, we also add the class name 'drag',\n dropZoneHolder.append('\"'');\n\n } else if (label !== '') {\n dropZoneHolder.append('
' + label + '
');\n }\n }\n\n dragDropToImageForm.waitForAllDropImagesToBeLoaded();\n },\n\n /**\n * This polls until all the drop-zone images have loaded, and then calls updateDropZones().\n */\n waitForAllDropImagesToBeLoaded: function() {\n var notYetLoadedImages = $('.dropzones img').not(function(i, imgNode) {\n return dragDropToImageForm.imageIsLoaded(imgNode);\n });\n\n if (notYetLoadedImages.length > 0) {\n setTimeout(function() {\n dragDropToImageForm.waitForAllDropImagesToBeLoaded();\n }, 100);\n return;\n }\n\n dragDropToImageForm.updateDropZones();\n },\n\n /**\n * Check if an image has loaded without errors.\n *\n * @param {HTMLImageElement} imgElement an image.\n * @returns {boolean} true if this image has loaded without errors.\n */\n imageIsLoaded: function(imgElement) {\n return imgElement.complete && imgElement.naturalHeight !== 0;\n },\n\n /**\n * Set the size and position of all the drop zones.\n */\n updateDropZones: function() {\n var bgimageurl = dragDropToImageForm.fp.file('bgimage').href;\n if (bgimageurl === null) {\n return; // There is not currently a valid preview to update.\n }\n\n var dropBackgroundPosition = $('fieldset#id_previewareaheader .dropbackground').offset(),\n numDrops = dragDropToImageForm.form.getFormValue('nodropzone', []);\n\n // Move each drop to the right position and update the text.\n for (var dropNo = 0; dropNo < numDrops; dropNo++) {\n var drop = $('.dropzones .drop' + dropNo);\n if (drop.length === 0) {\n continue;\n }\n var dragNo = dragDropToImageForm.form.getFormValue('drops', [dropNo, 'choice']) - 1;\n\n drop.offset({\n left: dropBackgroundPosition.left +\n parseInt(dragDropToImageForm.form.getFormValue('drops', [dropNo, 'xleft'])),\n top: dropBackgroundPosition.top +\n parseInt(dragDropToImageForm.form.getFormValue('drops', [dropNo, 'ytop']))\n });\n\n var label = dragDropToImageForm.form.getFormValue('draglabel', [dragNo]);\n if (drop.is('img')) {\n drop.attr('alt', label);\n } else {\n drop.html(label);\n }\n }\n\n // Resize them to the same size.\n $('.dropzones .droppreview').css('padding', '0');\n var numGroups = $('.draggroup select').first().find('option').length;\n for (var group = 1; group <= numGroups; group++) {\n dragDropToImageForm.resizeAllDragsAndDropsInGroup(group);\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 resizeAllDragsAndDropsInGroup: function(group) {\n var drops = $('.dropzones .droppreview.group' + group),\n maxWidth = 0,\n maxHeight = 0;\n\n // Find the maximum size of any drag in this groups.\n drops.each(function(i, drop) {\n maxWidth = Math.max(maxWidth, Math.ceil(drop.offsetWidth));\n maxHeight = Math.max(maxHeight, Math.ceil(drop.offsetHeight));\n });\n\n // The size we will want to set is a bit bigger than this.\n maxWidth += 10;\n maxHeight += 10;\n\n // Set each drag home to that size.\n drops.each(function(i, drop) {\n var left = Math.round((maxWidth - drop.offsetWidth) / 2),\n top = Math.floor((maxHeight - drop.offsetHeight) / 2);\n // Set top and left padding so the item is centred.\n $(drop).css({\n 'padding-left': left + 'px',\n 'padding-right': (maxWidth - drop.offsetWidth - left) + 'px',\n 'padding-top': top + 'px',\n 'padding-bottom': (maxHeight - drop.offsetHeight - top) + 'px'\n });\n });\n },\n\n /**\n * Events linked to form actions.\n */\n setupEventHandlers: function() {\n // Changes to settings in the draggable items section.\n $('fieldset#id_draggableitemheader')\n .on('change input', 'input, select', function(e) {\n var input = $(e.target).closest('select, input');\n if (input.hasClass('dragitemtype')) {\n dragDropToImageForm.updateVisibilityOfFilePickers();\n }\n\n dragDropToImageForm.setOptionsForDragItemSelectors();\n\n if (input.is('.dragitemtype, .draggroup')) {\n dragDropToImageForm.createDropZones();\n } else if (input.is('.draglabel')) {\n dragDropToImageForm.updateDropZones();\n }\n });\n\n // Changes to Drop zones section: left, top and drag item.\n $('fieldset#id_dropzoneheader').on('change input', 'input, select', function(e) {\n var input = $(e.target).closest('select, input');\n if (input.is('select')) {\n dragDropToImageForm.createDropZones();\n } else {\n dragDropToImageForm.updateDropZones();\n }\n });\n\n // Moving drop zones in the preview.\n $('fieldset#id_previewareaheader').on('mousedown touchstart', '.droppreview', function(e) {\n dragDropToImageForm.dragStart(e);\n });\n\n $(window).on('resize', function() {\n dragDropToImageForm.updateDropZones();\n });\n },\n\n /**\n * Update all the drag item filepickers, so they are only shown for\n */\n updateVisibilityOfFilePickers: function() {\n var numDrags = dragDropToImageForm.form.getFormValue('noitems', []);\n for (var dragNo = 0; dragNo < numDrags; dragNo++) {\n var picker = $('input#id_dragitem_' + dragNo).closest('.fitem_ffilepicker');\n if ('image' === dragDropToImageForm.form.getFormValue('drags', [dragNo, 'dragitemtype'])) {\n picker.show();\n } else {\n picker.hide();\n }\n }\n },\n\n\n setOptionsForDragItemSelectors: function() {\n var dragItemOptions = {'0': ''},\n numDrags = dragDropToImageForm.form.getFormValue('noitems', []),\n numDrops = dragDropToImageForm.form.getFormValue('nodropzone', []);\n\n // Work out the list of options.\n for (var dragNo = 0; dragNo < numDrags; dragNo++) {\n var label = dragDropToImageForm.form.getFormValue('draglabel', [dragNo]);\n var file = dragDropToImageForm.fp.file(dragDropToImageForm.form.toNameWithIndex('dragitem', [dragNo]));\n if ('image' === dragDropToImageForm.form.getFormValue('drags', [dragNo, 'dragitemtype']) && file.name !== null) {\n dragItemOptions[dragNo + 1] = (dragNo + 1) + '. ' + label + ' (' + file.name + ')';\n } else if (label !== '') {\n dragItemOptions[dragNo + 1] = (dragNo + 1) + '. ' + label;\n }\n }\n\n // Initialise each select.\n for (var dropNo = 0; dropNo < numDrops; dropNo++) {\n var selector = $('#id_drops_' + dropNo + '_choice');\n\n var selectedvalue = selector.val();\n selector.find('option').remove();\n for (var value in dragItemOptions) {\n if (!dragItemOptions.hasOwnProperty(value)) {\n continue;\n }\n selector.append('');\n var optionnode = selector.find('option[value=\"' + value + '\"]');\n if (parseInt(value) === parseInt(selectedvalue)) {\n optionnode.attr('selected', true);\n } else if (dragDropToImageForm.isItemUsed(parseInt(value))) {\n optionnode.attr('disabled', true);\n }\n }\n }\n },\n\n /**\n * Checks if the specified drag option is already used somewhere.\n *\n * @param {Number} value of the drag item to check\n * @return {Boolean} true if item is allocated to dropzone\n */\n isItemUsed: function(value) {\n if (value === 0) {\n return false; // None option can always be selected.\n }\n\n if (dragDropToImageForm.form.getFormValue('drags', [value - 1, 'infinite'])) {\n return false; // Infinite, so can't be used up.\n }\n\n return $('fieldset#id_dropzoneheader select').filter(function(i, selectNode) {\n return parseInt($(selectNode).val()) === value;\n }).length !== 0;\n },\n\n /**\n * Handles when a dropzone in dragged in the preview.\n * @param {Object} e Event object\n */\n dragStart: function(e) {\n var drop = $(e.target).closest('.droppreview');\n\n var info = dragDrop.prepare(e);\n if (!info.start) {\n return;\n }\n\n dragDrop.start(e, drop, function(x, y, drop) {\n dragDropToImageForm.dragMove(drop);\n }, function() {\n dragDropToImageForm.dragEnd();\n });\n },\n\n /**\n * Handles update while a drop is being dragged.\n *\n * @param {jQuery} drop the drop preview being moved.\n */\n dragMove: function(drop) {\n var backgroundImage = $('fieldset#id_previewareaheader .dropbackground'),\n backgroundPosition = backgroundImage.offset(),\n dropNo = drop.data('dropNo'),\n dropPosition = drop.offset(),\n left = Math.round(dropPosition.left - backgroundPosition.left),\n top = Math.round(dropPosition.top - backgroundPosition.top);\n\n // Constrain coordinates to be inside the background.\n left = Math.round(Math.max(0, Math.min(left, backgroundImage.outerWidth() - drop.outerWidth())));\n top = Math.round(Math.max(0, Math.min(top, backgroundImage.outerHeight() - drop.outerHeight())));\n\n // Update the form.\n dragDropToImageForm.form.setFormValue('drops', [dropNo, 'xleft'], left);\n dragDropToImageForm.form.setFormValue('drops', [dropNo, 'ytop'], top);\n },\n\n /**\n * Handles when the drag ends.\n */\n dragEnd: function() {\n // Redraw, in case the position was constrained.\n dragDropToImageForm.updateDropZones();\n },\n\n /**\n * Low level operations on form.\n */\n form: {\n toNameWithIndex: function(name, indexes) {\n var indexString = name;\n for (var i = 0; i < indexes.length; i++) {\n indexString = indexString + '[' + indexes[i] + ']';\n }\n return indexString;\n },\n\n getEl: function(name, indexes) {\n var form = $('form.mform[data-qtype=\"ddimageortext\"]')[0];\n return form.elements[this.toNameWithIndex(name, indexes)];\n },\n\n /**\n * Helper to get the value of a form elements with name like \"drops[0][xleft]\".\n *\n * @param {String} name the base name, e.g. 'drops'.\n * @param {String[]} indexes the indexes, e.g. ['0', 'xleft'].\n * @return {String} the value of that field.\n */\n getFormValue: function(name, indexes) {\n var el = this.getEl(name, indexes);\n if (!el.type) {\n el = el[el.length - 1];\n }\n if (el.type === 'checkbox') {\n return el.checked;\n } else {\n return el.value;\n }\n },\n\n /**\n * Helper to get the value of a form elements with name like \"drops[0][xleft]\".\n *\n * @param {String} name the base name, e.g. 'drops'.\n * @param {String[]} indexes the indexes, e.g. ['0', 'xleft'].\n * @param {String|Number} value the value to set.\n */\n setFormValue: function(name, indexes, value) {\n var el = this.getEl(name, indexes);\n if (el.type === 'checkbox') {\n el.checked = value;\n } else {\n el.value = value;\n }\n }\n },\n\n /**\n * Utility to get the file name and url from the filepicker.\n * @returns {Object} object containing functions {file, name}\n */\n filePickers: function() {\n var draftItemIdsToName;\n var nameToParentNode;\n\n if (draftItemIdsToName === undefined) {\n draftItemIdsToName = {};\n nameToParentNode = {};\n var fp = $('form.mform[data-qtype=\"ddimageortext\"] input.filepickerhidden');\n fp.each(function(index, filepicker) {\n draftItemIdsToName[filepicker.value] = filepicker.name;\n nameToParentNode[filepicker.name] = filepicker.parentNode;\n });\n }\n\n return {\n file: function(name) {\n var parentNode = $(nameToParentNode[name]);\n var fileAnchor = parentNode.find('div.filepicker-filelist a');\n if (fileAnchor.length) {\n return {href: fileAnchor.get(0).href, name: fileAnchor.get(0).innerHTML};\n } else {\n return {href: null, name: null};\n }\n },\n\n name: function(draftitemid) {\n return draftItemIdsToName[draftitemid];\n }\n };\n }\n };\n\n return {\n init: dragDropToImageForm.init\n };\n});\n"],"names":["define","$","dragDrop","dragDropToImageForm","maxBgImageSize","maxDragImageSize","fp","init","filePickers","updateVisibilityOfFilePickers","setOptionsForDragItemSelectors","setupEventHandlers","waitForFilePickerToInitialise","setupPreviewArea","append","file","href","M","util","js_pending","on","loadPreviewImage","length","setTimeout","one","afterPreviewImageLoaded","attr","createDropZones","js_complete","dropZoneHolder","empty","numDrops","form","getFormValue","dropNo","dragNo","group","label","imgUrl","waitForAllDropImagesToBeLoaded","not","i","imgNode","imageIsLoaded","updateDropZones","imgElement","complete","naturalHeight","dropBackgroundPosition","offset","drop","left","parseInt","top","is","html","css","numGroups","first","find","resizeAllDragsAndDropsInGroup","drops","maxWidth","maxHeight","each","Math","max","ceil","offsetWidth","offsetHeight","round","floor","e","input","target","closest","hasClass","dragStart","window","numDrags","picker","show","hide","dragItemOptions","toNameWithIndex","name","selector","selectedvalue","val","value","remove","hasOwnProperty","optionnode","isItemUsed","filter","selectNode","prepare","start","x","y","dragMove","dragEnd","backgroundImage","backgroundPosition","data","dropPosition","min","outerWidth","outerHeight","setFormValue","indexes","indexString","getEl","elements","this","el","type","checked","draftItemIdsToName","nameToParentNode","undefined","index","filepicker","parentNode","fileAnchor","get","innerHTML","draftitemid"],"mappings":"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"} \ No newline at end of file diff --git a/question/type/ddimageortext/amd/src/form.js b/question/type/ddimageortext/amd/src/form.js index f4ae38295f069..684c99ed30de7 100644 --- a/question/type/ddimageortext/amd/src/form.js +++ b/question/type/ddimageortext/amd/src/form.js @@ -55,20 +55,24 @@ define(['jquery', 'core/dragdrop'], function($, dragDrop) { */ init: function() { dragDropToImageForm.fp = dragDropToImageForm.filePickers(); + dragDropToImageForm.updateVisibilityOfFilePickers(); + dragDropToImageForm.setOptionsForDragItemSelectors(); + dragDropToImageForm.setupEventHandlers(); + dragDropToImageForm.waitForFilePickerToInitialise(); + }, + /** + * Add html for the preview area. + */ + setupPreviewArea: function() { $('#id_previewareaheader').append( '
' + - '
' + + '
' + ' ' + '
' + '
' + '
' + '
'); - - dragDropToImageForm.updateVisibilityOfFilePickers(); - dragDropToImageForm.setOptionsForDragItemSelectors(); - dragDropToImageForm.setupEventHandlers(); - dragDropToImageForm.waitForFilePickerToInitialise(); }, /** @@ -90,8 +94,13 @@ define(['jquery', 'core/dragdrop'], function($, dragDrop) { M.util.js_pending('dragDropToImageForm'); dragDropToImageForm.loadPreviewImage(); }); - - dragDropToImageForm.loadPreviewImage(); + if ($('#id_droparea').length) { + dragDropToImageForm.loadPreviewImage(); + } else { + // Setup preview area when the background image is uploaded the first time. + dragDropToImageForm.setupPreviewArea(); + dragDropToImageForm.loadPreviewImage(); + } }, /** diff --git a/question/type/ddmarker/amd/build/form.min.js b/question/type/ddmarker/amd/build/form.min.js index cf823f6287fbd..c46df059bf8af 100644 --- a/question/type/ddmarker/amd/build/form.min.js +++ b/question/type/ddmarker/amd/build/form.min.js @@ -5,6 +5,6 @@ * @copyright 2018 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("qtype_ddmarker/form",["jquery","core/dragdrop","qtype_ddmarker/shapes"],(function($,dragDrop,Shapes){function DropZoneManager(dropzoneNo){this.dropzoneNo=dropzoneNo,this.svgEl=null,this.shape=Shapes.make(this.getShapeType(),this.getLabel()),this.updateCoordinatesFromForm()}DropZoneManager.prototype.updateCoordinatesFromForm=function(svg){var coordinates=this.getCoordinates(),currentNumPoints="polygon"===this.shape.getType()&&this.shape.points.length;if(this.shape.getCoordinates()!==coordinates&&this.shape.parse(coordinates,1)){if("polygon"===this.shape.getType()&¤tNumPoints!==this.shape.points.length){var currentyActive=this.isActive();this.removeFromSvg(),svg&&(this.addToSvg(svg),currentyActive&&this.setActive())}else this.updateSvgEl();this.setCoordinatesInForm()}},DropZoneManager.prototype.updateLabel=function(){var label=this.getLabel();this.shape.label!==label&&(this.shape.label=label,this.updateSvgEl())},DropZoneManager.prototype.changeShape=function(svg){var newShapeType=this.getShapeType(),currentyActive=this.isActive();newShapeType!==this.shape.getType()&&(this.removeFromSvg(),this.shape=Shapes.getSimilar(newShapeType,this.shape),svg&&(this.addToSvg(svg),currentyActive&&this.setActive()),this.setCoordinatesInForm())},DropZoneManager.prototype.addToSvg=function(svg){if(null!==this.svgEl)throw new Error("this.svgEl already set");if(this.svgEl=this.shape.makeSvg(svg),this.svgEl){this.svgEl.setAttribute("class","dropzone"),this.svgEl.setAttribute("data-dropzone-no",this.dropzoneNo);var handles=this.shape.getHandlePositions();if(null!==handles){var moveHandle=Shapes.createSvgElement(this.svgEl,"circle");moveHandle.setAttribute("cx",handles.moveHandle.x),moveHandle.setAttribute("cy",handles.moveHandle.y),moveHandle.setAttribute("r",7),moveHandle.setAttribute("class","handle move");for(var i=0;i
')},setOptionsForDragItemSelectors:function(){var selector,i,label,dragItemsOptions={0:""},noItems=dragDropForm.form.getFormValue("noitems",[]),selectedValues=[];for(i=1;i<=noItems;i++)""!==(label=dragDropForm.form.getMarkerText(i))&&(dragItemsOptions[i]=$("
").text(label).html());for(i=0;i'+dragItemsOptions[value]+"";selector.append(option);var optionnode=selector.find('option[value="'+value+'"]');if(0!==value)if(value!==selectedValues[i]){var noofdrags=dragDropForm.form.getFormValue("drags",[value-1,"noofdrags"]);if(0!==Number(noofdrags))for(var k in selectedValues)if(Number(selectedValues[k])===value){if(1===Number(noofdrags)){optionnode.attr("disabled",!0);break}noofdrags--}}else optionnode.attr("selected",!0)}dragDropForm.dropZones.length>0&&dragDropForm.dropZones[i].updateLabel()}},createShapes:function(){for(var dropzoneNo=0;dropzoneNo'),dropzoneNo=0;dropzoneNo
')},setOptionsForDragItemSelectors:function(){var selector,i,label,dragItemsOptions={0:""},noItems=dragDropForm.form.getFormValue("noitems",[]),selectedValues=[];for(i=1;i<=noItems;i++)""!==(label=dragDropForm.form.getMarkerText(i))&&(dragItemsOptions[i]=$("
").text(label).html());for(i=0;i'+dragItemsOptions[value]+"";selector.append(option);var optionnode=selector.find('option[value="'+value+'"]');if(0!==value)if(value!==selectedValues[i]){var noofdrags=dragDropForm.form.getFormValue("drags",[value-1,"noofdrags"]);if(0!==Number(noofdrags))for(var k in selectedValues)if(Number(selectedValues[k])===value){if(1===Number(noofdrags)){optionnode.attr("disabled",!0);break}noofdrags--}}else optionnode.attr("selected",!0)}dragDropForm.dropZones.length>0&&dragDropForm.dropZones[i].updateLabel()}},createShapes:function(){for(var dropzoneNo=0;dropzoneNo'),dropzoneNo=0;dropzoneNo.\n\n/**\n * This class provides the enhancements to the drag-drop marker editing form.\n *\n * @module qtype_ddmarker/form\n * @copyright 2018 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/dragdrop', 'qtype_ddmarker/shapes'], function($, dragDrop, Shapes) {\n\n \"use strict\";\n\n /**\n * Create the manager object that deals with keeping everything synchronised for one drop zone.\n *\n * @param {int} dropzoneNo the index of this drop zone in the form. 0, 1, ....\n * @constructor\n */\n function DropZoneManager(dropzoneNo) {\n this.dropzoneNo = dropzoneNo;\n this.svgEl = null;\n\n this.shape = Shapes.make(this.getShapeType(), this.getLabel());\n this.updateCoordinatesFromForm();\n }\n\n /**\n * Update the coordinates from a particular string.\n *\n * @param {SVGElement} [svg] the SVG element that is the preview.\n */\n DropZoneManager.prototype.updateCoordinatesFromForm = function(svg) {\n var coordinates = this.getCoordinates(),\n currentNumPoints = this.shape.getType() === 'polygon' && this.shape.points.length;\n if (this.shape.getCoordinates() === coordinates) {\n return;\n }\n // We don't need to scale the shape for editing form.\n if (!this.shape.parse(coordinates, 1)) {\n // Invalid coordinates. Don't update the preview.\n return;\n }\n\n if (this.shape.getType() === 'polygon' && currentNumPoints !== this.shape.points.length) {\n // Polygon, and size has changed.\n var currentyActive = this.isActive();\n this.removeFromSvg();\n if (svg) {\n this.addToSvg(svg);\n if (currentyActive) {\n this.setActive();\n }\n }\n } else {\n // Simple update.\n this.updateSvgEl();\n }\n // Update the rounded coordinates if needed.\n this.setCoordinatesInForm();\n };\n\n /**\n * Update the label.\n */\n DropZoneManager.prototype.updateLabel = function() {\n var label = this.getLabel();\n if (this.shape.label !== label) {\n this.shape.label = label;\n this.updateSvgEl();\n }\n };\n\n /**\n * Handle if the type of shape has changed.\n *\n * @param {SVGElement} [svg] an SVG element to add this new shape to.\n */\n DropZoneManager.prototype.changeShape = function(svg) {\n var newShapeType = this.getShapeType(),\n currentyActive = this.isActive();\n\n if (newShapeType === this.shape.getType()) {\n return;\n }\n\n // It has really changed.\n this.removeFromSvg();\n this.shape = Shapes.getSimilar(newShapeType, this.shape);\n if (svg) {\n this.addToSvg(svg);\n if (currentyActive) {\n this.setActive();\n }\n }\n this.setCoordinatesInForm();\n };\n\n /**\n * Add this drop zone to an SVG graphic.\n *\n * @param {SVGElement} svg the SVG image to which to add this drop zone.\n */\n DropZoneManager.prototype.addToSvg = function(svg) {\n if (this.svgEl !== null) {\n throw new Error('this.svgEl already set');\n }\n this.svgEl = this.shape.makeSvg(svg);\n if (!this.svgEl) {\n return;\n }\n this.svgEl.setAttribute('class', 'dropzone');\n this.svgEl.setAttribute('data-dropzone-no', this.dropzoneNo);\n\n // Add handles.\n var handles = this.shape.getHandlePositions();\n if (handles === null) {\n return;\n }\n\n var moveHandle = Shapes.createSvgElement(this.svgEl, 'circle');\n moveHandle.setAttribute('cx', handles.moveHandle.x);\n moveHandle.setAttribute('cy', handles.moveHandle.y);\n moveHandle.setAttribute('r', 7);\n moveHandle.setAttribute('class', 'handle move');\n\n for (var i = 0; i < handles.editHandles.length; ++i) {\n this.makeEditHandle(i, handles.editHandles[i]);\n }\n };\n\n /**\n * Add a new edit handle.\n *\n * @param {int} index the handle index.\n * @param {Point} point the point at which to add the handle.\n */\n DropZoneManager.prototype.makeEditHandle = function(index, point) {\n var editHandle = Shapes.createSvgElement(this.svgEl, 'rect');\n editHandle.setAttribute('x', point.x - 6);\n editHandle.setAttribute('y', point.y - 6);\n editHandle.setAttribute('width', 11);\n editHandle.setAttribute('height', 11);\n editHandle.setAttribute('class', 'handle edit');\n editHandle.setAttribute('data-edit-handle-no', index);\n };\n\n /**\n * Remove this drop zone from an SVG image.\n */\n DropZoneManager.prototype.removeFromSvg = function() {\n if (this.svgEl !== null) {\n this.svgEl.parentNode.removeChild(this.svgEl);\n this.svgEl = null;\n }\n };\n\n /**\n * Update the shape of this drop zone (but not type) in an SVG image.\n */\n DropZoneManager.prototype.updateSvgEl = function() {\n if (this.svgEl === null) {\n return;\n }\n\n this.shape.updateSvg(this.svgEl);\n\n // Adjust handles.\n var handles = this.shape.getHandlePositions();\n if (handles === null) {\n return;\n }\n\n // Move handle.\n // The shape + its label are the first two children of svgEl.\n // Then come the move handle followed by the edit handles.\n this.svgEl.childNodes[2].setAttribute('cx', handles.moveHandle.x);\n this.svgEl.childNodes[2].setAttribute('cy', handles.moveHandle.y);\n\n // Edit handles.\n for (var i = 0; i < handles.editHandles.length; ++i) {\n this.svgEl.childNodes[3 + i].setAttribute('x', handles.editHandles[i].x - 6);\n this.svgEl.childNodes[3 + i].setAttribute('y', handles.editHandles[i].y - 6);\n }\n };\n\n /**\n * Find out of this drop zone is currently being edited.\n *\n * @return {boolean} true if it is.\n */\n DropZoneManager.prototype.isActive = function() {\n return this.svgEl !== null && this.svgEl.getAttribute('class').match(/\\bactive\\b/);\n };\n\n /**\n * Set this drop zone as being edited.\n */\n DropZoneManager.prototype.setActive = function() {\n // Move this one to last, so that it is always on top.\n // (Otherwise the handles may not be able to receive events.)\n var parent = this.svgEl.parentNode;\n parent.removeChild(this.svgEl);\n parent.appendChild(this.svgEl);\n this.svgEl.setAttribute('class', this.svgEl.getAttribute('class') + ' active');\n };\n\n /**\n * Set the coordinates in the form to match the current shape.\n */\n DropZoneManager.prototype.setCoordinatesInForm = function() {\n dragDropForm.form.setFormValue('drops', [this.dropzoneNo, 'coords'], this.shape.getCoordinates());\n };\n\n /**\n * Returns the coordinates for a drop zone from the text input in the form.\n * @returns {string} the coordinates.\n */\n DropZoneManager.prototype.getCoordinates = function() {\n return dragDropForm.form.getFormValue('drops', [this.dropzoneNo, 'coords']).replace(/\\s*/g, '');\n };\n\n /**\n * Returns the selected marker number from the dropdown in the form.\n * @returns {int} choice number.\n */\n DropZoneManager.prototype.getChoiceNo = function() {\n return dragDropForm.form.getFormValue('drops', [this.dropzoneNo, 'choice']);\n };\n\n /**\n * Returns the selected marker number from the dropdown in the form.\n * @returns {String} marker label text.\n */\n DropZoneManager.prototype.getLabel = function() {\n return dragDropForm.form.getMarkerText(this.getChoiceNo());\n };\n\n\n /**\n * Returns the selected type of shape in the form.\n * @returns {String} 'circle', 'rectangle' or 'polygon'.\n */\n DropZoneManager.prototype.getShapeType = function() {\n return dragDropForm.form.getFormValue('drops', [this.dropzoneNo, 'shape']);\n };\n\n /**\n * Start responding to dragging the move handle.\n * @param {Event} e Event object\n */\n DropZoneManager.prototype.handleMove = function(e) {\n var info = dragDrop.prepare(e);\n if (!info.start) {\n return;\n }\n\n var movingDropZone = this,\n lastX = info.x,\n lastY = info.y,\n dragProxy = this.makeDragProxy(info.x, info.y),\n bgImg = $('fieldset#id_previewareaheader .dropbackground'),\n maxX = bgImg.width(),\n maxY = bgImg.height();\n\n dragDrop.start(e, $(dragProxy), function(pageX, pageY) {\n movingDropZone.shape.move(pageX - lastX, pageY - lastY, maxX, maxY);\n lastX = pageX;\n lastY = pageY;\n movingDropZone.updateSvgEl();\n movingDropZone.setCoordinatesInForm();\n }, function() {\n document.body.removeChild(dragProxy);\n });\n };\n\n /**\n * Start responding to dragging the move handle.\n * @param {Event} e Event object\n * @param {int} handleIndex\n * @param {SVGElement} [svg] an SVG element to add this new shape to.\n */\n DropZoneManager.prototype.handleEdit = function(e, handleIndex, svg) {\n var info = dragDrop.prepare(e);\n if (!info.start) {\n return;\n }\n\n // For polygons, CTRL + drag adds a new point.\n if (this.shape.getType() === 'polygon' && (e.ctrlKey || e.metaKey)) {\n this.shape.addNewPointAfter(handleIndex);\n this.removeFromSvg();\n this.addToSvg(svg);\n this.setActive();\n }\n\n var changingDropZone = this,\n lastX = info.x,\n lastY = info.y,\n dragProxy = this.makeDragProxy(info.x, info.y),\n bgImg = $('fieldset#id_previewareaheader .dropbackground'),\n maxX = bgImg.width(),\n maxY = bgImg.height();\n\n dragDrop.start(e, $(dragProxy), function(pageX, pageY) {\n changingDropZone.shape.edit(handleIndex, pageX - lastX, pageY - lastY, maxX, maxY);\n lastX = pageX;\n lastY = pageY;\n changingDropZone.updateSvgEl();\n changingDropZone.setCoordinatesInForm();\n }, function() {\n document.body.removeChild(dragProxy);\n changingDropZone.shape.normalizeShape();\n changingDropZone.updateSvgEl();\n changingDropZone.setCoordinatesInForm();\n });\n };\n\n /**\n * Make an invisible drag proxy.\n *\n * @param {int} x x position .\n * @param {int} y y position.\n * @returns {HTMLElement} the drag proxy.\n */\n DropZoneManager.prototype.makeDragProxy = function(x, y) {\n var dragProxy = document.createElement('div');\n dragProxy.style.position = 'absolute';\n dragProxy.style.top = y + 'px';\n dragProxy.style.left = x + 'px';\n dragProxy.style.width = '1px';\n dragProxy.style.height = '1px';\n document.body.appendChild(dragProxy);\n return dragProxy;\n };\n\n /**\n * Singleton object for managing all the parts of the form.\n */\n var dragDropForm = {\n\n /**\n * @var {object} for interacting with the file pickers.\n */\n fp: null, // Object containing functions associated with the file picker.\n\n /**\n * @var {int} the number of drop-zones on the form.\n */\n noDropZones: null,\n\n /**\n * @var {DropZoneManager[]} the drop zones in the preview, indexed by drop zone number.\n */\n dropZones: [],\n\n /**\n * Initialise the form.\n */\n init: function() {\n dragDropForm.fp = dragDropForm.filePickers();\n dragDropForm.noDropZones = dragDropForm.form.getFormValue('nodropzone', []);\n dragDropForm.setupPreviewArea();\n dragDropForm.setOptionsForDragItemSelectors();\n dragDropForm.createShapes();\n dragDropForm.setupEventHandlers();\n dragDropForm.waitForFilePickerToInitialise();\n },\n\n /**\n * Add html for the preview area.\n */\n setupPreviewArea: function() {\n $('fieldset#id_previewareaheader div.fcontainer').append(\n '
' +\n '
' +\n ' ' +\n '
' +\n '
' +\n '
' +\n '
');\n },\n\n /**\n * When a new marker is added this function updates the Marker dropdown controls in Drop zones.\n */\n setOptionsForDragItemSelectors: function() {\n var dragItemsOptions = {'0': ''};\n var noItems = dragDropForm.form.getFormValue('noitems', []);\n var selectedValues = [];\n var selector;\n var i, label;\n for (i = 1; i <= noItems; i++) {\n label = dragDropForm.form.getMarkerText(i);\n if (label !== \"\") {\n // HTML escape the label.\n dragItemsOptions[i] = $('
').text(label).html();\n }\n }\n // Get all the currently selected drags for each drop.\n for (i = 0; i < dragDropForm.noDropZones; i++) {\n selector = $('#id_drops_' + i + '_choice');\n selectedValues[i] = Number(selector.val());\n }\n for (i = 0; i < dragDropForm.noDropZones; i++) {\n selector = $('#id_drops_' + i + '_choice');\n // Remove all options for drag choice.\n selector.find('option').remove();\n // And recreate the options.\n for (var value in dragItemsOptions) {\n value = Number(value);\n var option = '';\n selector.append(option);\n var optionnode = selector.find('option[value=\"' + value + '\"]');\n\n\n if (value === 0) {\n continue; // The 'no item' option is always selectable.\n }\n\n // Is this the currently selected value?\n if (value === selectedValues[i]) {\n optionnode.attr('selected', true);\n continue; // If it s selected, we must leave it enabled.\n }\n\n // Count how many times it is used, and if necessary, disable.\n var noofdrags = dragDropForm.form.getFormValue('drags', [value - 1, 'noofdrags']);\n if (Number(noofdrags) === 0) { // 'noofdrags === 0' means infinite.\n continue; // Nothing to check.\n }\n\n // Go through all selected values in drop downs.\n for (var k in selectedValues) {\n if (Number(selectedValues[k]) !== value) {\n continue;\n }\n\n // Count down 'noofdrags' and if reach zero then set disabled option for this drag item.\n if (Number(noofdrags) === 1) {\n optionnode.attr('disabled', true);\n break;\n } else {\n noofdrags--;\n }\n }\n }\n\n if (dragDropForm.dropZones.length > 0) {\n dragDropForm.dropZones[i].updateLabel();\n }\n }\n },\n\n /**\n * Create the shape representation of each dropZone.\n */\n createShapes: function() {\n for (var dropzoneNo = 0; dropzoneNo < dragDropForm.noDropZones; dropzoneNo++) {\n dragDropForm.dropZones[dropzoneNo] = new DropZoneManager(dropzoneNo);\n }\n },\n\n /**\n * Events linked to form actions.\n */\n setupEventHandlers: function() {\n // Changes to labels in the Markers section.\n $('fieldset#id_draggableitemheader').on('change input', 'input, select', function() {\n dragDropForm.setOptionsForDragItemSelectors();\n });\n\n // Changes to Drop zones section: shape, coordinates and marker.\n $('fieldset#id_dropzoneheader').on('change input', 'input, select', function(e) {\n var ids = e.currentTarget.name.match(/^drops\\[(\\d+)]\\[([a-z]*)]$/);\n if (!ids) {\n return;\n }\n\n var dropzoneNo = ids[1],\n inputType = ids[2],\n dropZone = dragDropForm.dropZones[dropzoneNo];\n\n switch (inputType) {\n case 'shape':\n dropZone.changeShape(dragDropForm.form.getSvg());\n break;\n\n case 'coords':\n dropZone.updateCoordinatesFromForm(dragDropForm.form.getSvg());\n break;\n\n case 'choice':\n dropZone.updateLabel();\n break;\n }\n });\n\n // Click to toggle graphical editing.\n var previewArea = $('fieldset#id_previewareaheader');\n previewArea.on('click', 'g.dropzone', function(e) {\n var dropzoneNo = $(e.currentTarget).data('dropzone-no'),\n currentlyActive = dragDropForm.dropZones[dropzoneNo].isActive();\n\n $(dragDropForm.form.getSvg()).find('.dropzone.active').removeClass('active');\n\n if (!currentlyActive) {\n dragDropForm.dropZones[dropzoneNo].setActive();\n }\n });\n\n // Drag start on a move handle.\n previewArea.on('mousedown touchstart', '.dropzone .handle.move', function(e) {\n var dropzoneNo = $(e.currentTarget).closest('g').data('dropzoneNo');\n\n dragDropForm.dropZones[dropzoneNo].handleMove(e);\n });\n\n // Drag start on a move handle.\n previewArea.on('mousedown touchstart', '.dropzone .handle.edit', function(e) {\n var dropzoneNo = $(e.currentTarget).closest('g').data('dropzoneNo'),\n handleIndex = e.currentTarget.getAttribute('data-edit-handle-no');\n\n dragDropForm.dropZones[dropzoneNo].handleEdit(e, handleIndex, dragDropForm.form.getSvg());\n });\n },\n\n /**\n * Prevents adding drop zones until the preview background image is ready to load.\n */\n waitForFilePickerToInitialise: function() {\n if (dragDropForm.fp.file('bgimage').href === null) {\n // It would be better to use an onload or onchange event rather than this timeout.\n // Unfortunately attempts to do this early are overwritten by filepicker during its loading.\n setTimeout(dragDropForm.waitForFilePickerToInitialise, 1000);\n return;\n }\n\n // From now on, when a new file gets loaded into the filepicker, update the preview.\n // This is not in the setupEventHandlers section as it needs to be delayed until\n // after filepicker's javascript has finished.\n $('form.mform[data-qtype=\"ddmarker\"]').on('change', '#id_bgimage', dragDropForm.loadPreviewImage);\n\n dragDropForm.loadPreviewImage();\n },\n\n /**\n * Loads the preview background image.\n */\n loadPreviewImage: function() {\n $('fieldset#id_previewareaheader .dropbackground')\n .one('load', dragDropForm.afterPreviewImageLoaded)\n .attr('src', dragDropForm.fp.file('bgimage').href);\n },\n\n /**\n * Functions to run after background image loaded.\n */\n afterPreviewImageLoaded: function() {\n var bgImg = $('fieldset#id_previewareaheader .dropbackground');\n // Place the dropzone area over the background image (adding one to account for the border).\n $('#ddm-dropzone').css('position', 'relative').css('top', (bgImg.height() + 1) * -1);\n $('#ddm-droparea').css('height', bgImg.height() + 20);\n dragDropForm.updateSvgDisplay();\n },\n\n /**\n * Draws or re-draws all dropzones in the preview area based on form data.\n * Call this function when there is a change in the form data.\n */\n updateSvgDisplay: function() {\n var bgImg = $('fieldset#id_previewareaheader .dropbackground'),\n dropzoneNo;\n\n if (dragDropForm.form.getSvg()) {\n // Already exists, just need to be updated.\n for (dropzoneNo = 0; dropzoneNo < dragDropForm.noDropZones; dropzoneNo++) {\n dragDropForm.dropZones[dropzoneNo].updateSvgEl();\n }\n\n } else {\n // Create.\n $('#ddm-dropzone').html('');\n for (dropzoneNo = 0; dropzoneNo < dragDropForm.noDropZones; dropzoneNo++) {\n dragDropForm.dropZones[dropzoneNo].addToSvg(dragDropForm.form.getSvg());\n }\n }\n },\n\n /**\n * Helper to make it easy to work with form elements with names like \"drops[0][shape]\".\n */\n form: {\n /**\n * Returns the label text for a marker.\n * @param {int} markerNo\n * @returns {string} Marker text\n */\n getMarkerText: function(markerNo) {\n if (Number(markerNo) !== 0) {\n var label = dragDropForm.form.getFormValue('drags', [markerNo - 1, 'label']);\n return label.replace(new RegExp(\"^\\\\s*(.*)\\\\s*$\"), \"$1\");\n } else {\n return '';\n }\n },\n\n /**\n * Get the SVG element, if there is one, otherwise return null.\n *\n * @returns {SVGElement|null} the SVG element or null.\n */\n getSvg: function() {\n var svg = $('fieldset#id_previewareaheader svg');\n if (svg.length === 0) {\n return null;\n } else {\n return svg[0];\n }\n },\n\n toNameWithIndex: function(name, indexes) {\n var indexString = name;\n for (var i = 0; i < indexes.length; i++) {\n indexString = indexString + '[' + indexes[i] + ']';\n }\n return indexString;\n },\n\n getEl: function(name, indexes) {\n var form = $('form.mform[data-qtype=\"ddmarker\"]')[0];\n return form.elements[this.toNameWithIndex(name, indexes)];\n },\n\n /**\n * Helper to get the value of a form elements with name like \"drops[0][shape]\".\n *\n * @param {String} name the base name, e.g. 'drops'.\n * @param {String[]} indexes the indexes, e.g. ['0', 'shape'].\n * @return {String} the value of that field.\n */\n getFormValue: function(name, indexes) {\n var el = this.getEl(name, indexes);\n if (el.type === 'checkbox') {\n return el.checked;\n } else {\n return el.value;\n }\n },\n\n /**\n * Helper to get the value of a form elements with name like \"drops[0][shape]\".\n *\n * @param {String} name the base name, e.g. 'drops'.\n * @param {String[]} indexes the indexes, e.g. ['0', 'shape'].\n * @param {String} value the value to set.\n */\n setFormValue: function(name, indexes, value) {\n var el = this.getEl(name, indexes);\n if (el.type === 'checkbox') {\n el.checked = value;\n } else {\n el.value = value;\n }\n }\n },\n\n /**\n * Utility to get the file name and url from the filepicker.\n * @returns {Object} object containing functions {file, name}\n */\n filePickers: function() {\n var draftItemIdsToName;\n var nameToParentNode;\n if (draftItemIdsToName === undefined) {\n draftItemIdsToName = {};\n nameToParentNode = {};\n $('form.mform input.filepickerhidden').each(function(key, filepicker) {\n draftItemIdsToName[filepicker.value] = filepicker.name;\n nameToParentNode[filepicker.name] = filepicker.parentNode;\n });\n }\n return {\n file: function(name) {\n var fileAnchor = $(nameToParentNode[name]).find('div.filepicker-filelist a');\n if (fileAnchor.length) {\n return {href: fileAnchor.get(0).href, name: fileAnchor.get(0).innerHTML};\n } else {\n return {href: null, name: null};\n }\n },\n name: function(draftitemid) {\n return draftItemIdsToName[draftitemid];\n }\n };\n }\n };\n\n /**\n * @alias module:qtype_ddmarker/form\n */\n return {\n /**\n * Initialise the form javascript features.\n * @param {Object} maxBgimageSize object with two properties: width and height.\n */\n init: dragDropForm.init\n };\n});\n"],"names":["define","$","dragDrop","Shapes","DropZoneManager","dropzoneNo","svgEl","shape","make","this","getShapeType","getLabel","updateCoordinatesFromForm","prototype","svg","coordinates","getCoordinates","currentNumPoints","getType","points","length","parse","currentyActive","isActive","removeFromSvg","addToSvg","setActive","updateSvgEl","setCoordinatesInForm","updateLabel","label","changeShape","newShapeType","getSimilar","Error","makeSvg","setAttribute","handles","getHandlePositions","moveHandle","createSvgElement","x","y","i","editHandles","makeEditHandle","index","point","editHandle","parentNode","removeChild","updateSvg","childNodes","getAttribute","match","parent","appendChild","dragDropForm","form","setFormValue","getFormValue","replace","getChoiceNo","getMarkerText","handleMove","e","info","prepare","start","movingDropZone","lastX","lastY","dragProxy","makeDragProxy","bgImg","maxX","width","maxY","height","pageX","pageY","move","document","body","handleEdit","handleIndex","ctrlKey","metaKey","addNewPointAfter","changingDropZone","edit","normalizeShape","createElement","style","position","top","left","fp","noDropZones","dropZones","init","filePickers","setupPreviewArea","setOptionsForDragItemSelectors","createShapes","setupEventHandlers","waitForFilePickerToInitialise","append","selector","dragItemsOptions","noItems","selectedValues","text","html","Number","val","value","find","remove","option","optionnode","noofdrags","k","attr","on","ids","currentTarget","name","inputType","dropZone","getSvg","previewArea","data","currentlyActive","removeClass","closest","file","href","loadPreviewImage","setTimeout","one","afterPreviewImageLoaded","css","updateSvgDisplay","outerWidth","outerHeight","markerNo","RegExp","toNameWithIndex","indexes","indexString","getEl","elements","el","type","checked","draftItemIdsToName","nameToParentNode","undefined","each","key","filepicker","fileAnchor","get","innerHTML","draftitemid"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"form.min.js","sources":["../src/form.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This class provides the enhancements to the drag-drop marker editing form.\n *\n * @module qtype_ddmarker/form\n * @copyright 2018 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/dragdrop', 'qtype_ddmarker/shapes'], function($, dragDrop, Shapes) {\n\n \"use strict\";\n\n /**\n * Create the manager object that deals with keeping everything synchronised for one drop zone.\n *\n * @param {int} dropzoneNo the index of this drop zone in the form. 0, 1, ....\n * @constructor\n */\n function DropZoneManager(dropzoneNo) {\n this.dropzoneNo = dropzoneNo;\n this.svgEl = null;\n\n this.shape = Shapes.make(this.getShapeType(), this.getLabel());\n this.updateCoordinatesFromForm();\n }\n\n /**\n * Update the coordinates from a particular string.\n *\n * @param {SVGElement} [svg] the SVG element that is the preview.\n */\n DropZoneManager.prototype.updateCoordinatesFromForm = function(svg) {\n var coordinates = this.getCoordinates(),\n currentNumPoints = this.shape.getType() === 'polygon' && this.shape.points.length;\n if (this.shape.getCoordinates() === coordinates) {\n return;\n }\n // We don't need to scale the shape for editing form.\n if (!this.shape.parse(coordinates, 1)) {\n // Invalid coordinates. Don't update the preview.\n return;\n }\n\n if (this.shape.getType() === 'polygon' && currentNumPoints !== this.shape.points.length) {\n // Polygon, and size has changed.\n var currentyActive = this.isActive();\n this.removeFromSvg();\n if (svg) {\n this.addToSvg(svg);\n if (currentyActive) {\n this.setActive();\n }\n }\n } else {\n // Simple update.\n this.updateSvgEl();\n }\n // Update the rounded coordinates if needed.\n this.setCoordinatesInForm();\n };\n\n /**\n * Update the label.\n */\n DropZoneManager.prototype.updateLabel = function() {\n var label = this.getLabel();\n if (this.shape.label !== label) {\n this.shape.label = label;\n this.updateSvgEl();\n }\n };\n\n /**\n * Handle if the type of shape has changed.\n *\n * @param {SVGElement} [svg] an SVG element to add this new shape to.\n */\n DropZoneManager.prototype.changeShape = function(svg) {\n var newShapeType = this.getShapeType(),\n currentyActive = this.isActive();\n\n if (newShapeType === this.shape.getType()) {\n return;\n }\n\n // It has really changed.\n this.removeFromSvg();\n this.shape = Shapes.getSimilar(newShapeType, this.shape);\n if (svg) {\n this.addToSvg(svg);\n if (currentyActive) {\n this.setActive();\n }\n }\n this.setCoordinatesInForm();\n };\n\n /**\n * Add this drop zone to an SVG graphic.\n *\n * @param {SVGElement} svg the SVG image to which to add this drop zone.\n */\n DropZoneManager.prototype.addToSvg = function(svg) {\n if (this.svgEl !== null) {\n throw new Error('this.svgEl already set');\n }\n this.svgEl = this.shape.makeSvg(svg);\n if (!this.svgEl) {\n return;\n }\n this.svgEl.setAttribute('class', 'dropzone');\n this.svgEl.setAttribute('data-dropzone-no', this.dropzoneNo);\n\n // Add handles.\n var handles = this.shape.getHandlePositions();\n if (handles === null) {\n return;\n }\n\n var moveHandle = Shapes.createSvgElement(this.svgEl, 'circle');\n moveHandle.setAttribute('cx', handles.moveHandle.x);\n moveHandle.setAttribute('cy', handles.moveHandle.y);\n moveHandle.setAttribute('r', 7);\n moveHandle.setAttribute('class', 'handle move');\n\n for (var i = 0; i < handles.editHandles.length; ++i) {\n this.makeEditHandle(i, handles.editHandles[i]);\n }\n };\n\n /**\n * Add a new edit handle.\n *\n * @param {int} index the handle index.\n * @param {Point} point the point at which to add the handle.\n */\n DropZoneManager.prototype.makeEditHandle = function(index, point) {\n var editHandle = Shapes.createSvgElement(this.svgEl, 'rect');\n editHandle.setAttribute('x', point.x - 6);\n editHandle.setAttribute('y', point.y - 6);\n editHandle.setAttribute('width', 11);\n editHandle.setAttribute('height', 11);\n editHandle.setAttribute('class', 'handle edit');\n editHandle.setAttribute('data-edit-handle-no', index);\n };\n\n /**\n * Remove this drop zone from an SVG image.\n */\n DropZoneManager.prototype.removeFromSvg = function() {\n if (this.svgEl !== null) {\n this.svgEl.parentNode.removeChild(this.svgEl);\n this.svgEl = null;\n }\n };\n\n /**\n * Update the shape of this drop zone (but not type) in an SVG image.\n */\n DropZoneManager.prototype.updateSvgEl = function() {\n if (this.svgEl === null) {\n return;\n }\n\n this.shape.updateSvg(this.svgEl);\n\n // Adjust handles.\n var handles = this.shape.getHandlePositions();\n if (handles === null) {\n return;\n }\n\n // Move handle.\n // The shape + its label are the first two children of svgEl.\n // Then come the move handle followed by the edit handles.\n this.svgEl.childNodes[2].setAttribute('cx', handles.moveHandle.x);\n this.svgEl.childNodes[2].setAttribute('cy', handles.moveHandle.y);\n\n // Edit handles.\n for (var i = 0; i < handles.editHandles.length; ++i) {\n this.svgEl.childNodes[3 + i].setAttribute('x', handles.editHandles[i].x - 6);\n this.svgEl.childNodes[3 + i].setAttribute('y', handles.editHandles[i].y - 6);\n }\n };\n\n /**\n * Find out of this drop zone is currently being edited.\n *\n * @return {boolean} true if it is.\n */\n DropZoneManager.prototype.isActive = function() {\n return this.svgEl !== null && this.svgEl.getAttribute('class').match(/\\bactive\\b/);\n };\n\n /**\n * Set this drop zone as being edited.\n */\n DropZoneManager.prototype.setActive = function() {\n // Move this one to last, so that it is always on top.\n // (Otherwise the handles may not be able to receive events.)\n var parent = this.svgEl.parentNode;\n parent.removeChild(this.svgEl);\n parent.appendChild(this.svgEl);\n this.svgEl.setAttribute('class', this.svgEl.getAttribute('class') + ' active');\n };\n\n /**\n * Set the coordinates in the form to match the current shape.\n */\n DropZoneManager.prototype.setCoordinatesInForm = function() {\n dragDropForm.form.setFormValue('drops', [this.dropzoneNo, 'coords'], this.shape.getCoordinates());\n };\n\n /**\n * Returns the coordinates for a drop zone from the text input in the form.\n * @returns {string} the coordinates.\n */\n DropZoneManager.prototype.getCoordinates = function() {\n return dragDropForm.form.getFormValue('drops', [this.dropzoneNo, 'coords']).replace(/\\s*/g, '');\n };\n\n /**\n * Returns the selected marker number from the dropdown in the form.\n * @returns {int} choice number.\n */\n DropZoneManager.prototype.getChoiceNo = function() {\n return dragDropForm.form.getFormValue('drops', [this.dropzoneNo, 'choice']);\n };\n\n /**\n * Returns the selected marker number from the dropdown in the form.\n * @returns {String} marker label text.\n */\n DropZoneManager.prototype.getLabel = function() {\n return dragDropForm.form.getMarkerText(this.getChoiceNo());\n };\n\n\n /**\n * Returns the selected type of shape in the form.\n * @returns {String} 'circle', 'rectangle' or 'polygon'.\n */\n DropZoneManager.prototype.getShapeType = function() {\n return dragDropForm.form.getFormValue('drops', [this.dropzoneNo, 'shape']);\n };\n\n /**\n * Start responding to dragging the move handle.\n * @param {Event} e Event object\n */\n DropZoneManager.prototype.handleMove = function(e) {\n var info = dragDrop.prepare(e);\n if (!info.start) {\n return;\n }\n\n var movingDropZone = this,\n lastX = info.x,\n lastY = info.y,\n dragProxy = this.makeDragProxy(info.x, info.y),\n bgImg = $('fieldset#id_previewareaheader .dropbackground'),\n maxX = bgImg.width(),\n maxY = bgImg.height();\n\n dragDrop.start(e, $(dragProxy), function(pageX, pageY) {\n movingDropZone.shape.move(pageX - lastX, pageY - lastY, maxX, maxY);\n lastX = pageX;\n lastY = pageY;\n movingDropZone.updateSvgEl();\n movingDropZone.setCoordinatesInForm();\n }, function() {\n document.body.removeChild(dragProxy);\n });\n };\n\n /**\n * Start responding to dragging the move handle.\n * @param {Event} e Event object\n * @param {int} handleIndex\n * @param {SVGElement} [svg] an SVG element to add this new shape to.\n */\n DropZoneManager.prototype.handleEdit = function(e, handleIndex, svg) {\n var info = dragDrop.prepare(e);\n if (!info.start) {\n return;\n }\n\n // For polygons, CTRL + drag adds a new point.\n if (this.shape.getType() === 'polygon' && (e.ctrlKey || e.metaKey)) {\n this.shape.addNewPointAfter(handleIndex);\n this.removeFromSvg();\n this.addToSvg(svg);\n this.setActive();\n }\n\n var changingDropZone = this,\n lastX = info.x,\n lastY = info.y,\n dragProxy = this.makeDragProxy(info.x, info.y),\n bgImg = $('fieldset#id_previewareaheader .dropbackground'),\n maxX = bgImg.width(),\n maxY = bgImg.height();\n\n dragDrop.start(e, $(dragProxy), function(pageX, pageY) {\n changingDropZone.shape.edit(handleIndex, pageX - lastX, pageY - lastY, maxX, maxY);\n lastX = pageX;\n lastY = pageY;\n changingDropZone.updateSvgEl();\n changingDropZone.setCoordinatesInForm();\n }, function() {\n document.body.removeChild(dragProxy);\n changingDropZone.shape.normalizeShape();\n changingDropZone.updateSvgEl();\n changingDropZone.setCoordinatesInForm();\n });\n };\n\n /**\n * Make an invisible drag proxy.\n *\n * @param {int} x x position .\n * @param {int} y y position.\n * @returns {HTMLElement} the drag proxy.\n */\n DropZoneManager.prototype.makeDragProxy = function(x, y) {\n var dragProxy = document.createElement('div');\n dragProxy.style.position = 'absolute';\n dragProxy.style.top = y + 'px';\n dragProxy.style.left = x + 'px';\n dragProxy.style.width = '1px';\n dragProxy.style.height = '1px';\n document.body.appendChild(dragProxy);\n return dragProxy;\n };\n\n /**\n * Singleton object for managing all the parts of the form.\n */\n var dragDropForm = {\n\n /**\n * @var {object} for interacting with the file pickers.\n */\n fp: null, // Object containing functions associated with the file picker.\n\n /**\n * @var {int} the number of drop-zones on the form.\n */\n noDropZones: null,\n\n /**\n * @var {DropZoneManager[]} the drop zones in the preview, indexed by drop zone number.\n */\n dropZones: [],\n\n /**\n * Initialise the form.\n */\n init: function() {\n dragDropForm.fp = dragDropForm.filePickers();\n dragDropForm.noDropZones = dragDropForm.form.getFormValue('nodropzone', []);\n dragDropForm.setOptionsForDragItemSelectors();\n dragDropForm.createShapes();\n dragDropForm.setupEventHandlers();\n dragDropForm.waitForFilePickerToInitialise();\n },\n\n /**\n * Add html for the preview area.\n */\n setupPreviewArea: function() {\n $('fieldset#id_previewareaheader div.fcontainer').append(\n '
' +\n '
' +\n ' ' +\n '
' +\n '
' +\n '
' +\n '
');\n },\n\n /**\n * When a new marker is added this function updates the Marker dropdown controls in Drop zones.\n */\n setOptionsForDragItemSelectors: function() {\n var dragItemsOptions = {'0': ''};\n var noItems = dragDropForm.form.getFormValue('noitems', []);\n var selectedValues = [];\n var selector;\n var i, label;\n for (i = 1; i <= noItems; i++) {\n label = dragDropForm.form.getMarkerText(i);\n if (label !== \"\") {\n // HTML escape the label.\n dragItemsOptions[i] = $('
').text(label).html();\n }\n }\n // Get all the currently selected drags for each drop.\n for (i = 0; i < dragDropForm.noDropZones; i++) {\n selector = $('#id_drops_' + i + '_choice');\n selectedValues[i] = Number(selector.val());\n }\n for (i = 0; i < dragDropForm.noDropZones; i++) {\n selector = $('#id_drops_' + i + '_choice');\n // Remove all options for drag choice.\n selector.find('option').remove();\n // And recreate the options.\n for (var value in dragItemsOptions) {\n value = Number(value);\n var option = '';\n selector.append(option);\n var optionnode = selector.find('option[value=\"' + value + '\"]');\n\n\n if (value === 0) {\n continue; // The 'no item' option is always selectable.\n }\n\n // Is this the currently selected value?\n if (value === selectedValues[i]) {\n optionnode.attr('selected', true);\n continue; // If it s selected, we must leave it enabled.\n }\n\n // Count how many times it is used, and if necessary, disable.\n var noofdrags = dragDropForm.form.getFormValue('drags', [value - 1, 'noofdrags']);\n if (Number(noofdrags) === 0) { // 'noofdrags === 0' means infinite.\n continue; // Nothing to check.\n }\n\n // Go through all selected values in drop downs.\n for (var k in selectedValues) {\n if (Number(selectedValues[k]) !== value) {\n continue;\n }\n\n // Count down 'noofdrags' and if reach zero then set disabled option for this drag item.\n if (Number(noofdrags) === 1) {\n optionnode.attr('disabled', true);\n break;\n } else {\n noofdrags--;\n }\n }\n }\n\n if (dragDropForm.dropZones.length > 0) {\n dragDropForm.dropZones[i].updateLabel();\n }\n }\n },\n\n /**\n * Create the shape representation of each dropZone.\n */\n createShapes: function() {\n for (var dropzoneNo = 0; dropzoneNo < dragDropForm.noDropZones; dropzoneNo++) {\n dragDropForm.dropZones[dropzoneNo] = new DropZoneManager(dropzoneNo);\n }\n },\n\n /**\n * Events linked to form actions.\n */\n setupEventHandlers: function() {\n // Changes to labels in the Markers section.\n $('fieldset#id_draggableitemheader').on('change input', 'input, select', function() {\n dragDropForm.setOptionsForDragItemSelectors();\n });\n\n // Changes to Drop zones section: shape, coordinates and marker.\n $('fieldset#id_dropzoneheader').on('change input', 'input, select', function(e) {\n var ids = e.currentTarget.name.match(/^drops\\[(\\d+)]\\[([a-z]*)]$/);\n if (!ids) {\n return;\n }\n\n var dropzoneNo = ids[1],\n inputType = ids[2],\n dropZone = dragDropForm.dropZones[dropzoneNo];\n\n switch (inputType) {\n case 'shape':\n dropZone.changeShape(dragDropForm.form.getSvg());\n break;\n\n case 'coords':\n dropZone.updateCoordinatesFromForm(dragDropForm.form.getSvg());\n break;\n\n case 'choice':\n dropZone.updateLabel();\n break;\n }\n });\n\n // Click to toggle graphical editing.\n var previewArea = $('fieldset#id_previewareaheader');\n previewArea.on('click', 'g.dropzone', function(e) {\n var dropzoneNo = $(e.currentTarget).data('dropzone-no'),\n currentlyActive = dragDropForm.dropZones[dropzoneNo].isActive();\n\n $(dragDropForm.form.getSvg()).find('.dropzone.active').removeClass('active');\n\n if (!currentlyActive) {\n dragDropForm.dropZones[dropzoneNo].setActive();\n }\n });\n\n // Drag start on a move handle.\n previewArea.on('mousedown touchstart', '.dropzone .handle.move', function(e) {\n var dropzoneNo = $(e.currentTarget).closest('g').data('dropzoneNo');\n\n dragDropForm.dropZones[dropzoneNo].handleMove(e);\n });\n\n // Drag start on a move handle.\n previewArea.on('mousedown touchstart', '.dropzone .handle.edit', function(e) {\n var dropzoneNo = $(e.currentTarget).closest('g').data('dropzoneNo'),\n handleIndex = e.currentTarget.getAttribute('data-edit-handle-no');\n\n dragDropForm.dropZones[dropzoneNo].handleEdit(e, handleIndex, dragDropForm.form.getSvg());\n });\n },\n\n /**\n * Prevents adding drop zones until the preview background image is ready to load.\n */\n waitForFilePickerToInitialise: function() {\n if (dragDropForm.fp.file('bgimage').href === null) {\n // It would be better to use an onload or onchange event rather than this timeout.\n // Unfortunately attempts to do this early are overwritten by filepicker during its loading.\n setTimeout(dragDropForm.waitForFilePickerToInitialise, 1000);\n return;\n }\n\n // From now on, when a new file gets loaded into the filepicker, update the preview.\n // This is not in the setupEventHandlers section as it needs to be delayed until\n // after filepicker's javascript has finished.\n $('form.mform[data-qtype=\"ddmarker\"]').on('change', '#id_bgimage', dragDropForm.loadPreviewImage);\n\n if ($('#ddm-droparea').length) {\n dragDropForm.loadPreviewImage();\n } else {\n // Setup preview area when the background image is uploaded the first time.\n dragDropForm.setupPreviewArea();\n dragDropForm.loadPreviewImage();\n }\n },\n\n /**\n * Loads the preview background image.\n */\n loadPreviewImage: function() {\n $('fieldset#id_previewareaheader .dropbackground')\n .one('load', dragDropForm.afterPreviewImageLoaded)\n .attr('src', dragDropForm.fp.file('bgimage').href);\n },\n\n /**\n * Functions to run after background image loaded.\n */\n afterPreviewImageLoaded: function() {\n var bgImg = $('fieldset#id_previewareaheader .dropbackground');\n // Place the dropzone area over the background image (adding one to account for the border).\n $('#ddm-dropzone').css('position', 'relative').css('top', (bgImg.height() + 1) * -1);\n $('#ddm-droparea').css('height', bgImg.height() + 20);\n dragDropForm.updateSvgDisplay();\n },\n\n /**\n * Draws or re-draws all dropzones in the preview area based on form data.\n * Call this function when there is a change in the form data.\n */\n updateSvgDisplay: function() {\n var bgImg = $('fieldset#id_previewareaheader .dropbackground'),\n dropzoneNo;\n\n if (dragDropForm.form.getSvg()) {\n // Already exists, just need to be updated.\n for (dropzoneNo = 0; dropzoneNo < dragDropForm.noDropZones; dropzoneNo++) {\n dragDropForm.dropZones[dropzoneNo].updateSvgEl();\n }\n\n } else {\n // Create.\n $('#ddm-dropzone').html('');\n for (dropzoneNo = 0; dropzoneNo < dragDropForm.noDropZones; dropzoneNo++) {\n dragDropForm.dropZones[dropzoneNo].addToSvg(dragDropForm.form.getSvg());\n }\n }\n },\n\n /**\n * Helper to make it easy to work with form elements with names like \"drops[0][shape]\".\n */\n form: {\n /**\n * Returns the label text for a marker.\n * @param {int} markerNo\n * @returns {string} Marker text\n */\n getMarkerText: function(markerNo) {\n if (Number(markerNo) !== 0) {\n var label = dragDropForm.form.getFormValue('drags', [markerNo - 1, 'label']);\n return label.replace(new RegExp(\"^\\\\s*(.*)\\\\s*$\"), \"$1\");\n } else {\n return '';\n }\n },\n\n /**\n * Get the SVG element, if there is one, otherwise return null.\n *\n * @returns {SVGElement|null} the SVG element or null.\n */\n getSvg: function() {\n var svg = $('fieldset#id_previewareaheader svg');\n if (svg.length === 0) {\n return null;\n } else {\n return svg[0];\n }\n },\n\n toNameWithIndex: function(name, indexes) {\n var indexString = name;\n for (var i = 0; i < indexes.length; i++) {\n indexString = indexString + '[' + indexes[i] + ']';\n }\n return indexString;\n },\n\n getEl: function(name, indexes) {\n var form = $('form.mform[data-qtype=\"ddmarker\"]')[0];\n return form.elements[this.toNameWithIndex(name, indexes)];\n },\n\n /**\n * Helper to get the value of a form elements with name like \"drops[0][shape]\".\n *\n * @param {String} name the base name, e.g. 'drops'.\n * @param {String[]} indexes the indexes, e.g. ['0', 'shape'].\n * @return {String} the value of that field.\n */\n getFormValue: function(name, indexes) {\n var el = this.getEl(name, indexes);\n if (el.type === 'checkbox') {\n return el.checked;\n } else {\n return el.value;\n }\n },\n\n /**\n * Helper to get the value of a form elements with name like \"drops[0][shape]\".\n *\n * @param {String} name the base name, e.g. 'drops'.\n * @param {String[]} indexes the indexes, e.g. ['0', 'shape'].\n * @param {String} value the value to set.\n */\n setFormValue: function(name, indexes, value) {\n var el = this.getEl(name, indexes);\n if (el.type === 'checkbox') {\n el.checked = value;\n } else {\n el.value = value;\n }\n }\n },\n\n /**\n * Utility to get the file name and url from the filepicker.\n * @returns {Object} object containing functions {file, name}\n */\n filePickers: function() {\n var draftItemIdsToName;\n var nameToParentNode;\n if (draftItemIdsToName === undefined) {\n draftItemIdsToName = {};\n nameToParentNode = {};\n $('form.mform input.filepickerhidden').each(function(key, filepicker) {\n draftItemIdsToName[filepicker.value] = filepicker.name;\n nameToParentNode[filepicker.name] = filepicker.parentNode;\n });\n }\n return {\n file: function(name) {\n var fileAnchor = $(nameToParentNode[name]).find('div.filepicker-filelist a');\n if (fileAnchor.length) {\n return {href: fileAnchor.get(0).href, name: fileAnchor.get(0).innerHTML};\n } else {\n return {href: null, name: null};\n }\n },\n name: function(draftitemid) {\n return draftItemIdsToName[draftitemid];\n }\n };\n }\n };\n\n /**\n * @alias module:qtype_ddmarker/form\n */\n return {\n /**\n * Initialise the form javascript features.\n * @param {Object} maxBgimageSize object with two properties: width and height.\n */\n init: dragDropForm.init\n };\n});\n"],"names":["define","$","dragDrop","Shapes","DropZoneManager","dropzoneNo","svgEl","shape","make","this","getShapeType","getLabel","updateCoordinatesFromForm","prototype","svg","coordinates","getCoordinates","currentNumPoints","getType","points","length","parse","currentyActive","isActive","removeFromSvg","addToSvg","setActive","updateSvgEl","setCoordinatesInForm","updateLabel","label","changeShape","newShapeType","getSimilar","Error","makeSvg","setAttribute","handles","getHandlePositions","moveHandle","createSvgElement","x","y","i","editHandles","makeEditHandle","index","point","editHandle","parentNode","removeChild","updateSvg","childNodes","getAttribute","match","parent","appendChild","dragDropForm","form","setFormValue","getFormValue","replace","getChoiceNo","getMarkerText","handleMove","e","info","prepare","start","movingDropZone","lastX","lastY","dragProxy","makeDragProxy","bgImg","maxX","width","maxY","height","pageX","pageY","move","document","body","handleEdit","handleIndex","ctrlKey","metaKey","addNewPointAfter","changingDropZone","edit","normalizeShape","createElement","style","position","top","left","fp","noDropZones","dropZones","init","filePickers","setOptionsForDragItemSelectors","createShapes","setupEventHandlers","waitForFilePickerToInitialise","setupPreviewArea","append","selector","dragItemsOptions","noItems","selectedValues","text","html","Number","val","value","find","remove","option","optionnode","noofdrags","k","attr","on","ids","currentTarget","name","inputType","dropZone","getSvg","previewArea","data","currentlyActive","removeClass","closest","file","href","loadPreviewImage","setTimeout","one","afterPreviewImageLoaded","css","updateSvgDisplay","outerWidth","outerHeight","markerNo","RegExp","toNameWithIndex","indexes","indexString","getEl","elements","el","type","checked","draftItemIdsToName","nameToParentNode","undefined","each","key","filepicker","fileAnchor","get","innerHTML","draftitemid"],"mappings":"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"} \ No newline at end of file diff --git a/question/type/ddmarker/amd/src/form.js b/question/type/ddmarker/amd/src/form.js index 2fd00adab9f21..a8049a69d2630 100644 --- a/question/type/ddmarker/amd/src/form.js +++ b/question/type/ddmarker/amd/src/form.js @@ -374,7 +374,6 @@ define(['jquery', 'core/dragdrop', 'qtype_ddmarker/shapes'], function($, dragDro init: function() { dragDropForm.fp = dragDropForm.filePickers(); dragDropForm.noDropZones = dragDropForm.form.getFormValue('nodropzone', []); - dragDropForm.setupPreviewArea(); dragDropForm.setOptionsForDragItemSelectors(); dragDropForm.createShapes(); dragDropForm.setupEventHandlers(); @@ -555,7 +554,13 @@ define(['jquery', 'core/dragdrop', 'qtype_ddmarker/shapes'], function($, dragDro // after filepicker's javascript has finished. $('form.mform[data-qtype="ddmarker"]').on('change', '#id_bgimage', dragDropForm.loadPreviewImage); - dragDropForm.loadPreviewImage(); + if ($('#ddm-droparea').length) { + dragDropForm.loadPreviewImage(); + } else { + // Setup preview area when the background image is uploaded the first time. + dragDropForm.setupPreviewArea(); + dragDropForm.loadPreviewImage(); + } }, /** diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 3b416f459ea52..eb7ad6f8c1f6c 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -227,7 +227,3 @@ body#page-question-type-ddmarker div[id^=fitem_id_][id*=hintclearwrong_] { body#page-question-type-ddmarker #fitem_id_penalty { margin-bottom: 2em; } - -body#page-question-type-ddmarker .ddarea.que.ddmarker { - overflow-y: scroll; -}