From a8efb0771498d63df08be289e321b0720b8e77b7 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Tue, 3 Nov 2020 21:25:51 +0000 Subject: [PATCH] MDL-70112 drag-drop qtypes: fix form preview --- question/type/ddimageortext/amd/build/form.min.js | 2 +- question/type/ddimageortext/amd/build/form.min.js.map | 2 +- question/type/ddimageortext/amd/src/form.js | 6 +++--- question/type/ddmarker/amd/build/form.min.js | 2 +- question/type/ddmarker/amd/build/form.min.js.map | 2 +- question/type/ddmarker/amd/src/form.js | 4 ++-- question/type/edit_question_form.php | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/question/type/ddimageortext/amd/build/form.min.js b/question/type/ddimageortext/amd/build/form.min.js index 175341485aa..f963ee65b66 100644 --- a/question/type/ddimageortext/amd/build/form.min.js +++ b/question/type/ddimageortext/amd/build/form.min.js @@ -1,2 +1,2 @@ -define ("qtype_ddimageortext/form",["jquery","core/dragdrop"],function(a,b){"use strict";var c={maxBgImageSize:null,maxDragImageSize:null,fp:null,init:function init(){c.fp=c.filePickers();a("#id_previewareaheader").append("
");c.updateVisibilityOfFilePickers();c.setOptionsForDragItemSelectors();c.setupEventHandlers();c.waitForFilePickerToInitialise()},waitForFilePickerToInitialise:function waitForFilePickerToInitialise(){if(null===c.fp.file("bgimage").href){setTimeout(c.waitForFilePickerToInitialise,1e3);return}M.util.js_pending("dragDropToImageForm");a("form.mform").on("change",".filepickerhidden",function(){M.util.js_pending("dragDropToImageForm");c.loadPreviewImage()});c.loadPreviewImage()},loadPreviewImage:function loadPreviewImage(){a("fieldset#id_previewareaheader .dropbackground").one("load",c.afterPreviewImageLoaded).attr("src",c.fp.file("bgimage").href)},afterPreviewImageLoaded:function afterPreviewImageLoaded(){c.createDropZones();M.util.js_complete("dragDropToImageForm")},createDropZones:function createDropZones(){var b=a(".dropzones");b.empty();var d=c.fp.file("bgimage").href;if(null===d){return}for(var e=c.form.getFormValue("nodropzone",[]),f=0,g;f")}else if(""!==i){b.append("
"+i+"
")}}c.waitForAllDropImagesToBeLoaded()},waitForAllDropImagesToBeLoaded:function waitForAllDropImagesToBeLoaded(){var b=a(".dropzones img").not(function(a,b){return c.imageIsLoaded(b)});if(0"+b[l]+"");var m=j.find("option[value=\""+l+"\"]");if(parseInt(l)===parseInt(k)){m.attr("selected",!0)}else if(c.isItemUsed(parseInt(l))){m.attr("disabled",!0)}}}},isItemUsed:function isItemUsed(b){if(0===b){return!1}if(c.form.getFormValue("drags",[b-1,"infinite"])){return!1}return 0!==a("fieldset#id_dropzoneheader select").filter(function(c,d){return parseInt(a(d).val())===b}).length},dragStart:function dragStart(d){var e=a(d.target).closest(".droppreview"),f=b.prepare(d);if(!f.start){return}b.start(d,e,function(a,b,d){c.dragMove(d)},function(){c.dragEnd()})},dragMove:function dragMove(b){var d=a("fieldset#id_previewareaheader .dropbackground"),e=d.offset(),f=b.data("dropNo"),g=b.offset(),h=Math.round(g.left-e.left),i=Math.round(g.top-e.top);h=Math.max(0,Math.min(h,d.width()-b.width()-10));i=Math.max(0,Math.min(i,d.height()-b.height()-10));c.form.setFormValue("drops",[f,"xleft"],h);c.form.setFormValue("drops",[f,"ytop"],i)},dragEnd:function dragEnd(){c.updateDropZones()},form:{toNameWithIndex:function toNameWithIndex(a,b){for(var c=a,d=0;d
");c.updateVisibilityOfFilePickers();c.setOptionsForDragItemSelectors();c.setupEventHandlers();c.waitForFilePickerToInitialise()},waitForFilePickerToInitialise:function waitForFilePickerToInitialise(){if(null===c.fp.file("bgimage").href){setTimeout(c.waitForFilePickerToInitialise,1e3);return}M.util.js_pending("dragDropToImageForm");a("form.mform[data-qtype=\"ddimageortext\"]").on("change",".filepickerhidden",function(){M.util.js_pending("dragDropToImageForm");c.loadPreviewImage()});c.loadPreviewImage()},loadPreviewImage:function loadPreviewImage(){a("fieldset#id_previewareaheader .dropbackground").one("load",c.afterPreviewImageLoaded).attr("src",c.fp.file("bgimage").href)},afterPreviewImageLoaded:function afterPreviewImageLoaded(){c.createDropZones();M.util.js_complete("dragDropToImageForm")},createDropZones:function createDropZones(){var b=a(".dropzones");b.empty();var d=c.fp.file("bgimage").href;if(null===d){return}for(var e=c.form.getFormValue("nodropzone",[]),f=0,g;f")}else if(""!==i){b.append("
"+i+"
")}}c.waitForAllDropImagesToBeLoaded()},waitForAllDropImagesToBeLoaded:function waitForAllDropImagesToBeLoaded(){var b=a(".dropzones img").not(function(a,b){return c.imageIsLoaded(b)});if(0"+b[l]+"");var m=j.find("option[value=\""+l+"\"]");if(parseInt(l)===parseInt(k)){m.attr("selected",!0)}else if(c.isItemUsed(parseInt(l))){m.attr("disabled",!0)}}}},isItemUsed:function isItemUsed(b){if(0===b){return!1}if(c.form.getFormValue("drags",[b-1,"infinite"])){return!1}return 0!==a("fieldset#id_dropzoneheader select").filter(function(c,d){return parseInt(a(d).val())===b}).length},dragStart:function dragStart(d){var e=a(d.target).closest(".droppreview"),f=b.prepare(d);if(!f.start){return}b.start(d,e,function(a,b,d){c.dragMove(d)},function(){c.dragEnd()})},dragMove:function dragMove(b){var d=a("fieldset#id_previewareaheader .dropbackground"),e=d.offset(),f=b.data("dropNo"),g=b.offset(),h=Math.round(g.left-e.left),i=Math.round(g.top-e.top);h=Math.max(0,Math.min(h,d.width()-b.width()-10));i=Math.max(0,Math.min(i,d.height()-b.height()-10));c.form.setFormValue("drops",[f,"xleft"],h);c.form.setFormValue("drops",[f,"ytop"],i)},dragEnd:function dragEnd(){c.updateDropZones()},form:{toNameWithIndex:function toNameWithIndex(a,b){for(var c=a,d=0;d.\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 * @package qtype_ddimageortext\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} with properties width and height.\n */\n maxBgImageSize: null,\n\n /**\n * @var {Object} with properties width and height.\n */\n maxDragImageSize: null,\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 * Initialise the form javascript features.\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').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 = $('select.draggroup').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 // The -10 here matches the +10 in resizeAllDragsAndDropsInGroup().\n left = Math.max(0, Math.min(left, backgroundImage.width() - drop.width() - 10));\n top = Math.max(0, Math.min(top, backgroundImage.height() - drop.height() - 10));\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')[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 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 /**\n * @alias module:qtype_ddimageortext/form\n */\n return {\n /**\n * Initialise the form JavaScript features.\n */\n init: dragDropToImageForm.init\n };\n});\n"],"file":"form.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/form.js"],"names":["define","$","dragDrop","dragDropToImageForm","maxBgImageSize","maxDragImageSize","fp","init","filePickers","append","updateVisibilityOfFilePickers","setOptionsForDragItemSelectors","setupEventHandlers","waitForFilePickerToInitialise","file","href","setTimeout","M","util","js_pending","on","loadPreviewImage","one","afterPreviewImageLoaded","attr","createDropZones","js_complete","dropZoneHolder","empty","bgimageurl","numDrops","form","getFormValue","dropNo","dragNo","group","label","imgUrl","waitForAllDropImagesToBeLoaded","notYetLoadedImages","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","remove","value","hasOwnProperty","optionnode","isItemUsed","filter","selectNode","info","prepare","start","x","y","dragMove","dragEnd","backgroundImage","backgroundPosition","data","dropPosition","min","width","height","setFormValue","indexes","indexString","getEl","elements","el","type","checked","draftItemIdsToName","nameToParentNode","index","filepicker","parentNode","fileAnchor","get","innerHTML","draftitemid"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/*\n * JavaScript to allow dragging options to slots (using mouse down or touch) or tab through slots using keyboard.\n *\n * @module qtype_ddimageortext/form\n * @package qtype_ddimageortext\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} with properties width and height.\n */\n maxBgImageSize: null,\n\n /**\n * @var {Object} with properties width and height.\n */\n maxDragImageSize: null,\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 * Initialise the form javascript features.\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 = $('select.draggroup').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 // The -10 here matches the +10 in resizeAllDragsAndDropsInGroup().\n left = Math.max(0, Math.min(left, backgroundImage.width() - drop.width() - 10));\n top = Math.max(0, Math.min(top, backgroundImage.height() - drop.height() - 10));\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 /**\n * @alias module:qtype_ddimageortext/form\n */\n return {\n /**\n * Initialise the form JavaScript features.\n */\n init: dragDropToImageForm.init\n };\n});\n"],"file":"form.min.js"} \ 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 6147e6ba17c..1e0c80fe767 100644 --- a/question/type/ddimageortext/amd/src/form.js +++ b/question/type/ddimageortext/amd/src/form.js @@ -82,7 +82,7 @@ define(['jquery', 'core/dragdrop'], function($, dragDrop) { // From now on, when a new file gets loaded into the filepicker, update the preview. // This is not in the setupEventHandlers section as it needs to be delayed until // after filepicker's javascript has finished. - $('form.mform').on('change', '.filepickerhidden', function() { + $('form.mform[data-qtype="ddimageortext"]').on('change', '.filepickerhidden', function() { M.util.js_pending('dragDropToImageForm'); dragDropToImageForm.loadPreviewImage(); }); @@ -428,7 +428,7 @@ define(['jquery', 'core/dragdrop'], function($, dragDrop) { }, getEl: function(name, indexes) { - var form = $('form.mform')[0]; + var form = $('form.mform[data-qtype="ddimageortext"]')[0]; return form.elements[this.toNameWithIndex(name, indexes)]; }, @@ -479,7 +479,7 @@ define(['jquery', 'core/dragdrop'], function($, dragDrop) { if (draftItemIdsToName === undefined) { draftItemIdsToName = {}; nameToParentNode = {}; - var fp = $('form.mform input.filepickerhidden'); + var fp = $('form.mform[data-qtype="ddimageortext"] input.filepickerhidden'); fp.each(function(index, filepicker) { draftItemIdsToName[filepicker.value] = filepicker.name; nameToParentNode[filepicker.name] = filepicker.parentNode; diff --git a/question/type/ddmarker/amd/build/form.min.js b/question/type/ddmarker/amd/build/form.min.js index edf140299d9..ef09d2ce945 100644 --- a/question/type/ddmarker/amd/build/form.min.js +++ b/question/type/ddmarker/amd/build/form.min.js @@ -1,2 +1,2 @@ -define ("qtype_ddmarker/form",["jquery","core/dragdrop","qtype_ddmarker/shapes"],function(a,b,c){"use strict";function d(a){this.dropzoneNo=a;this.svgEl=null;this.shape=c.make(this.getShapeType(),this.getLabel());this.updateCoordinatesFromForm()}d.prototype.updateCoordinatesFromForm=function(a){var b=this.getCoordinates(),c="polygon"===this.shape.getType()&&this.shape.points.length;if(this.shape.getCoordinates()===b){return}if(!this.shape.parse(b,1)){return}if("polygon"===this.shape.getType()&&c!==this.shape.points.length){var d=this.isActive();this.removeFromSvg();if(a){this.addToSvg(a);if(d){this.setActive()}}}else{this.updateSvgEl()}this.setCoordinatesInForm()};d.prototype.updateLabel=function(){var a=this.getLabel();if(this.shape.label!==a){this.shape.label=a;this.updateSvgEl()}};d.prototype.changeShape=function(a){var b=this.getShapeType(),d=this.isActive();if(b===this.shape.getType()){return}this.removeFromSvg();this.shape=c.getSimilar(b,this.shape);if(a){this.addToSvg(a);if(d){this.setActive()}}this.setCoordinatesInForm()};d.prototype.addToSvg=function(a){if(null!==this.svgEl){throw new Error("this.svgEl already set")}this.svgEl=this.shape.makeSvg(a);if(!this.svgEl){return}this.svgEl.setAttribute("class","dropzone");this.svgEl.setAttribute("data-dropzone-no",this.dropzoneNo);var b=this.shape.getHandlePositions();if(null===b){return}var d=c.createSvgElement(this.svgEl,"circle");d.setAttribute("cx",b.moveHandle.x);d.setAttribute("cy",b.moveHandle.y);d.setAttribute("r",7);d.setAttribute("class","handle move");for(var e=0;e
")},setOptionsForDragItemSelectors:function setOptionsForDragItemSelectors(){var b={0:""},c=f.form.getFormValue("noitems",[]),d=[],e,g,h;for(g=1;g<=c;g++){h=f.form.getMarkerText(g);if(""!==h){b[g]=a("
").text(h).html()}}for(g=0;g"+b[j]+"";e.append(l);var m=e.find("option[value=\""+j+"\"]");if(0===j){continue}if(j===d[g]){m.attr("selected",!0);continue}var n=f.form.getFormValue("drags",[j-1,"noofdrags"]);if(0===+n){continue}for(var o in d){if(+d[o]!==j){continue}if(1===+n){m.attr("disabled",!0);break}else{n--}}}if(0");for(c=0;c
")},setOptionsForDragItemSelectors:function setOptionsForDragItemSelectors(){var b={0:""},c=f.form.getFormValue("noitems",[]),d=[],e,g,h;for(g=1;g<=c;g++){h=f.form.getMarkerText(g);if(""!==h){b[g]=a("
").text(h).html()}}for(g=0;g"+b[j]+"";e.append(l);var m=e.find("option[value=\""+j+"\"]");if(0===j){continue}if(j===d[g]){m.attr("selected",!0);continue}var n=f.form.getFormValue("drags",[j-1,"noofdrags"]);if(0===+n){continue}for(var o in d){if(+d[o]!==j){continue}if(1===+n){m.attr("disabled",!0);break}else{n--}}}if(0");for(c=0;c.\n\n/**\n * This class provides the enhancements to the drag-drop marker editing form.\n *\n * @package qtype_ddmarker\n * @subpackage 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').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')[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"],"file":"form.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/form.js"],"names":["define","$","dragDrop","Shapes","DropZoneManager","dropzoneNo","svgEl","shape","make","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","dragItemsOptions","noItems","selectedValues","selector","text","html","val","find","remove","value","option","optionnode","attr","noofdrags","k","on","ids","currentTarget","name","inputType","dropZone","getSvg","previewArea","data","currentlyActive","removeClass","closest","file","href","setTimeout","loadPreviewImage","one","afterPreviewImageLoaded","css","updateSvgDisplay","outerWidth","outerHeight","markerNo","toNameWithIndex","indexes","indexString","getEl","elements","el","type","checked","draftItemIdsToName","nameToParentNode","each","key","filepicker","fileAnchor","get","innerHTML","draftitemid"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * This class provides the enhancements to the drag-drop marker editing form.\n *\n * @package qtype_ddmarker\n * @subpackage 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"],"file":"form.min.js"} \ 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 d8f61b2fcd7..a06e0b8206d 100644 --- a/question/type/ddmarker/amd/src/form.js +++ b/question/type/ddmarker/amd/src/form.js @@ -554,7 +554,7 @@ define(['jquery', 'core/dragdrop', 'qtype_ddmarker/shapes'], function($, dragDro // From now on, when a new file gets loaded into the filepicker, update the preview. // This is not in the setupEventHandlers section as it needs to be delayed until // after filepicker's javascript has finished. - $('form.mform').on('change', '#id_bgimage', dragDropForm.loadPreviewImage); + $('form.mform[data-qtype="ddmarker"]').on('change', '#id_bgimage', dragDropForm.loadPreviewImage); dragDropForm.loadPreviewImage(); }, @@ -645,7 +645,7 @@ define(['jquery', 'core/dragdrop', 'qtype_ddmarker/shapes'], function($, dragDro }, getEl: function(name, indexes) { - var form = $('form.mform')[0]; + var form = $('form.mform[data-qtype="ddmarker"]')[0]; return form.elements[this.toNameWithIndex(name, indexes)]; }, diff --git a/question/type/edit_question_form.php b/question/type/edit_question_form.php index 735615a48d8..3bf44cbadd5 100644 --- a/question/type/edit_question_form.php +++ b/question/type/edit_question_form.php @@ -110,7 +110,7 @@ abstract class question_edit_form extends question_wizard_form { $this->category = $category; $this->categorycontext = context::instance_by_id($category->contextid); - parent::__construct($submiturl, null, 'post', '', null, $formeditable); + parent::__construct($submiturl, null, 'post', '', ['data-qtype' => $this->qtype()], $formeditable); } /**