1717 * Main module for the learningmap editor
1818 *
1919 * @module mod_learningmap/learningmap
20- * @copyright 2025 ISB Bayern
20+ * @copyright 2021-2026 ISB Bayern
2121 * @author Stefan Hanauska <stefan.hanauska@csg-in.de>
2222 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2323 */
2424import { exception as displayException , saveCancel } from 'core/notification' ;
2525import Templates from 'core/templates' ;
2626import placestore from 'mod_learningmap/placestore' ;
2727import * as Str from 'core/str' ;
28+ import { debounce } from 'core/utils' ;
2829
2930const circleRadius = 10 ;
3031
@@ -108,13 +109,13 @@ export const init = async() => {
108109 if ( activitySelector . value ) {
109110 let text = document . getElementById ( 'text' + elementForActivitySelector ) ;
110111 if ( text ) {
111- text . replaceChildren ( svgdoc . createCDATASection (
112+ text . replaceChildren ( svgdoc . createTextNode (
112113 activitySelector . querySelector ( 'option[value="' + activitySelector . value + '"]' ) . textContent
113114 ) ) ;
114115 }
115116 let title = document . getElementById ( 'title' + elementForActivitySelector ) ;
116117 if ( title ) {
117- title . replaceChildren ( svgdoc . createCDATASection (
118+ title . replaceChildren ( svgdoc . createTextNode (
118119 activitySelector . querySelector ( 'option[value="' + activitySelector . value + '"]' ) . textContent
119120 ) ) ;
120121 }
@@ -306,11 +307,11 @@ export const init = async() => {
306307 dragel = el ;
307308 if ( el ) {
308309 el . addEventListener ( 'mousedown' , startDrag ) ;
309- el . addEventListener ( 'mousemove' , drag ) ;
310+ el . addEventListener ( 'mousemove' , debounce ( drag , 5 ) ) ;
310311 el . addEventListener ( 'mouseup' , endDrag ) ;
311312 el . addEventListener ( 'mouseleave' , endDrag ) ;
312313 el . addEventListener ( 'touchstart' , startTouch ) ;
313- el . addEventListener ( 'touchmove' , drag ) ;
314+ el . addEventListener ( 'touchmove' , debounce ( drag , 5 ) ) ;
314315 el . addEventListener ( 'touchend' , endTouch ) ;
315316 el . addEventListener ( 'touchleave' , endTouch ) ;
316317 el . addEventListener ( 'touchcancel' , endTouch ) ;
@@ -336,7 +337,7 @@ export const init = async() => {
336337 pathsToUpdateSecondPoint = placestore . getPathsWithSid ( selectedElement . id ) ;
337338 } else if ( evt . target . nodeName == 'text' ) {
338339 selectedElement = evt . target ;
339- let place = selectedElement . parentNode . querySelector ( '.learningmap-place' ) ;
340+ let place = findPlaceForText ( selectedElement . id ) ;
340341 offset = getMousePosition ( evt ) ;
341342 offset . x -= parseInt ( selectedElement . getAttributeNS ( null , "dx" ) ) + place . cx . baseVal . value ;
342343 offset . y -= parseInt ( selectedElement . getAttributeNS ( null , "dy" ) ) + place . cy . baseVal . value ;
@@ -365,7 +366,7 @@ export const init = async() => {
365366 let cx = coord . x - offset . x ;
366367 let cy = coord . y - offset . y ;
367368 if ( selectedElement . nodeName == 'text' ) {
368- let place = selectedElement . parentNode . querySelector ( '.learningmap-place' ) ;
369+ let place = findPlaceForText ( selectedElement . id ) ;
369370 // Calculate the delta from the current mouse position to the corresponding place.
370371 // coord: current mouse position
371372 // offset: delta from the mouse position to the coordinates of the text node
@@ -641,7 +642,7 @@ export const init = async() => {
641642 // Default value for delta: Circle radius * 1.5 (as a padding)
642643 text . setAttribute ( 'dx' , circleRadius * 1.5 ) ;
643644 text . setAttribute ( 'dy' , circleRadius * 1.5 ) ;
644- let textcontent = svgdoc . createCDATASection ( content ) ;
645+ let textcontent = svgdoc . createTextNode ( content ) ;
645646 text . replaceChildren ( textcontent ) ;
646647 return text ;
647648 }
@@ -689,10 +690,9 @@ export const init = async() => {
689690 * @param {* } child child item to set the link on
690691 * @param {* } id id of the link
691692 * @param {* } title title of the link
692- * @param {* } text text to describe the link
693693 * @returns {any }
694694 */
695- function link ( child , id , title = null , text = null ) {
695+ function link ( child , id , title = null ) {
696696 let link = document . createElementNS ( 'http://www.w3.org/2000/svg' , 'a' ) ;
697697 link . setAttribute ( 'id' , id ) ;
698698 link . setAttribute ( 'xlink:href' , '' ) ;
@@ -701,9 +701,6 @@ export const init = async() => {
701701 if ( title !== null ) {
702702 link . appendChild ( title ) ;
703703 }
704- if ( text !== null ) {
705- link . appendChild ( text ) ;
706- }
707704 return link ;
708705 }
709706
@@ -718,7 +715,7 @@ export const init = async() => {
718715 titlenode = document . createElementNS ( 'http://www.w3.org/2000/svg' , 'title' ) ;
719716 svgnode . appendChild ( titlenode ) ;
720717 }
721- let titlecontent = svgdoc . createCDATASection ( values . title ) ;
718+ let titlecontent = svgdoc . createTextNode ( values . title ) ;
722719 titlenode . replaceChildren ( titlecontent ) ;
723720 titlenode . setAttribute ( 'id' , 'title-' + placestore . getMapid ( ) ) ;
724721
@@ -727,7 +724,7 @@ export const init = async() => {
727724 descnode = document . createElementNS ( 'http://www.w3.org/2000/svg' , 'desc' ) ;
728725 svgnode . appendChild ( descnode ) ;
729726 }
730- let desccontent = svgdoc . createCDATASection ( values . description ) ;
727+ let desccontent = svgdoc . createTextNode ( values . description ) ;
731728 descnode . replaceChildren ( desccontent ) ;
732729 descnode . setAttribute ( 'id' , 'desc-' + placestore . getMapid ( ) ) ;
733730 }
@@ -760,6 +757,10 @@ export const init = async() => {
760757 */
761758 function addPlace ( event ) {
762759 let placesgroup = document . getElementById ( 'placesGroup' ) ;
760+ if ( ! placesgroup ) {
761+ placesgroup = document . getElementById ( 'placesGroup-' + placestore . getMapid ( ) ) ;
762+ }
763+ const textgroup = document . getElementById ( 'textsGroup-' + placestore . getMapid ( ) ) ;
763764 let placeId = 'p' + placestore . getId ( ) ;
764765 let linkId = 'a' + placestore . getId ( ) ;
765766 var CTM = event . target . getScreenCTM ( ) ;
@@ -772,10 +773,12 @@ export const init = async() => {
772773 link (
773774 circle ( cx , cy , circleRadius , 'learningmap-place learningmap-draggable learningmap-emptyplace' , placeId ) ,
774775 linkId ,
775- title ( 'title' + placeId ) ,
776- text ( 'text' + placeId , '' , cx , cy )
776+ title ( 'title' + placeId )
777777 )
778778 ) ;
779+ textgroup . appendChild (
780+ text ( 'text' + placeId , '' , cx , cy )
781+ ) ;
779782 placestore . addPlace ( placeId , linkId ) ;
780783 }
781784
@@ -840,6 +843,9 @@ export const init = async() => {
840843 let pid = 'p' + fid + '_' + sid ;
841844 if ( document . getElementById ( pid ) === null ) {
842845 let pathsgroup = document . getElementById ( 'pathsGroup' ) ;
846+ if ( ! pathsgroup ) {
847+ pathsgroup = document . getElementById ( 'pathsGroup-' + placestore . getMapid ( ) ) ;
848+ }
843849 let first = document . getElementById ( 'p' + fid ) ;
844850 let second = document . getElementById ( 'p' + sid ) ;
845851 if ( pathsgroup && first && second ) {
@@ -871,7 +877,10 @@ export const init = async() => {
871877 placestore . removePlace ( event . target . id ) ;
872878 parent . removeChild ( place ) ;
873879 parent . parentNode . removeChild ( parent ) ;
874-
880+ let textNode = document . getElementById ( 'text' + event . target . id ) ;
881+ if ( textNode ) {
882+ textNode . parentNode . removeChild ( textNode ) ;
883+ }
875884 updateCode ( ) ;
876885 }
877886
@@ -906,6 +915,9 @@ export const init = async() => {
906915 let previewimage = document . getElementsByClassName ( 'realpreview' ) ;
907916 if ( previewimage . length > 0 ) {
908917 let background = document . getElementById ( 'learningmap-background-image' ) ;
918+ if ( ! background ) {
919+ background = document . getElementById ( 'learningmap-background-image-' + placestore . getMapid ( ) ) ;
920+ }
909921 let backgroundurl = previewimage [ 0 ] . getAttribute ( 'src' ) . split ( '?' ) [ 0 ] ;
910922 // If the uploaded file reuses the filename of a previously uploaded image, they differ
911923 // only in the oid. So one has to append the oid to the url.
@@ -922,6 +934,9 @@ export const init = async() => {
922934 */
923935 function registerBackgroundListener ( ) {
924936 let background = document . getElementById ( 'learningmap-background-image' ) ;
937+ if ( ! background ) {
938+ background = document . getElementById ( 'learningmap-background-image-' + placestore . getMapid ( ) ) ;
939+ }
925940 if ( background ) {
926941 background . addEventListener ( 'load' , function ( ) {
927942 background . removeAttribute ( 'height' ) ;
@@ -1069,8 +1084,9 @@ export const init = async() => {
10691084 }
10701085 }
10711086 let placeNode = document . getElementById ( place . id ) ;
1087+ let textGroup = document . getElementById ( 'textsGroup-' + placestore . getMapid ( ) ) ;
10721088 let textNode = text ( 'text' + place . id , content , placeNode . cx . baseVal . value , placeNode . cy . baseVal . value ) ;
1073- placeNode . parentNode . appendChild ( textNode ) ;
1089+ textGroup . appendChild ( textNode ) ;
10741090 }
10751091 }
10761092 }
@@ -1082,4 +1098,14 @@ export const init = async() => {
10821098 let advancedSettings = document . getElementById ( 'learningmap-advanced-settings' ) ;
10831099 advancedSettings . setAttribute ( 'hidden' , '' ) ;
10841100 }
1101+
1102+ /**
1103+ * Returns the place that belongs to the given text id.
1104+ * @param {* } textId
1105+ * @returns {* } The place element
1106+ */
1107+ function findPlaceForText ( textId ) {
1108+ let placename = textId . replace ( 'text' , '' ) ;
1109+ return svgnode . getElementById ( placename ) ;
1110+ }
10851111} ;
0 commit comments