forked from EVOgeek/Vmeda.Online
		
	
						commit
						31041f5ff9
					
				| @ -133,10 +133,8 @@ export class AddonQtypeDdImageOrTextQuestion { | ||||
|             const dragNode = this.doc.cloneNewDragItem(i, dragItemNo); | ||||
|             i++; | ||||
| 
 | ||||
|             if (!this.readOnly) { | ||||
|                 // Make the item draggable.
 | ||||
|                 this.draggableForQuestion(dragNode, group, choice); | ||||
|             } | ||||
|             // Make the item draggable.
 | ||||
|             this.draggableForQuestion(dragNode, group, choice); | ||||
| 
 | ||||
|             // If the draggable item needs to be created more than once, create the rest of copies.
 | ||||
|             if (dragNode.classList.contains('infinite')) { | ||||
| @ -146,9 +144,8 @@ export class AddonQtypeDdImageOrTextQuestion { | ||||
|                 while (dragsToCreate > 0) { | ||||
|                     const newDragNode = this.doc.cloneNewDragItem(i, dragItemNo); | ||||
|                     i++; | ||||
|                     if (!this.readOnly) { | ||||
|                         this.draggableForQuestion(newDragNode, group, choice); | ||||
|                     } | ||||
|                     this.draggableForQuestion(newDragNode, group, choice); | ||||
| 
 | ||||
|                     dragsToCreate--; | ||||
|                 } | ||||
|             } | ||||
| @ -202,11 +199,15 @@ export class AddonQtypeDdImageOrTextQuestion { | ||||
|         let dragItemsArea = <HTMLElement> topNode.querySelector('div.draghomes'); | ||||
| 
 | ||||
|         if (dragItemsArea) { | ||||
|             // On 3.9+ dragitems were removed.
 | ||||
|             const dragItems = topNode.querySelector('div.dragitems'); | ||||
| 
 | ||||
|             if (dragItems) { | ||||
|                 // Remove empty div.dragitems.
 | ||||
|                 dragItems.remove(); | ||||
|             } | ||||
| 
 | ||||
|             // 3.6+ site, transform HTML so it has the same structure as in Moodle 3.5.
 | ||||
| 
 | ||||
|             // Remove empty div.dragitems.
 | ||||
|             topNode.querySelector('div.dragitems').remove(); | ||||
| 
 | ||||
|             const ddArea = topNode.querySelector('div.ddarea'); | ||||
| 
 | ||||
|             // Move div.dropzones to div.ddarea.
 | ||||
| @ -332,17 +333,19 @@ export class AddonQtypeDdImageOrTextQuestion { | ||||
|         drag.setAttribute('group', String(group)); | ||||
|         drag.setAttribute('choice', String(choice)); | ||||
| 
 | ||||
|         // Listen to click events.
 | ||||
|         drag.addEventListener('click', (e) => { | ||||
|             e.preventDefault(); | ||||
|             e.stopPropagation(); | ||||
|         if (!this.readOnly) { | ||||
|             // Listen to click events.
 | ||||
|             drag.addEventListener('click', (e) => { | ||||
|                 e.preventDefault(); | ||||
|                 e.stopPropagation(); | ||||
| 
 | ||||
|             if (drag.classList.contains('beingdragged')) { | ||||
|                 this.deselectDrags(); | ||||
|             } else { | ||||
|                 this.selectDrag(drag); | ||||
|             } | ||||
|         }); | ||||
|                 if (drag.classList.contains('beingdragged')) { | ||||
|                     this.deselectDrags(); | ||||
|                 } else { | ||||
|                     this.selectDrag(drag); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -387,14 +390,9 @@ export class AddonQtypeDdImageOrTextQuestion { | ||||
|     getUnplacedChoiceForDrop(choice: number, drop: HTMLElement): HTMLElement { | ||||
|         const dragItems = this.getChoicesForDrop(choice, drop); | ||||
| 
 | ||||
|         for (let x = 0; x < dragItems.length; x++) { | ||||
|             const dragItem = dragItems[x]; | ||||
|             if (this.readOnly || (!dragItem.classList.contains('placed') && !dragItem.classList.contains('beingdragged'))) { | ||||
|                 return dragItem; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|         return dragItems.find((dragItem) => { | ||||
|             return (!dragItem.classList.contains('placed') && !dragItem.classList.contains('beingdragged')); | ||||
|         }) || null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -530,7 +528,7 @@ export class AddonQtypeDdImageOrTextQuestion { | ||||
|         if (originInputId && originInputId != targetInputId) { | ||||
|             // Remove it from the previous place.
 | ||||
|             const originInputNode = <HTMLInputElement> this.doc.topNode().querySelector('input#' + originInputId); | ||||
|             originInputNode.setAttribute('value', ''); | ||||
|             originInputNode.setAttribute('value', '0'); | ||||
|         } | ||||
| 
 | ||||
|         // Now position the draggable and set it to the input.
 | ||||
| @ -539,7 +537,10 @@ export class AddonQtypeDdImageOrTextQuestion { | ||||
|         drag.style.top = position[1] - 1 + 'px'; | ||||
|         drag.classList.add('placed'); | ||||
| 
 | ||||
|         inputNode.setAttribute('value', drag.getAttribute('choice')); | ||||
|         if (drag.getAttribute('choice')) { | ||||
|             inputNode.setAttribute('value', drag.getAttribute('choice')); | ||||
|         } | ||||
| 
 | ||||
|         drag.setAttribute('inputid', targetInputId); | ||||
|     } | ||||
| 
 | ||||
| @ -575,7 +576,7 @@ export class AddonQtypeDdImageOrTextQuestion { | ||||
|         const inputId = drag.getAttribute('inputid'); | ||||
|         if (inputId) { | ||||
|             const inputNode = <HTMLInputElement> this.doc.topNode().querySelector('input#' + inputId); | ||||
|             inputNode.setAttribute('value', ''); | ||||
|             inputNode.setAttribute('value', '0'); | ||||
|         } | ||||
| 
 | ||||
|         // Move the element to its original position.
 | ||||
| @ -652,6 +653,7 @@ export class AddonQtypeDdImageOrTextQuestion { | ||||
| 
 | ||||
|             if (choice > 0) { | ||||
|                 const dragItem = this.getUnplacedChoiceForDrop(choice, dropZone); | ||||
| 
 | ||||
|                 if (dragItem !== null) { | ||||
|                     this.placeDragInDrop(dragItem, dropZone); | ||||
|                 } | ||||
|  | ||||
| @ -35,6 +35,28 @@ addon-qtype-ddimageortext { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .group2 { | ||||
|         border-radius: 10px 0 0 0; | ||||
|     } | ||||
|     .group3 { | ||||
|         border-radius: 0 10px 0 0; | ||||
|     } | ||||
|     .group4 { | ||||
|         border-radius: 0 0 10px 0; | ||||
|     } | ||||
|     .group5 { | ||||
|         border-radius: 0 0 0 10px; | ||||
|     } | ||||
|     .group6 { | ||||
|         border-radius: 0 10px 10px 0; | ||||
|     } | ||||
|     .group7 { | ||||
|         border-radius: 10px 0 0 10px; | ||||
|     } | ||||
|     .group8 { | ||||
|         border-radius: 10px 10px 10px 10px; | ||||
|     } | ||||
| 
 | ||||
|     .drag { | ||||
|         border: 1px solid $gray-darker; | ||||
|         color: $text-color; | ||||
|  | ||||
| @ -27,6 +27,7 @@ export interface AddonQtypeDdMarkerQuestionDocStructure { | ||||
|     dragItems?: () => HTMLElement[]; | ||||
|     dragItemsForChoice?: (choiceNo: number) => HTMLElement[]; | ||||
|     dragItemForChoice?: (choiceNo: number, itemNo: number) => HTMLElement; | ||||
|     dragItemPlaceholder?: (choiceNo: number) => HTMLElement; | ||||
|     dragItemBeingDragged?: (choiceNo: number) => HTMLElement; | ||||
|     dragItemHome?: (choiceNo: number) => HTMLElement; | ||||
|     dragItemHomes?: () => HTMLElement[]; | ||||
| @ -36,6 +37,14 @@ export interface AddonQtypeDdMarkerQuestionDocStructure { | ||||
|     markerTexts?: () => HTMLElement; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Point type. | ||||
|  */ | ||||
| export type AddonQtypeDdMarkerQuestionPoint = { | ||||
|     x: number; // X axis coordinates.
 | ||||
|     y: number; // Y axis coordinates.
 | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Class to make a question of ddmarker type work. | ||||
|  */ | ||||
| @ -98,14 +107,13 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|      * @return The new element. | ||||
|      */ | ||||
|     cloneNewDragItem(dragHome: HTMLElement, itemNo: number): HTMLElement { | ||||
|         const marker = <HTMLElement> dragHome.querySelector('span.markertext'); | ||||
|         marker.style.opacity = '0.6'; | ||||
| 
 | ||||
|         // Clone the element and add the right classes.
 | ||||
|         const drag = <HTMLElement> dragHome.cloneNode(true); | ||||
|         drag.classList.remove('draghome'); | ||||
|         drag.classList.add('dragitem'); | ||||
|         drag.classList.add('item' + itemNo); | ||||
|         drag.classList.remove('dragplaceholder'); // In case it has it.
 | ||||
|         dragHome.classList.add('dragplaceholder'); | ||||
| 
 | ||||
|         // Insert the new drag after the dragHome.
 | ||||
|         dragHome.parentElement.insertBefore(drag, dragHome.nextSibling); | ||||
| @ -122,15 +130,14 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|      * @param bgImgXY X and Y of the BG IMG relative position. | ||||
|      * @return Position relative to the window. | ||||
|      */ | ||||
|     convertToWindowXY(bgImgXY: number[]): number[] { | ||||
|         const bgImg = this.doc.bgImg(), | ||||
|             position = this.domUtils.getElementXY(bgImg, null, 'ddarea'); | ||||
|     convertToWindowXY(bgImgXY: string): number[] { | ||||
|         const bgImg = this.doc.bgImg(); | ||||
|         const position = this.domUtils.getElementXY(bgImg, null, 'ddarea'); | ||||
|         let coordsNumbers = this.parsePoint(bgImgXY); | ||||
| 
 | ||||
|         // Render the position related to the current image dimensions.
 | ||||
|         bgImgXY[0] *= this.proportion; | ||||
|         bgImgXY[1] *= this.proportion; | ||||
|         coordsNumbers = this.makePointProportional(coordsNumbers); | ||||
| 
 | ||||
|         return [Number(bgImgXY[0]) + position[0], Number(bgImgXY[1]) + position[1]]; | ||||
|         return [coordsNumbers.x + position[0], coordsNumbers.y + position[1]]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -139,10 +146,10 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|      * @param coords Coordinates to check. | ||||
|      * @return Whether they're inside the background image. | ||||
|      */ | ||||
|     coordsInImg(coords: number[]): boolean { | ||||
|     coordsInImg(coords: AddonQtypeDdMarkerQuestionPoint): boolean { | ||||
|         const bgImg = this.doc.bgImg(); | ||||
| 
 | ||||
|         return (coords[0] * this.proportion <= bgImg.width && coords[1] * this.proportion <= bgImg.height); | ||||
|         return (coords.x * this.proportion <= bgImg.width + 1) && (coords.y * this.proportion <= bgImg.height + 1); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -173,7 +180,7 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|      */ | ||||
|     docStructure(slot: number): AddonQtypeDdMarkerQuestionDocStructure { | ||||
|         const topNode = <HTMLElement> this.container.querySelector('.addon-qtype-ddmarker-container'), | ||||
|             dragItemsArea = <HTMLElement> topNode.querySelector('div.dragitems'); | ||||
|             dragItemsArea = <HTMLElement> topNode.querySelector('div.dragitems, div.draghomes'); | ||||
| 
 | ||||
|         return { | ||||
|             topNode: (): HTMLElement => { | ||||
| @ -194,14 +201,18 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|             dragItemForChoice: (choiceNo: number, itemNo: number): HTMLElement => { | ||||
|                 return <HTMLElement> dragItemsArea.querySelector('span.dragitem.choice' + choiceNo + '.item' + itemNo); | ||||
|             }, | ||||
|             dragItemPlaceholder: (choiceNo: number): HTMLElement => { | ||||
|                 return <HTMLElement> dragItemsArea.querySelector('span.dragplaceholder.choice' + choiceNo); | ||||
|             }, | ||||
|             dragItemBeingDragged: (choiceNo: number): HTMLElement => { | ||||
|                 return <HTMLElement> dragItemsArea.querySelector('span.dragitem.beingdragged.choice' + choiceNo); | ||||
|             }, | ||||
|             dragItemHome: (choiceNo: number): HTMLElement => { | ||||
|                 return <HTMLElement> dragItemsArea.querySelector('span.draghome.choice' + choiceNo); | ||||
|                 return <HTMLElement> dragItemsArea.querySelector('span.draghome.choice' + choiceNo + | ||||
|                     ', span.marker.choice' + choiceNo); | ||||
|             }, | ||||
|             dragItemHomes: (): HTMLElement[] => { | ||||
|                 return <HTMLElement[]> Array.from(dragItemsArea.querySelectorAll('span.draghome')); | ||||
|                 return <HTMLElement[]> Array.from(dragItemsArea.querySelectorAll('span.draghome, span.marker')); | ||||
|             }, | ||||
|             getClassnameNumericSuffix: (node: HTMLElement, prefix: string): number => { | ||||
| 
 | ||||
| @ -328,13 +339,11 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|                 const markerSpan = <HTMLElement> this.doc.topNode().querySelector( | ||||
|                         'div.ddarea div.markertexts span.markertext' + dropZoneNo); | ||||
|                 if (markerSpan !== null) { | ||||
| 
 | ||||
|                     xyForText[0] = (xyForText[0] - markerSpan.offsetWidth / 2) * this.proportion; | ||||
|                     xyForText[1] = (xyForText[1] - markerSpan.offsetHeight / 2) * this.proportion; | ||||
| 
 | ||||
|                     const width = this.domUtils.getElementMeasure(markerSpan, true, true, false, true); | ||||
|                     const height = this.domUtils.getElementMeasure(markerSpan, false, true, false, true); | ||||
|                     markerSpan.style.opacity = '0.6'; | ||||
|                     markerSpan.style.left = xyForText[0] + 'px'; | ||||
|                     markerSpan.style.top = xyForText[1] + 'px'; | ||||
|                     markerSpan.style.left = (xyForText.x - (width / 2)) + 'px'; | ||||
|                     markerSpan.style.top = (xyForText.y - (height / 2)) + 'px'; | ||||
| 
 | ||||
|                     const markerSpanAnchor = markerSpan.querySelector('a'); | ||||
|                     if (markerSpanAnchor !== null) { | ||||
| @ -364,38 +373,36 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|      * Draw a circle in a drop zone. | ||||
|      * | ||||
|      * @param dropZoneNo Number of the drop zone. | ||||
|      * @param coords Coordinates of the circle. | ||||
|      * @param coordinates Coordinates of the circle. | ||||
|      * @param colour Colour of the circle. | ||||
|      * @return X and Y position of the center of the circle. | ||||
|      */ | ||||
|     drawShapeCircle(dropZoneNo: number, coords: string, colour: string): number[] { | ||||
|         // Extract the numbers in the coordinates.
 | ||||
|         const coordsParts = coords.match(/(\d+),(\d+);(\d+)/); | ||||
|     drawShapeCircle(dropZoneNo: number, coordinates: string, colour: string): AddonQtypeDdMarkerQuestionPoint { | ||||
|         if (!coordinates.match(/^\d+(\.\d+)?,\d+(\.\d+)?;\d+(\.\d+)?$/)) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         if (coordsParts && coordsParts.length === 4) { | ||||
|             // Remove first element and convert them to number.
 | ||||
|             coordsParts.shift(); | ||||
|         const bits = coordinates.split(';'); | ||||
|         let centre = this.parsePoint(bits[0]); | ||||
|         const radius = Number(bits[1]); | ||||
| 
 | ||||
|             const coordsPartsNum = coordsParts.map((i) => { | ||||
|                return Number(i); | ||||
|         // Calculate circle limits and check it's inside the background image.
 | ||||
|         const circleLimit = {x: centre.x - radius, y: centre.y - radius}; | ||||
|         if (this.coordsInImg(circleLimit)) { | ||||
|             centre = this.makePointProportional(centre); | ||||
| 
 | ||||
|             // All good, create the shape.
 | ||||
|             this.shapes[dropZoneNo] = this.graphics.addShape({ | ||||
|                 type: 'circle', | ||||
|                 color: colour | ||||
|             }, { | ||||
|                 cx: centre.x, | ||||
|                 cy: centre.y, | ||||
|                 r: Math.round(radius * this.proportion) | ||||
|             }); | ||||
| 
 | ||||
|             // Calculate circle limits and check it's inside the background image.
 | ||||
|             const circleLimit = [coordsPartsNum[0] - coordsPartsNum[2], coordsPartsNum[1] - coordsPartsNum[2]]; | ||||
|             if (this.coordsInImg(circleLimit)) { | ||||
|                 // All good, create the shape.
 | ||||
|                 this.shapes[dropZoneNo] = this.graphics.addShape({ | ||||
|                     type: 'circle', | ||||
|                     color: colour | ||||
|                 }, { | ||||
|                     cx: coordsPartsNum[0] * this.proportion, | ||||
|                     cy: coordsPartsNum[1] * this.proportion, | ||||
|                     r: coordsPartsNum[2] * this.proportion | ||||
|                 }); | ||||
| 
 | ||||
|                 // Return the center.
 | ||||
|                 return [coordsPartsNum[0], coordsPartsNum[1]]; | ||||
|             } | ||||
|             // Return the centre.
 | ||||
|             return centre; | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
| @ -405,39 +412,40 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|      * Draw a rectangle in a drop zone. | ||||
|      * | ||||
|      * @param dropZoneNo Number of the drop zone. | ||||
|      * @param coords Coordinates of the rectangle. | ||||
|      * @param coordinates Coordinates of the rectangle. | ||||
|      * @param colour Colour of the rectangle. | ||||
|      * @return X and Y position of the center of the rectangle. | ||||
|      */ | ||||
|     drawShapeRectangle(dropZoneNo: number, coords: string, colour: string): number[] { | ||||
|         // Extract the numbers in the coordinates.
 | ||||
|         const coordsParts = coords.match(/(\d+),(\d+);(\d+),(\d+)/); | ||||
|     drawShapeRectangle(dropZoneNo: number, coordinates: string, colour: string): AddonQtypeDdMarkerQuestionPoint { | ||||
|         if (!coordinates.match(/^\d+(\.\d+)?,\d+(\.\d+)?;\d+(\.\d+)?,\d+(\.\d+)?$/)) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         if (coordsParts && coordsParts.length === 5) { | ||||
|             // Remove first element and convert them to number.
 | ||||
|             coordsParts.shift(); | ||||
|         const bits = coordinates.split(';'); | ||||
|         const startPoint = this.parsePoint(bits[0]); | ||||
|         const size = this.parsePoint(bits[1]); | ||||
| 
 | ||||
|             const coordsPartsNum = coordsParts.map((i) => { | ||||
|                return Number(i); | ||||
|         // Calculate rectangle limits and check it's inside the background image.
 | ||||
|         const rectLimits = {x: startPoint.x + size.x, y: startPoint.y + size.y}; | ||||
|         if (this.coordsInImg(rectLimits)) { | ||||
|             const startPointProp = this.makePointProportional(startPoint); | ||||
|             const sizeProp = this.makePointProportional(size); | ||||
| 
 | ||||
|             // All good, create the shape.
 | ||||
|             this.shapes[dropZoneNo] = this.graphics.addShape({ | ||||
|                 type: 'rect', | ||||
|                 color: colour | ||||
|             }, { | ||||
|                 x: startPointProp.x, | ||||
|                 y: startPointProp.y, | ||||
|                 width: sizeProp.x, | ||||
|                 height: sizeProp.y | ||||
|             }); | ||||
| 
 | ||||
|             // Calculate rectangle limits and check it's inside the background image.
 | ||||
|             const rectLimits = [coordsPartsNum[0] + coordsPartsNum[2], coordsPartsNum[1] + coordsPartsNum[3]]; | ||||
|             if (this.coordsInImg(rectLimits)) { | ||||
|                 // All good, create the shape.
 | ||||
|                 this.shapes[dropZoneNo] = this.graphics.addShape({ | ||||
|                     type: 'rect', | ||||
|                     color: colour | ||||
|                 }, { | ||||
|                     x: coordsPartsNum[0] * this.proportion, | ||||
|                     y: coordsPartsNum[1] * this.proportion, | ||||
|                     width: coordsPartsNum[2] * this.proportion, | ||||
|                     height: coordsPartsNum[3] * this.proportion | ||||
|                 }); | ||||
|             const centre = { x: startPoint.x + (size.x / 2) , y: startPoint.y + (size.y / 2)}; | ||||
| 
 | ||||
|                 // Return the center.
 | ||||
|                 return [coordsPartsNum[0] + coordsPartsNum[2] / 2, coordsPartsNum[1] + coordsPartsNum[3] / 2]; | ||||
|             } | ||||
|             // Return the centre.
 | ||||
|             return this.makePointProportional(centre); | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
| @ -447,53 +455,83 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|      * Draw a polygon in a drop zone. | ||||
|      * | ||||
|      * @param dropZoneNo Number of the drop zone. | ||||
|      * @param coords Coordinates of the polygon. | ||||
|      * @param coordinates Coordinates of the polygon. | ||||
|      * @param colour Colour of the polygon. | ||||
|      * @return X and Y position of the center of the polygon. | ||||
|      */ | ||||
|     drawShapePolygon(dropZoneNo: number, coords: string, colour: string): number[] { | ||||
|         const coordsParts = coords.split(';'), | ||||
|             points = [], | ||||
|             bgImg = this.doc.bgImg(), | ||||
|             maxXY = [0, 0], | ||||
|             minXY = [bgImg.width, bgImg.height]; | ||||
| 
 | ||||
|         for (const i in coordsParts) { | ||||
|             // Extract the X and Y of this point.
 | ||||
|             const partsString = coordsParts[i].match(/^(\d+),(\d+)$/), | ||||
|                 parts = partsString && partsString.map((part) => { | ||||
|                     return Number(part); | ||||
|                 }); | ||||
| 
 | ||||
|             if (parts !== null && this.coordsInImg([parts[1], parts[2]])) { | ||||
|                 parts[1] *= this.proportion; | ||||
|                 parts[2] *= this.proportion; | ||||
| 
 | ||||
|                 // Calculate min and max points to find center to show marker on.
 | ||||
|                 minXY[0] = Math.min(parts[1], minXY[0]); | ||||
|                 minXY[1] = Math.min(parts[2], minXY[1]); | ||||
|                 maxXY[0] = Math.max(parts[1], maxXY[0]); | ||||
|                 maxXY[1] = Math.max(parts[2], maxXY[1]); | ||||
| 
 | ||||
|                 points.push(parts[1] + ',' + parts[2]); | ||||
|             } | ||||
|     drawShapePolygon(dropZoneNo: number, coordinates: string, colour: string): AddonQtypeDdMarkerQuestionPoint { | ||||
|         if (!coordinates.match(/^\d+(\.\d+)?,\d+(\.\d+)?(?:;\d+(\.\d+)?,\d+(\.\d+)?)*$/)) { | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         if (points.length > 2) { | ||||
|         const bits = coordinates.split(';'); | ||||
|         const centre = {x: 0, y: 0}; | ||||
|         const points = bits.map((bit) => { | ||||
|             const point = this.parsePoint(bit); | ||||
|             centre.x += point.x; | ||||
|             centre.y += point.y; | ||||
| 
 | ||||
|             return point; | ||||
|         }); | ||||
| 
 | ||||
|         if (points.length > 0) { | ||||
|             centre.x = Math.round(centre.x / points.length); | ||||
|             centre.y = Math.round(centre.y / points.length); | ||||
|         } | ||||
| 
 | ||||
|         const pointsOnImg = []; | ||||
|         points.forEach((point) => { | ||||
|             if (this.coordsInImg(point)) { | ||||
|                 point = this.makePointProportional(point); | ||||
| 
 | ||||
|                 pointsOnImg.push(point.x + ',' + point.y); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         if (pointsOnImg.length > 2) { | ||||
|             this.shapes[dropZoneNo] = this.graphics.addShape({ | ||||
|                 type: 'polygon', | ||||
|                 color: colour | ||||
|             }, { | ||||
|                 points: points.join(' ') | ||||
|                 points: pointsOnImg.join(' ') | ||||
|             }); | ||||
| 
 | ||||
|             // Return the center.
 | ||||
|             return [(minXY[0] + maxXY[0]) / 2, (minXY[1] + maxXY[1]) / 2]; | ||||
|             // Return the centre.
 | ||||
|             return this.makePointProportional(centre); | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Make a point from the string representation. | ||||
|      * | ||||
|      * @param coordinates "x,y". | ||||
|      * @return Coordinates to the point. | ||||
|      */ | ||||
|     parsePoint(coordinates: string): AddonQtypeDdMarkerQuestionPoint { | ||||
|         const bits = coordinates.split(','); | ||||
|         if (bits.length !== 2) { | ||||
|             throw coordinates + ' is not a valid point'; | ||||
|         } | ||||
| 
 | ||||
|         return {x: Number(bits[0]), y: Number(bits[1])}; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Make proportional position of the point. | ||||
|      * | ||||
|      * @param  point Point coordinates. | ||||
|      * @return Converted point. | ||||
|      */ | ||||
|     makePointProportional(point: AddonQtypeDdMarkerQuestionPoint): AddonQtypeDdMarkerQuestionPoint { | ||||
|         return { | ||||
|             x: Math.round(point.x * this.proportion), | ||||
|             y: Math.round(point.y * this.proportion) | ||||
| 
 | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Drop a drag element into a certain position. | ||||
|      * | ||||
| @ -507,9 +545,6 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|             // Set the position related to the natural image dimensions.
 | ||||
|             if (this.proportion < 1) { | ||||
|                 position[0] = Math.round(position[0] / this.proportion); | ||||
|             } | ||||
| 
 | ||||
|             if (this.proportion < 1) { | ||||
|                 position[1] = Math.round(position[1] / this.proportion); | ||||
|             } | ||||
|         } | ||||
| @ -538,11 +573,7 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|             const coordsStrings = fv.split(';'); | ||||
| 
 | ||||
|             for (let i = 0; i < coordsStrings.length; i++) { | ||||
|                 const coordsNumbers = coordsStrings[i].split(',').map((i) => { | ||||
|                     return Number(i); | ||||
|                 }); | ||||
| 
 | ||||
|                 coords[coords.length] = this.convertToWindowXY(coordsNumbers); | ||||
|                 coords[coords.length] = this.convertToWindowXY(coordsStrings[i]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -581,9 +612,6 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|         // Set the position related to the natural image dimensions.
 | ||||
|         if (this.proportion < 1) { | ||||
|             position[0] = Math.round(position[0] / this.proportion); | ||||
|         } | ||||
| 
 | ||||
|         if (this.proportion < 1) { | ||||
|             position[1] = Math.round(position[1] / this.proportion); | ||||
|         } | ||||
| 
 | ||||
| @ -723,6 +751,12 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|             this.question.loaded = true; | ||||
|         }; | ||||
| 
 | ||||
|         if (bgImg.complete && bgImg.naturalWidth) { | ||||
|             imgLoaded(); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         bgImg.addEventListener('load', imgLoaded); | ||||
| 
 | ||||
|         // Try again after a while.
 | ||||
| @ -764,13 +798,25 @@ export class AddonQtypeDdMarkerQuestion { | ||||
|                     dragItem.classList.remove('unneeded'); | ||||
|                 } | ||||
| 
 | ||||
|                 const placeholder = this.doc.dragItemPlaceholder(choiceNo); | ||||
| 
 | ||||
|                 // Remove the class only if is placed on the image.
 | ||||
|                 if (homePosition[0] != coords[i][0] || homePosition[1] != coords[i][1]) { | ||||
|                     dragItem.classList.remove('unplaced'); | ||||
|                 } | ||||
|                     dragItem.classList.add('placed'); | ||||
| 
 | ||||
|                 dragItem.style.left = coords[i][0] + 'px'; | ||||
|                 dragItem.style.top = coords[i][1] + 'px'; | ||||
|                     const computedStyle = getComputedStyle(dragItem); | ||||
|                     const left = coords[i][0] - this.domUtils.getComputedStyleMeasure(computedStyle, 'marginLeft'); | ||||
|                     const top = coords[i][1] - this.domUtils.getComputedStyleMeasure(computedStyle, 'marginTop'); | ||||
| 
 | ||||
|                     dragItem.style.left = left + 'px'; | ||||
|                     dragItem.style.top = top + 'px'; | ||||
|                     placeholder.classList.add('active'); | ||||
|                 } else { | ||||
|                     dragItem.classList.remove('placed'); | ||||
|                     dragItem.classList.add('unplaced'); | ||||
|                     placeholder.classList.remove('active'); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -9,24 +9,15 @@ addon-qtype-ddmarker { | ||||
|         display: block; | ||||
|     } | ||||
| 
 | ||||
|     .droparea { | ||||
|         display: inline-block; | ||||
|     } | ||||
| 
 | ||||
|     div.droparea img { | ||||
|         border: 1px solid $gray-darker; | ||||
|         max-width: 100%; | ||||
|     } | ||||
| 
 | ||||
|     .draghome img, .draghome span { | ||||
|         visibility: hidden; | ||||
|     } | ||||
| 
 | ||||
|     .dragitems .dragitem { | ||||
|         cursor: pointer; | ||||
|         position: absolute; | ||||
|         z-index: 2; | ||||
|     } | ||||
| 
 | ||||
|     .dropzones { | ||||
|         position: absolute; | ||||
|     } | ||||
|     .dropzones svg { | ||||
|         z-index: 3; | ||||
|     } | ||||
| @ -35,31 +26,81 @@ addon-qtype-ddmarker { | ||||
|         z-index: 5; | ||||
|         box-shadow: $core-dd-question-selected-shadow; | ||||
|     } | ||||
|     .dragitems .draghome { | ||||
|         margin: 10px; | ||||
|         display: inline-block; | ||||
| 
 | ||||
|     .dragitems, // Previous to 3.9. | ||||
|     .draghomes { | ||||
|         &.readonly { | ||||
|             .dragitem, | ||||
|             .marker { | ||||
|                 cursor: auto; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         .dragitem, // Previous to 3.9. | ||||
|         .draghome, | ||||
|         .marker { | ||||
|             vertical-align: top; | ||||
|             cursor: pointer; | ||||
|             position: relative; | ||||
|             margin: 10px; | ||||
|             display: inline-block; | ||||
|             &.dragplaceholder { | ||||
|                 display: none; | ||||
|                 visibility: hidden; | ||||
| 
 | ||||
|                 &.active { | ||||
|                     display: inline-block; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             &.unplaced { | ||||
|                 position: relative; | ||||
|             } | ||||
|             &.placed { | ||||
|                 position: absolute; | ||||
|                 opacity: 0.6; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .dragitems.readonly .dragitem { | ||||
|         cursor: auto; | ||||
|     .droparea { | ||||
|         .dragitem, | ||||
|         .marker { | ||||
|             cursor: pointer; | ||||
|             position: absolute; | ||||
|             vertical-align: top; | ||||
|             z-index: 2; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     div.ddarea { | ||||
|         text-align: center; | ||||
|         position: relative; | ||||
|     } | ||||
|     div.ddarea .dropzones, | ||||
|     div.ddarea .markertexts { | ||||
|         top: 0; | ||||
|         left: 0; | ||||
|         min-height: 80px; | ||||
|         position: absolute; | ||||
|         @include text-align('start'); | ||||
|     } | ||||
| 
 | ||||
|     .dropbackground { | ||||
|         margin: 0 auto; | ||||
|     } | ||||
| 
 | ||||
|     div.dragitems div.draghome, div.dragitems div.dragitem, | ||||
|     div.draghome, div.drag { | ||||
|     div.dragitems div.draghome, | ||||
|     div.dragitems div.dragitem, | ||||
|     div.draghome, | ||||
|     div.drag, | ||||
|     div.draghomes div.marker, | ||||
|     div.marker, | ||||
|     div.drag { | ||||
|         font: 13px/1.231 arial,helvetica,clean,sans-serif; | ||||
|     } | ||||
|     div.dragitems span.markertext, | ||||
|     div.draghomes span.markertext, | ||||
|     div.markertexts span.markertext { | ||||
|         margin: 0 5px; | ||||
|         z-index: 2; | ||||
| @ -86,17 +127,18 @@ addon-qtype-ddmarker { | ||||
|         border-color: $yellow; | ||||
|         padding: 5px; | ||||
|         border-radius: 10px; | ||||
|         filter: alpha(opacity=60); | ||||
|         opacity: 0.6; | ||||
|         margin: 5px; | ||||
|         display: inline-block; | ||||
|     } | ||||
|     div.dragitems img.target { | ||||
|     div.dragitems img.target, | ||||
|     div.draghomes img.target { | ||||
|         position: absolute; | ||||
|         left: -7px; /* This must be half the size of the target image, minus 0.5. */ | ||||
|         top: -7px;  /* In other words, this works for a 15x15 cross-hair. */ | ||||
|     } | ||||
|     div.dragitems div.draghome img.target { | ||||
|     div.dragitems div.draghome img.target, | ||||
|     div.draghomes div.marker img.target { | ||||
|         display: none; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -89,14 +89,20 @@ export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent imple | ||||
|             } | ||||
|         } else if (this.question.amdArgs) { | ||||
|             // Moodle version >= 3.6.
 | ||||
|             if (typeof this.question.amdArgs[1] != 'undefined') { | ||||
|                 this.imgSrc = this.question.amdArgs[1]; | ||||
|             let nextIndex = 1; | ||||
|             // Moodle version >= 3.9, imgSrc is not specified, do not advance index.
 | ||||
|             if (typeof this.question.amdArgs[nextIndex] != 'undefined' && typeof this.question.amdArgs[nextIndex] != 'boolean') { | ||||
|                 this.imgSrc = this.question.amdArgs[nextIndex]; | ||||
|                 nextIndex++; | ||||
|             } | ||||
|             if (typeof this.question.amdArgs[2] != 'undefined') { | ||||
|                 this.question.readOnly = this.question.amdArgs[2]; | ||||
| 
 | ||||
|             if (typeof this.question.amdArgs[nextIndex] != 'undefined') { | ||||
|                 this.question.readOnly = this.question.amdArgs[nextIndex]; | ||||
|             } | ||||
|             if (typeof this.question.amdArgs[3] != 'undefined') { | ||||
|                 this.dropZones = this.question.amdArgs[3]; | ||||
|             nextIndex++; | ||||
| 
 | ||||
|             if (typeof this.question.amdArgs[nextIndex] != 'undefined') { | ||||
|                 this.dropZones = this.question.amdArgs[nextIndex]; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -33,7 +33,6 @@ addon-qtype-ddwtos { | ||||
|     } | ||||
|     .draghome, .drag.unplaced{ | ||||
|         border: 1px solid $gray-darker; | ||||
|         border-radius: 5px; | ||||
|     } | ||||
|     .draghome { | ||||
|         visibility: hidden; | ||||
| @ -89,6 +88,28 @@ addon-qtype-ddwtos { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .group2 { | ||||
|         border-radius: 10px 0 0 0; | ||||
|     } | ||||
|     .group3 { | ||||
|         border-radius: 0 10px 0 0; | ||||
|     } | ||||
|     .group4 { | ||||
|         border-radius: 0 0 10px 0; | ||||
|     } | ||||
|     .group5 { | ||||
|         border-radius: 0 0 0 10px; | ||||
|     } | ||||
|     .group6 { | ||||
|         border-radius: 0 10px 10px 0; | ||||
|     } | ||||
|     .group7 { | ||||
|         border-radius: 10px 0 0 10px; | ||||
|     } | ||||
|     .group8 { | ||||
|         border-radius: 10px 10px 10px 10px; | ||||
|     } | ||||
| 
 | ||||
|     sub, sup { | ||||
|         font-size: 80%; | ||||
|         position: relative; | ||||
|  | ||||
| @ -277,7 +277,7 @@ export class CoreQuestionHelperProvider { | ||||
| 
 | ||||
|                 const amdRegExp = new RegExp('require\\(\\[["\']qtype_' + question.type + '/question["\']\\],[^f]*' + | ||||
|                     'function\\(amd\\)[^\\{]*\\{[^a]*amd\\.init\\((["\'](q|question-' + usageId + '-)' + question.slot + | ||||
|                     '["\'].*)\\);', 'm'); | ||||
|                     '["\'].*?)\\);', 'm'); | ||||
|                 const amdMatch = match.match(amdRegExp); | ||||
| 
 | ||||
|                 if (amdMatch) { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user