From e1e4a53faa5216e86f5f1a9ca23cb72227b2c045 Mon Sep 17 00:00:00 2001 From: Stefan Hanauska Date: Mon, 22 Jul 2024 07:28:06 +0200 Subject: [PATCH] MBS-8971: Add intro --- amd/build/learningmap.min.js | 2 +- amd/build/learningmap.min.js.map | 2 +- amd/build/placestore.min.js | 2 +- amd/build/placestore.min.js.map | 2 +- amd/src/learningmap.js | 4 +- amd/src/placestore.js | 4 +- .../moodle2/backup_learningmap_stepslib.php | 14 ++- ...estore_learningmap_activity_task.class.php | 32 +++-- .../moodle2/restore_learningmap_stepslib.php | 1 + classes/migrationhelper.php | 119 ++++++++++++++++++ db/install.xml | 4 +- db/upgrade.php | 32 ++++- index.php | 2 +- lang/en/learningmap.php | 4 +- lib.php | 67 +++++----- mod_form.php | 76 +++++++---- styles.css | 4 + templates/cssskeleton.mustache | 2 +- tests/generator/lib.php | 8 +- tests/generator/test.json | 2 +- .../mod_learningmap_activitymanager_test.php | 2 +- tests/mod_learningmap_generation_test.php | 2 +- tests/mod_learningmap_mapworker_test.php | 8 +- version.php | 6 +- view.php | 6 +- 25 files changed, 308 insertions(+), 99 deletions(-) create mode 100644 classes/migrationhelper.php diff --git a/amd/build/learningmap.min.js b/amd/build/learningmap.min.js index 9088ad5..55057a0 100644 --- a/amd/build/learningmap.min.js +++ b/amd/build/learningmap.min.js @@ -1,3 +1,3 @@ -define("mod_learningmap/learningmap",["exports","core/notification","core/templates","mod_learningmap/placestore"],(function(_exports,_notification,_templates,_placestore){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=_interopRequireDefault(_templates),_placestore=_interopRequireDefault(_placestore);const targetPoints_firstPoint=1,targetPoints_secondPoint=2,targetPoints_bezierPoint=3,pathTypes_line=1,pathTypes_quadraticbezier=2;_exports.init=()=>{var offset,dragel,pathsToUpdateFirstPoint,pathsToUpdateSecondPoint;_templates.default.prefetchTemplates(["mod_learningmap/cssskeleton"]);var selectedElement=null,firstPlace=null,secondPlace=null,lastTarget=null,elementForActivitySelector=null,touchstart=!1,touchend=!1,touchmove=0;let mapdiv=document.getElementById("learningmap-editor-map"),code=document.getElementById("id_introeditor_text"),activitySetting=document.getElementById("learningmap-activity-setting"),activitySelector=document.getElementById("learningmap-activity-selector"),activityStarting=document.getElementById("learningmap-activity-starting"),activityTarget=document.getElementById("learningmap-activity-target"),activityHiddenWarning=document.getElementById("learningmap-activity-hidden-warning"),advancedSettingsIcon=document.getElementById("learningmap-advanced-settings-icon"),treeView=document.querySelector(".fp-viewbar .fp-vb-tree");treeView&&treeView.setAttribute("style","display: none;");let iconView=document.querySelector(".fp-viewbar .fp-vb-icons");iconView&&setTimeout((()=>{iconView.dispatchEvent(new Event("click"))}),1e3),activitySelector&&(activitySelector.addEventListener("change",(function(){if(_placestore.default.setActivityId(elementForActivitySelector,activitySelector.value),activitySelector.value){let text=document.getElementById("text"+elementForActivitySelector);text&&(text.textContent=activitySelector.querySelector('option[value="'+activitySelector.value+'"]').textContent);let title=document.getElementById("title"+elementForActivitySelector);title&&(title.textContent=activitySelector.querySelector('option[value="'+activitySelector.value+'"]').textContent),document.getElementById(elementForActivitySelector).classList.remove("learningmap-emptyplace")}else document.getElementById(elementForActivitySelector).classList.add("learningmap-emptyplace");updateActivities(),updateCode()})),activityStarting.addEventListener("change",(function(){activityStarting.checked?_placestore.default.addStartingPlace(elementForActivitySelector):_placestore.default.removeStartingPlace(elementForActivitySelector),updateCode()})),activityTarget.addEventListener("change",(function(){activityTarget.checked?(_placestore.default.addTargetPlace(elementForActivitySelector),document.getElementById(elementForActivitySelector).classList.add("learningmap-targetplace")):(_placestore.default.removeTargetPlace(elementForActivitySelector),document.getElementById(elementForActivitySelector).classList.remove("learningmap-targetplace")),updateCode()})));let placestoreInput=document.getElementsByName("placestore")[0];if(placestoreInput&&_placestore.default.loadJSON(placestoreInput.value),updateActivities(),advancedSettingsIcon){let advancedSettings=document.getElementById("learningmap-advanced-settings");advancedSettingsIcon.addEventListener("click",(function(){null===advancedSettings.getAttribute("hidden")?hideAdvancedSettings():(advancedSettings.removeAttribute("hidden"),hideContextMenu())}));let advancedSettingsClose=document.getElementById("learningmap-advanced-settings-close");advancedSettingsClose&&advancedSettingsClose.addEventListener("click",(function(){advancedSettings.setAttribute("hidden","")})),advancedSettingsLogic("hidepaths",_placestore.default.getHidePaths,_placestore.default.setHidePaths),advancedSettingsLogic("usecheckmark",_placestore.default.getUseCheckmark,_placestore.default.setUseCheckmark),advancedSettingsLogic("hover",_placestore.default.getHover,_placestore.default.setHover),advancedSettingsLogic("pulse",_placestore.default.getPulse,_placestore.default.setPulse),advancedSettingsLogic("showall",_placestore.default.getShowall,_placestore.default.setShowall),advancedSettingsLogic("hidestroke",_placestore.default.getHideStroke,_placestore.default.setHideStroke),advancedSettingsLogic("showtext",_placestore.default.getShowText,_placestore.default.setShowText,(function(){let options=Array.from(activitySelector.getElementsByTagName("option")),places=_placestore.default.getPlaces();for(const place of places)if(null===document.getElementById("text"+place.id)){let content="";for(const option of options)if(option.value==place.linkedActivity){content=option.textContent;break}let placeNode=document.getElementById(place.id),textNode=text("text"+place.id,content,placeNode.cx.baseVal.value,placeNode.cy.baseVal.value);placeNode.parentNode.appendChild(textNode)}})),advancedSettingsLogic("slicemode",_placestore.default.getSliceMode,_placestore.default.setSliceMode),advancedSettingsLogic("showwaygone",_placestore.default.getShowWayGone,_placestore.default.setShowWayGone)}colorChooserLogic("stroke","text"),colorChooserLogic("place"),colorChooserLogic("visited"),code&&mapdiv&&(mapdiv.innerHTML=code.value),refreshBackgroundImage(),function(){let background=document.getElementById("learningmap-background-image");background&&background.addEventListener("load",(function(){background.removeAttribute("height");let height=parseInt(background.getBBox().height),width=background.getBBox().width;_placestore.default.setBackgroundDimensions(width,height),svg.setAttribute("viewBox","0 0 "+_placestore.default.width+" "+_placestore.default.height),background.setAttribute("width",width),background.setAttribute("height",height),updateCode()}))}(),updateCode();let svg=document.getElementById("learningmap-svgmap-"+_placestore.default.getMapid());function showContextMenu(e){if(unselectAll(),hideAdvancedSettings(),activitySetting&&null!==document.getElementById(e.target.id))if(e.touches&&(e=e.touches[0]),e.target.classList.contains("learningmap-place")){e.target.classList.add("learningmap-selected-activity-selector");let activityId=_placestore.default.getActivityId(e.target.id),scalingFactor=mapdiv.clientWidth/800;activitySetting.style.setProperty("--pos-x",e.target.cx.baseVal.value*scalingFactor+"px"),activitySetting.style.setProperty("--pos-y",e.target.cy.baseVal.value*scalingFactor+"px"),activitySetting.style.setProperty("--map-width",mapdiv.clientWidth+"px"),activitySetting.style.setProperty("--map-height",mapdiv.clientHeight+"px"),activitySetting.style.display="block",document.getElementById("learningmap-activity-selector").value=activityId,document.getElementById("learningmap-activity-starting").checked=_placestore.default.isStartingPlace(e.target.id),document.getElementById("learningmap-activity-target").checked=_placestore.default.isTargetPlace(e.target.id),elementForActivitySelector=e.target.id,updateActivities()}else hideContextMenu(),hideAdvancedSettings()}function hideContextMenu(){let e=document.getElementById(elementForActivitySelector);e&&e.classList.remove("learningmap-selected-activity-selector"),activitySetting.style.display="none"}!function(el){dragel=el,el&&(el.addEventListener("mousedown",startDrag),el.addEventListener("mousemove",drag),el.addEventListener("mouseup",endDrag),el.addEventListener("mouseleave",endDrag),el.addEventListener("touchstart",(function(evt){evt.cancelable&&evt.preventDefault();evt.target.classList.contains("learningmap-draggable")||"text"==evt.target.nodeName||"path"==evt.target.nodeName?(touchstart?(dblclickHandler(evt),touchstart=!1):(touchstart=!0,touchmove=0,touchend=!1,setTimeout((evt=>{touchmove<3&&!touchend&&(evt.touches&&(evt=evt.touches[0]),showContextMenu(evt))}),2e3,evt),setTimeout((()=>{touchstart=!1}),300)),startDrag(evt)):touchstart?(dblclickHandler(evt),touchstart=!1):(touchstart=!0,touchend=!1,touchmove=0,setTimeout((()=>{touchstart=!1}),300))})),el.addEventListener("touchmove",drag),el.addEventListener("touchend",endTouch),el.addEventListener("touchleave",endTouch),el.addEventListener("touchcancel",endTouch));function startDrag(evt){if(evt.cancelable&&evt.preventDefault(),pathsToUpdateFirstPoint=[],pathsToUpdateSecondPoint=[],evt.target.classList.contains("learningmap-draggable"))selectedElement=evt.target,(offset=getMousePosition(evt)).x-=parseInt(selectedElement.getAttributeNS(null,"cx")),offset.y-=parseInt(selectedElement.getAttributeNS(null,"cy")),pathsToUpdateFirstPoint=_placestore.default.getPathsWithFid(selectedElement.id),pathsToUpdateSecondPoint=_placestore.default.getPathsWithSid(selectedElement.id);else if("text"==evt.target.nodeName){let place=(selectedElement=evt.target).parentNode.querySelector(".learningmap-place");(offset=getMousePosition(evt)).x-=parseInt(selectedElement.getAttributeNS(null,"dx"))+place.cx.baseVal.value,offset.y-=parseInt(selectedElement.getAttributeNS(null,"dy"))+place.cy.baseVal.value}else if("path"==evt.target.nodeName){selectedElement=evt.target,offset=getMousePosition(evt);let pathPoint=transformCoordinates(evt.layerX,evt.layerY);offset.x+=pathPoint.x,offset.y+=pathPoint.y}}function drag(evt){if(evt.cancelable&&evt.preventDefault(),touchmove++,selectedElement){var coord=getMousePosition(evt);let cx=coord.x-offset.x,cy=coord.y-offset.y;if("text"==selectedElement.nodeName){let place=selectedElement.parentNode.querySelector(".learningmap-place"),dx=coord.x-offset.x-place.cx.baseVal.value,dy=coord.y-offset.y-place.cy.baseVal.value;selectedElement.setAttributeNS(null,"dx",dx),selectedElement.setAttributeNS(null,"dy",dy)}if("path"==selectedElement.nodeName&&selectedElement.setAttribute("d",updatePathDeclaration(selectedElement.getAttribute("d"),coord.x,coord.y,targetPoints_bezierPoint)),"circle"==selectedElement.nodeName){selectedElement.setAttributeNS(null,"cx",cx),selectedElement.setAttributeNS(null,"cy",cy);let textNode=document.getElementById("text"+selectedElement.id);null!==textNode&&(textNode.setAttributeNS(null,"x",cx),textNode.setAttributeNS(null,"y",cy)),pathsToUpdateFirstPoint.forEach((function(path){let pathNode=document.getElementById(path.id);null!==pathNode&&("path"==pathNode.nodeName?pathNode.setAttribute("d",updatePathDeclaration(pathNode.getAttribute("d"),cx,cy,targetPoints_firstPoint)):(pathNode.setAttribute("x1",cx),pathNode.setAttribute("y1",cy)))})),pathsToUpdateSecondPoint.forEach((function(path){let pathNode=document.getElementById(path.id);null!==pathNode&&("path"==pathNode.nodeName?pathNode.setAttribute("d",updatePathDeclaration(pathNode.getAttribute("d"),cx,cy,targetPoints_secondPoint)):(pathNode.setAttribute("x2",cx),pathNode.setAttribute("y2",cy)))}))}}}function endDrag(evt){evt.cancelable&&evt.preventDefault(),selectedElement=null,unselectAll(),updateCode()}function endTouch(evt){selectedElement=null,touchend=!0,touchmove<3&&touchstart?clickHandler(evt):endDrag(evt),evt.cancelable&&evt.preventDefault()}function updatePathDeclaration(oldDefinition,targetX,targetY){let targetP=arguments.length>3&&void 0!==arguments[3]?arguments[3]:targetPoints_firstPoint,parts=oldDefinition.split(" "),fromX=0,fromY=0,toX=0,toY=0,bezierX=0,bezierY=0,pathType=pathTypes_line;for(let i=0;i2&&void 0!==arguments[2]?arguments[2]:null,text=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,link=document.createElementNS("http://www.w3.org/2000/svg","a");link.setAttribute("id",id),link.setAttribute("xlink:href",""),link.appendChild(child),null!==title&&link.appendChild(title);null!==text&&link.appendChild(text);return link}(function(x,y,r,classes,id){let circle=document.createElementNS("http://www.w3.org/2000/svg","circle");return circle.setAttribute("class",classes),circle.setAttribute("id",id),circle.setAttribute("cx",x),circle.setAttribute("cy",y),circle.setAttribute("r",r),circle}(cx,cy,10,"learningmap-place learningmap-draggable learningmap-emptyplace",placeId),linkId,function(id){let title=document.createElementNS("http://www.w3.org/2000/svg","title");return title.setAttribute("id",id),title}("title"+placeId),text("text"+placeId,"",cx,cy))),_placestore.default.addPlace(placeId,linkId)}(event):event.target.classList.contains("learningmap-place")?lastTarget==event.target.id?(lastTarget=null,clickHandler(event)):function(event){let place=document.getElementById(event.target.id),parent=place.parentNode;id=event.target.id,_placestore.default.getTouchingPaths(id).forEach((function(e){removePath(e.id)})),_placestore.default.removePlace(event.target.id),parent.removeChild(place),parent.parentNode.removeChild(parent),updateCode();var id}(event):event.target.classList.contains("learningmap-path")&&removePath(event.target.id),updateCode()}function text(id,content,x,y){let text=document.createElementNS("http://www.w3.org/2000/svg","text");return text.setAttribute("id",id),text.setAttribute("x",x),text.setAttribute("y",y),text.setAttribute("dx",15),text.setAttribute("dy",15),text.textContent=content,text}function clickHandler(event){if(event.preventDefault(),hideContextMenu(),hideAdvancedSettings(),event.target.classList.contains("learningmap-place")&&null===selectedElement)if(null===firstPlace)firstPlace=event.target.id,document.getElementById(firstPlace).classList.add("learningmap-selected");else{secondPlace=event.target.id;let fid=parseInt(firstPlace.replace("p","")),sid=parseInt(secondPlace.replace("p",""));if(sid==fid)return;if(sid0){let background=document.getElementById("learningmap-background-image"),backgroundurl=previewimage[0].getAttribute("src").split("?")[0];previewimage[0].getAttribute("src").split("?")[1].includes("&oid=")&&(backgroundurl+="?oid="+previewimage[0].getAttribute("src").split("&oid=")[1]),background.setAttribute("xlink:href",backgroundurl)}}function updateCSS(){_templates.default.renderForPromise("mod_learningmap/cssskeleton",_placestore.default.getPlacestore()).then((_ref=>{let{html:html,js:js}=_ref;return _templates.default.replaceNode("#learningmap-svgstyle",html,js),updateCode(),!0})).catch((ex=>(0,_notification.exception)(ex)))}function updateActivities(){let activities=_placestore.default.getAllActivities(),options=Array.from(activitySelector.getElementsByTagName("option"));activityHiddenWarning.setAttribute("hidden",""),options.forEach((function(n){activities.includes(n.value)?(n.classList.add("learningmap-used-activity"),n.selected&&1==n.getAttribute("data-activity-hidden")&&activityHiddenWarning.removeAttribute("hidden")):n.classList.remove("learningmap-used-activity")}))}function colorChooserLogic(name){let secondValue=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",colorChooser=document.getElementById("learningmap-color-"+name);colorChooser&&(colorChooser.addEventListener("change",(function(){_placestore.default.setColor(name,colorChooser.value),""!=secondValue&&_placestore.default.setColor(secondValue,colorChooser.value),updateCSS()})),colorChooser.value=_placestore.default.getColor(name))}function advancedSettingsLogic(name,getCall,setCall){let callback=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,settingItem=document.getElementById("learningmap-advanced-setting-"+name);settingItem&&(settingItem.checked=getCall.call(_placestore.default),settingItem.addEventListener("change",(function(){setCall.call(_placestore.default,settingItem.checked),null!==callback&&callback(),updateCSS()})))}function hideAdvancedSettings(){document.getElementById("learningmap-advanced-settings").setAttribute("hidden","")}}})); +define("mod_learningmap/learningmap",["exports","core/notification","core/templates","mod_learningmap/placestore"],(function(_exports,_notification,_templates,_placestore){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=_interopRequireDefault(_templates),_placestore=_interopRequireDefault(_placestore);const targetPoints_firstPoint=1,targetPoints_secondPoint=2,targetPoints_bezierPoint=3,pathTypes_line=1,pathTypes_quadraticbezier=2;_exports.init=()=>{var offset,dragel,pathsToUpdateFirstPoint,pathsToUpdateSecondPoint;_templates.default.prefetchTemplates(["mod_learningmap/cssskeleton"]);var selectedElement=null,firstPlace=null,secondPlace=null,lastTarget=null,elementForActivitySelector=null,touchstart=!1,touchend=!1,touchmove=0;let mapdiv=document.getElementById("learningmap-editor-map"),code=document.getElementById("id_svgcode"),activitySetting=document.getElementById("learningmap-activity-setting"),activitySelector=document.getElementById("learningmap-activity-selector"),activityStarting=document.getElementById("learningmap-activity-starting"),activityTarget=document.getElementById("learningmap-activity-target"),activityHiddenWarning=document.getElementById("learningmap-activity-hidden-warning"),advancedSettingsIcon=document.getElementById("learningmap-advanced-settings-icon"),treeView=document.querySelector(".fp-viewbar .fp-vb-tree");treeView&&treeView.setAttribute("style","display: none;");let iconView=document.querySelector(".fp-viewbar .fp-vb-icons");iconView&&setTimeout((()=>{iconView.dispatchEvent(new Event("click"))}),1e3),activitySelector&&(activitySelector.addEventListener("change",(function(){if(_placestore.default.setActivityId(elementForActivitySelector,activitySelector.value),activitySelector.value){let text=document.getElementById("text"+elementForActivitySelector);text&&(text.textContent=activitySelector.querySelector('option[value="'+activitySelector.value+'"]').textContent);let title=document.getElementById("title"+elementForActivitySelector);title&&(title.textContent=activitySelector.querySelector('option[value="'+activitySelector.value+'"]').textContent),document.getElementById(elementForActivitySelector).classList.remove("learningmap-emptyplace")}else document.getElementById(elementForActivitySelector).classList.add("learningmap-emptyplace");updateActivities(),updateCode()})),activityStarting.addEventListener("change",(function(){activityStarting.checked?_placestore.default.addStartingPlace(elementForActivitySelector):_placestore.default.removeStartingPlace(elementForActivitySelector),updateCode()})),activityTarget.addEventListener("change",(function(){activityTarget.checked?(_placestore.default.addTargetPlace(elementForActivitySelector),document.getElementById(elementForActivitySelector).classList.add("learningmap-targetplace")):(_placestore.default.removeTargetPlace(elementForActivitySelector),document.getElementById(elementForActivitySelector).classList.remove("learningmap-targetplace")),updateCode()})));let placestoreInput=document.getElementsByName("placestore")[0];if(placestoreInput&&_placestore.default.loadJSON(placestoreInput.value),updateActivities(),advancedSettingsIcon){let advancedSettings=document.getElementById("learningmap-advanced-settings");advancedSettingsIcon.addEventListener("click",(function(){null===advancedSettings.getAttribute("hidden")?hideAdvancedSettings():(advancedSettings.removeAttribute("hidden"),hideContextMenu())}));let advancedSettingsClose=document.getElementById("learningmap-advanced-settings-close");advancedSettingsClose&&advancedSettingsClose.addEventListener("click",(function(){advancedSettings.setAttribute("hidden","")})),advancedSettingsLogic("hidepaths",_placestore.default.getHidePaths,_placestore.default.setHidePaths),advancedSettingsLogic("usecheckmark",_placestore.default.getUseCheckmark,_placestore.default.setUseCheckmark),advancedSettingsLogic("hover",_placestore.default.getHover,_placestore.default.setHover),advancedSettingsLogic("pulse",_placestore.default.getPulse,_placestore.default.setPulse),advancedSettingsLogic("showall",_placestore.default.getShowall,_placestore.default.setShowall),advancedSettingsLogic("hidestroke",_placestore.default.getHideStroke,_placestore.default.setHideStroke),advancedSettingsLogic("showtext",_placestore.default.getShowText,_placestore.default.setShowText,(function(){let options=Array.from(activitySelector.getElementsByTagName("option")),places=_placestore.default.getPlaces();for(const place of places)if(null===document.getElementById("text"+place.id)){let content="";for(const option of options)if(option.value==place.linkedActivity){content=option.textContent;break}let placeNode=document.getElementById(place.id),textNode=text("text"+place.id,content,placeNode.cx.baseVal.value,placeNode.cy.baseVal.value);placeNode.parentNode.appendChild(textNode)}})),advancedSettingsLogic("slicemode",_placestore.default.getSliceMode,_placestore.default.setSliceMode),advancedSettingsLogic("showwaygone",_placestore.default.getShowWayGone,_placestore.default.setShowWayGone)}colorChooserLogic("stroke","text"),colorChooserLogic("place"),colorChooserLogic("visited"),code&&mapdiv&&(mapdiv.innerHTML=code.value),refreshBackgroundImage(),function(){let background=document.getElementById("learningmap-background-image");background&&background.addEventListener("load",(function(){background.removeAttribute("height");let height=parseInt(background.getBBox().height),width=background.getBBox().width;_placestore.default.setBackgroundDimensions(width,height),svg.setAttribute("viewBox","0 0 "+_placestore.default.width+" "+_placestore.default.height),background.setAttribute("width",width),background.setAttribute("height",height),updateCode()}))}(),updateCode();let svg=document.getElementById("learningmap-svgmap-"+_placestore.default.getMapid());function showContextMenu(e){if(unselectAll(),hideAdvancedSettings(),activitySetting&&null!==document.getElementById(e.target.id))if(e.touches&&(e=e.touches[0]),e.target.classList.contains("learningmap-place")){e.target.classList.add("learningmap-selected-activity-selector");let activityId=_placestore.default.getActivityId(e.target.id),scalingFactor=mapdiv.clientWidth/800;activitySetting.style.setProperty("--pos-x",e.target.cx.baseVal.value*scalingFactor+"px"),activitySetting.style.setProperty("--pos-y",e.target.cy.baseVal.value*scalingFactor+"px"),activitySetting.style.setProperty("--map-width",mapdiv.clientWidth+"px"),activitySetting.style.setProperty("--map-height",mapdiv.clientHeight+"px"),activitySetting.style.display="block",document.getElementById("learningmap-activity-selector").value=activityId,document.getElementById("learningmap-activity-starting").checked=_placestore.default.isStartingPlace(e.target.id),document.getElementById("learningmap-activity-target").checked=_placestore.default.isTargetPlace(e.target.id),elementForActivitySelector=e.target.id,updateActivities()}else hideContextMenu(),hideAdvancedSettings()}function hideContextMenu(){let e=document.getElementById(elementForActivitySelector);e&&e.classList.remove("learningmap-selected-activity-selector"),activitySetting.style.display="none"}!function(el){dragel=el,el&&(el.addEventListener("mousedown",startDrag),el.addEventListener("mousemove",drag),el.addEventListener("mouseup",endDrag),el.addEventListener("mouseleave",endDrag),el.addEventListener("touchstart",(function(evt){evt.cancelable&&evt.preventDefault();evt.target.classList.contains("learningmap-draggable")||"text"==evt.target.nodeName||"path"==evt.target.nodeName?(touchstart?(dblclickHandler(evt),touchstart=!1):(touchstart=!0,touchmove=0,touchend=!1,setTimeout((evt=>{touchmove<3&&!touchend&&(evt.touches&&(evt=evt.touches[0]),showContextMenu(evt))}),2e3,evt),setTimeout((()=>{touchstart=!1}),300)),startDrag(evt)):touchstart?(dblclickHandler(evt),touchstart=!1):(touchstart=!0,touchend=!1,touchmove=0,setTimeout((()=>{touchstart=!1}),300))})),el.addEventListener("touchmove",drag),el.addEventListener("touchend",endTouch),el.addEventListener("touchleave",endTouch),el.addEventListener("touchcancel",endTouch));function startDrag(evt){if(evt.cancelable&&evt.preventDefault(),pathsToUpdateFirstPoint=[],pathsToUpdateSecondPoint=[],evt.target.classList.contains("learningmap-draggable"))selectedElement=evt.target,(offset=getMousePosition(evt)).x-=parseInt(selectedElement.getAttributeNS(null,"cx")),offset.y-=parseInt(selectedElement.getAttributeNS(null,"cy")),pathsToUpdateFirstPoint=_placestore.default.getPathsWithFid(selectedElement.id),pathsToUpdateSecondPoint=_placestore.default.getPathsWithSid(selectedElement.id);else if("text"==evt.target.nodeName){let place=(selectedElement=evt.target).parentNode.querySelector(".learningmap-place");(offset=getMousePosition(evt)).x-=parseInt(selectedElement.getAttributeNS(null,"dx"))+place.cx.baseVal.value,offset.y-=parseInt(selectedElement.getAttributeNS(null,"dy"))+place.cy.baseVal.value}else if("path"==evt.target.nodeName){selectedElement=evt.target,offset=getMousePosition(evt);let pathPoint=transformCoordinates(evt.layerX,evt.layerY);offset.x+=pathPoint.x,offset.y+=pathPoint.y}}function drag(evt){if(evt.cancelable&&evt.preventDefault(),touchmove++,selectedElement){var coord=getMousePosition(evt);let cx=coord.x-offset.x,cy=coord.y-offset.y;if("text"==selectedElement.nodeName){let place=selectedElement.parentNode.querySelector(".learningmap-place"),dx=coord.x-offset.x-place.cx.baseVal.value,dy=coord.y-offset.y-place.cy.baseVal.value;selectedElement.setAttributeNS(null,"dx",dx),selectedElement.setAttributeNS(null,"dy",dy)}if("path"==selectedElement.nodeName&&selectedElement.setAttribute("d",updatePathDeclaration(selectedElement.getAttribute("d"),coord.x,coord.y,targetPoints_bezierPoint)),"circle"==selectedElement.nodeName){selectedElement.setAttributeNS(null,"cx",cx),selectedElement.setAttributeNS(null,"cy",cy);let textNode=document.getElementById("text"+selectedElement.id);null!==textNode&&(textNode.setAttributeNS(null,"x",cx),textNode.setAttributeNS(null,"y",cy)),pathsToUpdateFirstPoint.forEach((function(path){let pathNode=document.getElementById(path.id);null!==pathNode&&("path"==pathNode.nodeName?pathNode.setAttribute("d",updatePathDeclaration(pathNode.getAttribute("d"),cx,cy,targetPoints_firstPoint)):(pathNode.setAttribute("x1",cx),pathNode.setAttribute("y1",cy)))})),pathsToUpdateSecondPoint.forEach((function(path){let pathNode=document.getElementById(path.id);null!==pathNode&&("path"==pathNode.nodeName?pathNode.setAttribute("d",updatePathDeclaration(pathNode.getAttribute("d"),cx,cy,targetPoints_secondPoint)):(pathNode.setAttribute("x2",cx),pathNode.setAttribute("y2",cy)))}))}}}function endDrag(evt){evt.cancelable&&evt.preventDefault(),selectedElement=null,unselectAll(),updateCode()}function endTouch(evt){selectedElement=null,touchend=!0,touchmove<3&&touchstart?clickHandler(evt):endDrag(evt),evt.cancelable&&evt.preventDefault()}function updatePathDeclaration(oldDefinition,targetX,targetY){let targetP=arguments.length>3&&void 0!==arguments[3]?arguments[3]:targetPoints_firstPoint,parts=oldDefinition.split(" "),fromX=0,fromY=0,toX=0,toY=0,bezierX=0,bezierY=0,pathType=pathTypes_line;for(let i=0;i2&&void 0!==arguments[2]?arguments[2]:null,text=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,link=document.createElementNS("http://www.w3.org/2000/svg","a");link.setAttribute("id",id),link.setAttribute("xlink:href",""),link.appendChild(child),null!==title&&link.appendChild(title);null!==text&&link.appendChild(text);return link}(function(x,y,r,classes,id){let circle=document.createElementNS("http://www.w3.org/2000/svg","circle");return circle.setAttribute("class",classes),circle.setAttribute("id",id),circle.setAttribute("cx",x),circle.setAttribute("cy",y),circle.setAttribute("r",r),circle}(cx,cy,10,"learningmap-place learningmap-draggable learningmap-emptyplace",placeId),linkId,function(id){let title=document.createElementNS("http://www.w3.org/2000/svg","title");return title.setAttribute("id",id),title}("title"+placeId),text("text"+placeId,"",cx,cy))),_placestore.default.addPlace(placeId,linkId)}(event):event.target.classList.contains("learningmap-place")?lastTarget==event.target.id?(lastTarget=null,clickHandler(event)):function(event){let place=document.getElementById(event.target.id),parent=place.parentNode;id=event.target.id,_placestore.default.getTouchingPaths(id).forEach((function(e){removePath(e.id)})),_placestore.default.removePlace(event.target.id),parent.removeChild(place),parent.parentNode.removeChild(parent),updateCode();var id}(event):event.target.classList.contains("learningmap-path")&&removePath(event.target.id),updateCode()}function text(id,content,x,y){let text=document.createElementNS("http://www.w3.org/2000/svg","text");return text.setAttribute("id",id),text.setAttribute("x",x),text.setAttribute("y",y),text.setAttribute("dx",15),text.setAttribute("dy",15),text.textContent=content,text}function clickHandler(event){if(event.preventDefault(),hideContextMenu(),hideAdvancedSettings(),event.target.classList.contains("learningmap-place")&&null===selectedElement)if(null===firstPlace)firstPlace=event.target.id,document.getElementById(firstPlace).classList.add("learningmap-selected");else{secondPlace=event.target.id;let fid=parseInt(firstPlace.replace("p","")),sid=parseInt(secondPlace.replace("p",""));if(sid==fid)return;if(sid0){let background=document.getElementById("learningmap-background-image"),backgroundurl=previewimage[0].getAttribute("src").split("?")[0];previewimage[0].getAttribute("src").split("?")[1].includes("&oid=")&&(backgroundurl+="?oid="+previewimage[0].getAttribute("src").split("&oid=")[1]),background.setAttribute("xlink:href",backgroundurl)}}function updateCSS(){_templates.default.renderForPromise("mod_learningmap/cssskeleton",_placestore.default.getPlacestore()).then((_ref=>{let{html:html,js:js}=_ref;return _templates.default.replaceNode("#learningmap-svgstyle",html,js),updateCode(),!0})).catch((ex=>(0,_notification.exception)(ex)))}function updateActivities(){let activities=_placestore.default.getAllActivities(),options=Array.from(activitySelector.getElementsByTagName("option"));activityHiddenWarning.setAttribute("hidden",""),options.forEach((function(n){activities.includes(n.value)?(n.classList.add("learningmap-used-activity"),n.selected&&1==n.getAttribute("data-activity-hidden")&&activityHiddenWarning.removeAttribute("hidden")):n.classList.remove("learningmap-used-activity")}))}function colorChooserLogic(name){let secondValue=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",colorChooser=document.getElementById("learningmap-color-"+name);colorChooser&&(colorChooser.addEventListener("change",(function(){_placestore.default.setColor(name,colorChooser.value),""!=secondValue&&_placestore.default.setColor(secondValue,colorChooser.value),updateCSS()})),colorChooser.value=_placestore.default.getColor(name))}function advancedSettingsLogic(name,getCall,setCall){let callback=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,settingItem=document.getElementById("learningmap-advanced-setting-"+name);settingItem&&(settingItem.checked=getCall.call(_placestore.default),settingItem.addEventListener("change",(function(){setCall.call(_placestore.default,settingItem.checked),null!==callback&&callback(),updateCSS()})))}function hideAdvancedSettings(){document.getElementById("learningmap-advanced-settings").setAttribute("hidden","")}}})); //# sourceMappingURL=learningmap.min.js.map \ No newline at end of file diff --git a/amd/build/learningmap.min.js.map b/amd/build/learningmap.min.js.map index 49395bc..c79bdf7 100644 --- a/amd/build/learningmap.min.js.map +++ b/amd/build/learningmap.min.js.map @@ -1 +1 @@ -{"version":3,"file":"learningmap.min.js","sources":["../src/learningmap.js"],"sourcesContent":["import {exception as displayException} from 'core/notification';\nimport Templates from 'core/templates';\nimport placestore from 'mod_learningmap/placestore';\n\nconst circleRadius = 10;\n\n// Constants for updatePathDeclaration.\nconst targetPoints = {\n firstPoint: 1,\n secondPoint: 2,\n bezierPoint: 3,\n};\n\nconst pathTypes = {\n line: 1,\n quadraticbezier: 2,\n};\n\nexport const init = () => {\n // Load the needed template on startup for better execution speed.\n Templates.prefetchTemplates(['mod_learningmap/cssskeleton']);\n\n // Variable for storing the mouse offset\n var offset;\n\n // Variable for draggable element\n var dragel;\n\n // Variables for storing the paths that need update of the first or\n // the second coordinates.\n var pathsToUpdateFirstPoint, pathsToUpdateSecondPoint;\n\n // Variables for handling the currently selected elements\n var selectedElement = null,\n firstPlace = null,\n secondPlace = null,\n lastTarget = null;\n\n // Variable for storing the selected element for the activity selector\n var elementForActivitySelector = null;\n\n // Variables for simulating double click on touch devices, set when the\n // corresponding events are handled\n var touchstart = false;\n var touchend = false;\n // Counter for touchmove events\n var touchmove = 0;\n\n // DOM nodes for the editor\n let mapdiv = document.getElementById('learningmap-editor-map');\n let code = document.getElementById('id_introeditor_text');\n\n // DOM nodes for the activity selector\n let activitySetting = document.getElementById('learningmap-activity-setting');\n let activitySelector = document.getElementById('learningmap-activity-selector');\n let activityStarting = document.getElementById('learningmap-activity-starting');\n let activityTarget = document.getElementById('learningmap-activity-target');\n let activityHiddenWarning = document.getElementById('learningmap-activity-hidden-warning');\n let advancedSettingsIcon = document.getElementById('learningmap-advanced-settings-icon');\n\n // Hide tree view as there is no preview file we can attach to\n let treeView = document.querySelector('.fp-viewbar .fp-vb-tree');\n if (treeView) {\n treeView.setAttribute('style', 'display: none;');\n }\n\n // Trigger click event on icon view to ensure that tree view is not active.\n let iconView = document.querySelector('.fp-viewbar .fp-vb-icons');\n if (iconView) {\n // Handle possible delay in form loading.\n setTimeout(() => {\n iconView.dispatchEvent(new Event('click'));\n }, 1000);\n }\n\n // Attach listeners to the activity selector\n if (activitySelector) {\n // Show places that are not linked to an activity\n activitySelector.addEventListener('change', function() {\n placestore.setActivityId(elementForActivitySelector, activitySelector.value);\n if (activitySelector.value) {\n let text = document.getElementById('text' + elementForActivitySelector);\n if (text) {\n text.textContent = activitySelector.querySelector('option[value=\"' + activitySelector.value + '\"]').textContent;\n }\n let title = document.getElementById('title' + elementForActivitySelector);\n if (title) {\n title.textContent =\n activitySelector.querySelector('option[value=\"' + activitySelector.value + '\"]').textContent;\n }\n document.getElementById(elementForActivitySelector).classList.remove('learningmap-emptyplace');\n } else {\n document.getElementById(elementForActivitySelector).classList.add('learningmap-emptyplace');\n }\n updateActivities();\n updateCode();\n });\n // Add / remove a place to the starting places array\n activityStarting.addEventListener('change', function() {\n if (activityStarting.checked) {\n placestore.addStartingPlace(elementForActivitySelector);\n } else {\n placestore.removeStartingPlace(elementForActivitySelector);\n }\n updateCode();\n });\n // Add / remove a place to the target places array\n activityTarget.addEventListener('change', function() {\n if (activityTarget.checked) {\n placestore.addTargetPlace(elementForActivitySelector);\n document.getElementById(elementForActivitySelector).classList.add('learningmap-targetplace');\n } else {\n placestore.removeTargetPlace(elementForActivitySelector);\n document.getElementById(elementForActivitySelector).classList.remove('learningmap-targetplace');\n }\n updateCode();\n });\n }\n\n // Load placestore values from the hidden input field\n let placestoreInput = document.getElementsByName('placestore')[0];\n if (placestoreInput) {\n placestore.loadJSON(placestoreInput.value);\n }\n\n // Mark all activities in the placestore as \"used\".\n updateActivities();\n\n // Attach listeners to the advanced settings div\n if (advancedSettingsIcon) {\n let advancedSettings = document.getElementById('learningmap-advanced-settings');\n advancedSettingsIcon.addEventListener('click', function() {\n if (advancedSettings.getAttribute('hidden') === null) {\n hideAdvancedSettings();\n } else {\n advancedSettings.removeAttribute('hidden');\n hideContextMenu();\n }\n });\n let advancedSettingsClose = document.getElementById('learningmap-advanced-settings-close');\n if (advancedSettingsClose) {\n advancedSettingsClose.addEventListener('click', function() {\n advancedSettings.setAttribute('hidden', '');\n });\n }\n\n advancedSettingsLogic('hidepaths', placestore.getHidePaths, placestore.setHidePaths);\n advancedSettingsLogic('usecheckmark', placestore.getUseCheckmark, placestore.setUseCheckmark);\n advancedSettingsLogic('hover', placestore.getHover, placestore.setHover);\n advancedSettingsLogic('pulse', placestore.getPulse, placestore.setPulse);\n advancedSettingsLogic('showall', placestore.getShowall, placestore.setShowall);\n advancedSettingsLogic('hidestroke', placestore.getHideStroke, placestore.setHideStroke);\n advancedSettingsLogic('showtext', placestore.getShowText, placestore.setShowText, fixPlaceLabels);\n advancedSettingsLogic('slicemode', placestore.getSliceMode, placestore.setSliceMode);\n advancedSettingsLogic('showwaygone', placestore.getShowWayGone, placestore.setShowWayGone);\n }\n\n // Attach listener to the color choosers\n colorChooserLogic('stroke', 'text');\n colorChooserLogic('place');\n colorChooserLogic('visited');\n\n // Get SVG code from the (hidden) textarea field\n if (code && mapdiv) {\n mapdiv.innerHTML = code.value;\n }\n // Reload background image to get the correct width and height values\n refreshBackgroundImage();\n registerBackgroundListener();\n updateCode();\n\n // Enable dragging of places\n let svg = document.getElementById('learningmap-svgmap-' + placestore.getMapid());\n makeDraggable(svg);\n\n // Refresh stylesheet values from placestore\n updateCSS();\n\n // Add listeners for clicking and context menu\n if (mapdiv) {\n mapdiv.addEventListener('dblclick', dblclickHandler);\n mapdiv.addEventListener('click', clickHandler);\n\n mapdiv.addEventListener('contextmenu', function(e) {\n e.preventDefault();\n showContextMenu(e);\n }, false);\n }\n /**\n * Shows the context menu at the current mouse position\n * @param {*} e\n */\n function showContextMenu(e) {\n unselectAll();\n hideAdvancedSettings();\n // Check for the existence of the target (could have vanished since the event started).\n if (activitySetting && document.getElementById(e.target.id) !== null) {\n if (e.touches) {\n e = e.touches[0];\n }\n if (e.target.classList.contains('learningmap-place')) {\n e.target.classList.add('learningmap-selected-activity-selector');\n let activityId = placestore.getActivityId(e.target.id);\n let scalingFactor = mapdiv.clientWidth / 800;\n activitySetting.style.setProperty('--pos-x', e.target.cx.baseVal.value * scalingFactor + 'px');\n activitySetting.style.setProperty('--pos-y', e.target.cy.baseVal.value * scalingFactor + 'px');\n activitySetting.style.setProperty('--map-width', mapdiv.clientWidth + 'px');\n activitySetting.style.setProperty('--map-height', mapdiv.clientHeight + 'px');\n activitySetting.style.display = 'block';\n document.getElementById('learningmap-activity-selector').value = activityId;\n document.getElementById('learningmap-activity-starting').checked = placestore.isStartingPlace(e.target.id);\n document.getElementById('learningmap-activity-target').checked = placestore.isTargetPlace(e.target.id);\n elementForActivitySelector = e.target.id;\n updateActivities();\n } else {\n hideContextMenu();\n hideAdvancedSettings();\n }\n }\n }\n\n /**\n * Hides the context menu\n */\n function hideContextMenu() {\n let e = document.getElementById(elementForActivitySelector);\n if (e) {\n e.classList.remove('learningmap-selected-activity-selector');\n }\n activitySetting.style.display = 'none';\n }\n\n let backgroundfileNode = document.getElementById('id_introeditor_itemid_fieldset');\n if (backgroundfileNode) {\n let observer = new MutationObserver(refreshBackgroundImage);\n observer.observe(backgroundfileNode, {attributes: true, childList: true, subtree: true});\n }\n\n /**\n * Helper function for getting the right coordinates from the mouse\n * @param {*} evt\n * @returns {object}\n */\n function getMousePosition(evt) {\n if (evt.touches) {\n evt = evt.touches[0];\n }\n return transformCoordinates(evt.clientX, evt.clientY);\n }\n\n /**\n * Transforms client coordinates to SVG coordinates\n * @param {number} x x coordinate to transform\n * @param {number} y y coordinate to transform\n * @returns {object} Object containing transformed x and y coordinate\n */\n function transformCoordinates(x, y) {\n var CTM = dragel.getScreenCTM();\n return {\n x: (x - CTM.e) / CTM.a,\n y: (y - CTM.f) / CTM.d\n };\n }\n\n /**\n * Enables dragging on an DOM node\n * @param {*} el\n */\n function makeDraggable(el) {\n dragel = el;\n if (el) {\n el.addEventListener('mousedown', startDrag);\n el.addEventListener('mousemove', drag);\n el.addEventListener('mouseup', endDrag);\n el.addEventListener('mouseleave', endDrag);\n el.addEventListener('touchstart', startTouch);\n el.addEventListener('touchmove', drag);\n el.addEventListener('touchend', endTouch);\n el.addEventListener('touchleave', endTouch);\n el.addEventListener('touchcancel', endTouch);\n }\n\n /**\n * Function called whenn dragging starts.\n * @param {*} evt\n */\n function startDrag(evt) {\n if (evt.cancelable) {\n evt.preventDefault();\n }\n pathsToUpdateFirstPoint = [];\n pathsToUpdateSecondPoint = [];\n if (evt.target.classList.contains('learningmap-draggable')) {\n selectedElement = evt.target;\n offset = getMousePosition(evt);\n offset.x -= parseInt(selectedElement.getAttributeNS(null, \"cx\"));\n offset.y -= parseInt(selectedElement.getAttributeNS(null, \"cy\"));\n // Get paths that need to be updated.\n pathsToUpdateFirstPoint = placestore.getPathsWithFid(selectedElement.id);\n pathsToUpdateSecondPoint = placestore.getPathsWithSid(selectedElement.id);\n } else if (evt.target.nodeName == 'text') {\n selectedElement = evt.target;\n let place = selectedElement.parentNode.querySelector('.learningmap-place');\n offset = getMousePosition(evt);\n offset.x -= parseInt(selectedElement.getAttributeNS(null, \"dx\")) + place.cx.baseVal.value;\n offset.y -= parseInt(selectedElement.getAttributeNS(null, \"dy\")) + place.cy.baseVal.value;\n } else if (evt.target.nodeName == 'path') {\n selectedElement = evt.target;\n offset = getMousePosition(evt);\n let pathPoint = transformCoordinates(evt.layerX, evt.layerY);\n offset.x += pathPoint.x;\n offset.y += pathPoint.y;\n }\n }\n\n /**\n * Function called during dragging. Continuously updates circles center coordinates and the\n * coordinates of the touching paths.\n * @param {*} evt\n */\n function drag(evt) {\n if (evt.cancelable) {\n evt.preventDefault();\n }\n // Count touchmove events\n touchmove++;\n if (selectedElement) {\n var coord = getMousePosition(evt);\n let cx = coord.x - offset.x;\n let cy = coord.y - offset.y;\n if (selectedElement.nodeName == 'text') {\n let place = selectedElement.parentNode.querySelector('.learningmap-place');\n // Calculate the delta from the current mouse position to the corresponding place.\n // coord: current mouse position\n // offset: delta from the mouse position to the coordinates of the text node\n let dx = coord.x - offset.x - place.cx.baseVal.value;\n let dy = coord.y - offset.y - place.cy.baseVal.value;\n selectedElement.setAttributeNS(null, \"dx\", dx);\n selectedElement.setAttributeNS(null, \"dy\", dy);\n }\n if (selectedElement.nodeName == 'path') {\n selectedElement.setAttribute(\n 'd',\n updatePathDeclaration(selectedElement.getAttribute('d'), coord.x, coord.y, targetPoints.bezierPoint)\n );\n }\n if (selectedElement.nodeName == 'circle') {\n selectedElement.setAttributeNS(null, \"cx\", cx);\n selectedElement.setAttributeNS(null, \"cy\", cy);\n let textNode = document.getElementById('text' + selectedElement.id);\n if (textNode !== null) {\n textNode.setAttributeNS(null, 'x', cx);\n textNode.setAttributeNS(null, 'y', cy);\n }\n pathsToUpdateFirstPoint.forEach(function(path) {\n let pathNode = document.getElementById(path.id);\n if (pathNode !== null) {\n if (pathNode.nodeName == 'path') {\n pathNode.setAttribute(\n 'd',\n updatePathDeclaration(pathNode.getAttribute('d'), cx, cy, targetPoints.firstPoint)\n );\n } else {\n pathNode.setAttribute('x1', cx);\n pathNode.setAttribute('y1', cy);\n }\n }\n });\n\n pathsToUpdateSecondPoint.forEach(function(path) {\n let pathNode = document.getElementById(path.id);\n if (pathNode !== null) {\n if (pathNode.nodeName == 'path') {\n pathNode.setAttribute(\n 'd',\n updatePathDeclaration(pathNode.getAttribute('d'), cx, cy, targetPoints.secondPoint)\n );\n } else {\n pathNode.setAttribute('x2', cx);\n pathNode.setAttribute('y2', cy);\n }\n }\n });\n }\n }\n }\n\n /**\n * Function called when dragging ends.\n * @param {*} evt\n */\n function endDrag(evt) {\n if (evt.cancelable) {\n evt.preventDefault();\n }\n selectedElement = null;\n unselectAll();\n updateCode();\n }\n\n /**\n * Function called when touchstart event occurs.\n * @param {*} evt\n */\n function startTouch(evt) {\n if (evt.cancelable) {\n evt.preventDefault();\n }\n if (\n evt.target.classList.contains('learningmap-draggable') ||\n evt.target.nodeName == 'text' ||\n evt.target.nodeName == 'path'\n ) {\n if (!touchstart) {\n touchstart = true;\n touchmove = 0;\n touchend = false;\n setTimeout(\n (evt) => {\n if (touchmove < 3 && !touchend) {\n if (evt.touches) {\n evt = evt.touches[0];\n }\n showContextMenu(evt);\n }\n },\n 2000,\n evt\n );\n setTimeout(\n () => {\n touchstart = false;\n },\n 300);\n } else {\n dblclickHandler(evt);\n touchstart = false;\n }\n startDrag(evt);\n } else {\n if (!touchstart) {\n touchstart = true;\n touchend = false;\n touchmove = 0;\n setTimeout(\n () => {\n touchstart = false;\n },\n 300);\n } else {\n dblclickHandler(evt);\n touchstart = false;\n }\n }\n }\n\n /**\n * Function called when touchend, touchleave or touchcancel event occurs.\n * @param {*} evt\n */\n function endTouch(evt) {\n selectedElement = null;\n touchend = true;\n // If there was only a small move (<3 move events), this also counts as a click.\n if (touchmove < 3 && touchstart) {\n clickHandler(evt);\n } else {\n endDrag(evt);\n }\n if (evt.cancelable) {\n evt.preventDefault();\n }\n }\n\n /**\n * Updates the path declaration of lines and quadratic bezier curves setting one of the points.\n * @param {string} oldDefinition SVG path definition string\n * @param {number} targetX x coordinate of the point to set\n * @param {number} targetY y coordinate of the point to set\n * @param {number} targetP Which point to change (you can use the targetPoints constants here)\n * @returns {string} Updated SVG path definition\n */\n function updatePathDeclaration(oldDefinition, targetX, targetY, targetP = targetPoints.firstPoint) {\n let parts = oldDefinition.split(' ');\n let fromX = 0;\n let fromY = 0;\n let toX = 0;\n let toY = 0;\n let bezierX = 0;\n let bezierY = 0;\n let pathType = pathTypes.line;\n\n // The d attribute of an SVG path in a learning map can have two different formats (in this version):\n // \"M x1 y1 L x2 y2\" Line from x1, y1 to x2, y2\n // \"M x1 y2 Q x3 y3 x2 y2\" Quadratic bezier curve inside the triangle defined by x1, y1, x2, y2 and x3, y3.\n for (let i = 0; i < parts.length; i++) {\n // Every path contains the first point in that way.\n if (parts[i] == 'M') {\n fromX = parseInt(parts[i + 1]);\n fromY = parseInt(parts[i + 2]);\n i += 2;\n }\n // This path is a direct line, so there are only two points in total.\n if (parts[i] == 'L') {\n toX = parseInt(parts[i + 1]);\n toY = parseInt(parts[i + 2]);\n i += 2;\n }\n // This path is a bezier curve, there are three points in total.\n if (parts[i] == 'Q') {\n bezierX = parseInt(parts[i + 1]);\n bezierY = parseInt(parts[i + 2]);\n toX = parseInt(parts[i + 3]);\n toY = parseInt(parts[i + 4]);\n i += 4;\n pathType = pathTypes.quadraticbezier;\n }\n }\n\n switch (targetP) {\n case targetPoints.firstPoint:\n fromX = targetX;\n fromY = targetY;\n break;\n case targetPoints.secondPoint:\n toX = targetX;\n toY = targetY;\n break;\n case targetPoints.bezierPoint:\n // Calculate the third triangle point for the bezier curve.\n bezierX = targetX * 2 - (fromX + toX) * 0.5;\n bezierY = targetY * 2 - (fromY + toY) * 0.5;\n pathType = pathTypes.quadraticbezier;\n break;\n }\n\n if (pathType == pathTypes.quadraticbezier) {\n return 'M ' + fromX + ' ' + fromY + ' Q ' + bezierX + ' ' + bezierY + ', ' + toX + ' ' + toY;\n } else {\n return 'M ' + fromX + ' ' + fromY + ' L ' + toX + ' ' + toY;\n }\n }\n }\n\n /**\n * Updates the form fields for the SVG code and the placestore from the editor.\n */\n function updateCode() {\n if (code && mapdiv) {\n code.innerHTML = mapdiv.innerHTML;\n }\n if (placestoreInput) {\n document.getElementsByName('placestore')[0].value = JSON.stringify(placestore.getPlacestore());\n }\n }\n\n /**\n * Handles double clicks on the map\n * @param {*} event\n */\n function dblclickHandler(event) {\n hideContextMenu();\n hideAdvancedSettings();\n unselectAll();\n if (event.target.classList.contains('learningmap-mapcontainer') ||\n event.target.classList.contains('learningmap-background-image')) {\n addPlace(event);\n } else if (event.target.classList.contains('learningmap-place')) {\n if (lastTarget == event.target.id) {\n lastTarget = null;\n clickHandler(event);\n } else {\n removePlace(event);\n }\n } else if (event.target.classList.contains('learningmap-path')) {\n removePath(event.target.id);\n }\n updateCode();\n }\n\n /**\n * Returns an empty title tag with the given id.\n * @param {*} id id for the title\n * @returns {any}\n */\n function title(id) {\n let title = document.createElementNS('http://www.w3.org/2000/svg', 'title');\n title.setAttribute('id', id);\n return title;\n }\n\n /**\n * Returns an text tag with the given id.\n * @param {*} id id for the text\n * @param {*} content content of the tag\n * @param {*} x x coordinate of the text\n * @param {*} y y coordinate of the text\n * @returns {any}\n */\n function text(id, content, x, y) {\n let text = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n text.setAttribute('id', id);\n text.setAttribute('x', x);\n text.setAttribute('y', y);\n // Default value for delta: Circle radius * 1.5 (as a padding)\n text.setAttribute('dx', circleRadius * 1.5);\n text.setAttribute('dy', circleRadius * 1.5);\n text.textContent = content;\n return text;\n }\n\n /**\n * Returns a circle tag with the given dimensions.\n * @param {*} x x coordinate of the center\n * @param {*} y y coordinate of the center\n * @param {*} r radius\n * @param {*} classes classes to add\n * @param {*} id id of the circle\n * @returns {any}\n */\n function circle(x, y, r, classes, id) {\n let circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n circle.setAttribute('class', classes);\n circle.setAttribute('id', id);\n circle.setAttribute('cx', x);\n circle.setAttribute('cy', y);\n circle.setAttribute('r', r);\n return circle;\n }\n\n /**\n * Returns a path between two points.\n * @param {*} x1 x coordinate of the first point\n * @param {*} y1 y coordinate of the first point\n * @param {*} x2 x coordinate of the second point\n * @param {*} y2 y coordinate of the second point\n * @param {*} classes CSS classes to set\n * @param {*} id id of the path\n * @returns {any}\n */\n function path(x1, y1, x2, y2, classes, id) {\n let path = document.createElementNS('http://www.w3.org/2000/svg', 'path');\n path.setAttribute('class', classes);\n path.setAttribute('id', id);\n path.setAttribute('d', 'M ' + x1 + ' ' + y1 + ' L ' + x2 + ' ' + y2);\n return path;\n }\n\n /**\n * Returns a link around a given child element. This function also adds a title element next\n * to the child for accessibility.\n * @param {*} child child item to set the link on\n * @param {*} id id of the link\n * @param {*} title title of the link\n * @param {*} text text to describe the link\n * @returns {any}\n */\n function link(child, id, title = null, text = null) {\n let link = document.createElementNS('http://www.w3.org/2000/svg', 'a');\n link.setAttribute('id', id);\n link.setAttribute('xlink:href', '');\n link.appendChild(child);\n if (title !== null) {\n link.appendChild(title);\n }\n if (text !== null) {\n link.appendChild(text);\n }\n return link;\n }\n\n /**\n * Adds a place on the SVG map. This function also prepares the code for linking activities\n * and adding titles (for accessibility).\n * @param {*} event event causing the command\n */\n function addPlace(event) {\n let placesgroup = document.getElementById('placesGroup');\n let placeId = 'p' + placestore.getId();\n let linkId = 'a' + placestore.getId();\n var CTM = event.target.getScreenCTM();\n if (event.touches) {\n event = event.touches[0];\n }\n let cx = (event.clientX - CTM.e) / CTM.a;\n let cy = (event.clientY - CTM.f) / CTM.d;\n placesgroup.appendChild(\n link(\n circle(cx, cy, circleRadius, 'learningmap-place learningmap-draggable learningmap-emptyplace', placeId),\n linkId,\n title('title' + placeId),\n text('text' + placeId, '', cx, cy)\n )\n );\n placestore.addPlace(placeId, linkId);\n }\n\n /**\n * Handles single clicks on the background image.\n * @param {*} event click event\n * @returns {void}\n */\n function clickHandler(event) {\n event.preventDefault();\n hideContextMenu();\n hideAdvancedSettings();\n if (event.target.classList.contains('learningmap-place') && selectedElement === null) {\n if (firstPlace === null) {\n firstPlace = event.target.id;\n document.getElementById(firstPlace).classList.add('learningmap-selected');\n } else {\n secondPlace = event.target.id;\n let fid = parseInt(firstPlace.replace('p', ''));\n let sid = parseInt(secondPlace.replace('p', ''));\n if (sid == fid) {\n return;\n }\n if (sid < fid) {\n let z = sid;\n sid = fid;\n fid = z;\n }\n addPath(fid, sid);\n let first = document.getElementById(firstPlace);\n if (first) {\n first.classList.remove('learningmap-selected');\n }\n firstPlace = null;\n lastTarget = secondPlace;\n secondPlace = null;\n }\n } else {\n unselectAll();\n firstPlace = null;\n }\n }\n /**\n * Removes the classes 'learningmap-selected' and 'learningmap-selectet-activity-selector'\n * from all nodes\n */\n function unselectAll() {\n Array.from(document.getElementsByClassName('learningmap-selected')).forEach(function(e) {\n e.classList.remove('learningmap-selected');\n });\n Array.from(document.getElementsByClassName('learningmap-selected-activity-selector')).forEach(function(e) {\n e.classList.remove('learningmap-selected-activity-selector');\n });\n }\n\n /**\n * Adds a path between two places.\n * @param {number} fid id of the first place (meant to be the smaller one)\n * @param {number} sid id of the second place (meant to be the bigger one)\n */\n function addPath(fid, sid) {\n let pid = 'p' + fid + '_' + sid;\n if (document.getElementById(pid) === null) {\n let pathsgroup = document.getElementById('pathsGroup');\n let first = document.getElementById('p' + fid);\n let second = document.getElementById('p' + sid);\n if (pathsgroup && first && second) {\n pathsgroup.appendChild(\n path(\n first.cx.baseVal.value,\n first.cy.baseVal.value,\n second.cx.baseVal.value,\n second.cy.baseVal.value,\n 'learningmap-path',\n pid\n )\n );\n placestore.addPath(pid, 'p' + fid, 'p' + sid);\n }\n }\n }\n\n /**\n * Removes a place from the SVG and the placestore. This function also removes all\n * touching paths and entries in statringplaces / targetplaces linking to the removed\n * place.\n * @param {any} event event causing the remove order\n */\n function removePlace(event) {\n let place = document.getElementById(event.target.id);\n let parent = place.parentNode;\n removePathsTouchingPlace(event.target.id);\n placestore.removePlace(event.target.id);\n parent.removeChild(place);\n parent.parentNode.removeChild(parent);\n\n updateCode();\n }\n\n /**\n * Removes all paths touching a certain place\n * @param {number} id id of the place\n */\n function removePathsTouchingPlace(id) {\n placestore.getTouchingPaths(id).forEach(\n function(e) {\n removePath(e.id);\n }\n );\n }\n\n /**\n * Removes a path from the SVG and from the placestore\n * @param {number} id id of the path\n */\n function removePath(id) {\n let path = document.getElementById(id);\n if (path !== null) {\n path.parentNode.removeChild(path);\n placestore.removePath(id);\n }\n }\n\n /**\n * Sets the background image of the SVG to the current image in filemanager.\n */\n function refreshBackgroundImage() {\n let previewimage = document.getElementsByClassName('realpreview');\n if (previewimage.length > 0) {\n let background = document.getElementById('learningmap-background-image');\n let backgroundurl = previewimage[0].getAttribute('src').split('?')[0];\n // If the uploaded file reuses the filename of a previously uploaded image, they differ\n // only in the oid. So one has to append the oid to the url.\n if (previewimage[0].getAttribute('src').split('?')[1].includes('&oid=')) {\n backgroundurl += '?oid=' + previewimage[0].getAttribute('src').split('&oid=')[1];\n }\n background.setAttribute('xlink:href', backgroundurl);\n }\n }\n\n /**\n * Adds an eventListener to the background image for watching file changes and updating\n * height and width of the image.\n */\n function registerBackgroundListener() {\n let background = document.getElementById('learningmap-background-image');\n if (background) {\n background.addEventListener('load', function() {\n background.removeAttribute('height');\n let height = parseInt(background.getBBox().height);\n let width = background.getBBox().width;\n placestore.setBackgroundDimensions(width, height);\n svg.setAttribute('viewBox', '0 0 ' + placestore.width + ' ' + placestore.height);\n background.setAttribute('width', width);\n background.setAttribute('height', height);\n updateCode();\n });\n }\n }\n\n /**\n * Updates CSS code inside the SVG (called, when one of the colors is changed).\n * Calls updateCode() when completed.\n */\n function updateCSS() {\n Templates.renderForPromise('mod_learningmap/cssskeleton', placestore.getPlacestore())\n .then(({html, js}) => {\n Templates.replaceNode('#learningmap-svgstyle', html, js);\n updateCode();\n return true;\n })\n .catch(ex => displayException(ex));\n }\n\n /**\n * Updates the activity selector to highlight the activities already used\n * and to show the alert for hidden activities.\n */\n function updateActivities() {\n let activities = placestore.getAllActivities();\n let options = Array.from(activitySelector.getElementsByTagName('option'));\n activityHiddenWarning.setAttribute('hidden', '');\n options.forEach(function(n) {\n if (activities.includes(n.value)) {\n n.classList.add('learningmap-used-activity');\n if (n.selected) {\n if (n.getAttribute('data-activity-hidden') == true) {\n activityHiddenWarning.removeAttribute('hidden');\n }\n }\n } else {\n n.classList.remove('learningmap-used-activity');\n }\n });\n }\n\n /**\n * Adds the event listener to the color chooser buttons.\n * @param {*} name name of the color\n * @param {*} secondValue name of a second placestore value that has to be changed along\n */\n function colorChooserLogic(name, secondValue = '') {\n let colorChooser = document.getElementById('learningmap-color-' + name);\n if (colorChooser) {\n colorChooser.addEventListener('change', function() {\n placestore.setColor(name, colorChooser.value);\n if (secondValue != '') {\n placestore.setColor(secondValue, colorChooser.value);\n }\n updateCSS();\n });\n colorChooser.value = placestore.getColor(name);\n }\n }\n\n /**\n * Adds the event listener to advanced settings menu items\n * @param {*} name Name of the item\n * @param {*} getCall Method of placestore to call to read value\n * @param {*} setCall Method of placestore to call to save value\n * @param {*} callback Additional callback after value is saved\n */\n function advancedSettingsLogic(name, getCall, setCall, callback = null) {\n let settingItem = document.getElementById('learningmap-advanced-setting-' + name);\n if (settingItem) {\n settingItem.checked = getCall.call(placestore);\n settingItem.addEventListener('change', function() {\n setCall.call(placestore, settingItem.checked);\n if (callback !== null) {\n callback();\n }\n updateCSS();\n });\n }\n }\n\n /**\n * Adds missing text nodes\n */\n function fixPlaceLabels() {\n let options = Array.from(activitySelector.getElementsByTagName('option'));\n let places = placestore.getPlaces();\n for (const place of places) {\n if (document.getElementById('text' + place.id) === null) {\n let content = '';\n for (const option of options) {\n if (option.value == place.linkedActivity) {\n content = option.textContent;\n break;\n }\n }\n let placeNode = document.getElementById(place.id);\n let textNode = text('text' + place.id, content, placeNode.cx.baseVal.value, placeNode.cy.baseVal.value);\n placeNode.parentNode.appendChild(textNode);\n }\n }\n }\n\n /**\n * Hides the advanced settings menu.\n */\n function hideAdvancedSettings() {\n let advancedSettings = document.getElementById('learningmap-advanced-settings');\n advancedSettings.setAttribute('hidden', '');\n }\n};\n"],"names":["targetPoints","pathTypes","offset","dragel","pathsToUpdateFirstPoint","pathsToUpdateSecondPoint","prefetchTemplates","selectedElement","firstPlace","secondPlace","lastTarget","elementForActivitySelector","touchstart","touchend","touchmove","mapdiv","document","getElementById","code","activitySetting","activitySelector","activityStarting","activityTarget","activityHiddenWarning","advancedSettingsIcon","treeView","querySelector","setAttribute","iconView","setTimeout","dispatchEvent","Event","addEventListener","setActivityId","value","text","textContent","title","classList","remove","add","updateActivities","updateCode","checked","addStartingPlace","removeStartingPlace","addTargetPlace","removeTargetPlace","placestoreInput","getElementsByName","loadJSON","advancedSettings","getAttribute","hideAdvancedSettings","removeAttribute","hideContextMenu","advancedSettingsClose","advancedSettingsLogic","placestore","getHidePaths","setHidePaths","getUseCheckmark","setUseCheckmark","getHover","setHover","getPulse","setPulse","getShowall","setShowall","getHideStroke","setHideStroke","getShowText","setShowText","options","Array","from","getElementsByTagName","places","getPlaces","place","id","content","option","linkedActivity","placeNode","textNode","cx","baseVal","cy","parentNode","appendChild","getSliceMode","setSliceMode","getShowWayGone","setShowWayGone","colorChooserLogic","innerHTML","refreshBackgroundImage","background","height","parseInt","getBBox","width","setBackgroundDimensions","svg","registerBackgroundListener","getMapid","showContextMenu","e","unselectAll","target","touches","contains","activityId","getActivityId","scalingFactor","clientWidth","style","setProperty","clientHeight","display","isStartingPlace","isTargetPlace","el","startDrag","drag","endDrag","evt","cancelable","preventDefault","nodeName","dblclickHandler","endTouch","getMousePosition","x","getAttributeNS","y","getPathsWithFid","getPathsWithSid","pathPoint","transformCoordinates","layerX","layerY","coord","dx","dy","setAttributeNS","updatePathDeclaration","forEach","path","pathNode","clickHandler","oldDefinition","targetX","targetY","targetP","parts","split","fromX","fromY","toX","toY","bezierX","bezierY","pathType","i","length","makeDraggable","updateCSS","backgroundfileNode","MutationObserver","observe","attributes","childList","subtree","clientX","clientY","CTM","getScreenCTM","a","f","d","JSON","stringify","getPlacestore","event","placesgroup","placeId","getId","linkId","child","link","createElementNS","r","classes","circle","addPlace","parent","getTouchingPaths","removePath","removePlace","removeChild","circleRadius","fid","replace","sid","z","pid","pathsgroup","first","second","x1","y1","x2","y2","addPath","getElementsByClassName","previewimage","backgroundurl","includes","renderForPromise","then","_ref","html","js","replaceNode","catch","ex","activities","getAllActivities","n","selected","name","secondValue","colorChooser","setColor","getColor","getCall","setCall","callback","settingItem","call"],"mappings":"+aAOMA,wBACU,EADVA,yBAEW,EAFXA,yBAGW,EAGXC,eACI,EADJA,0BAEe,gBAGD,SAKZC,OAGAC,OAIAC,wBAAyBC,4CAVnBC,kBAAkB,CAAC,oCAazBC,gBAAkB,KAClBC,WAAa,KACbC,YAAc,KACdC,WAAa,KAGbC,2BAA6B,KAI7BC,YAAa,EACbC,UAAW,EAEXC,UAAY,MAGZC,OAASC,SAASC,eAAe,0BACjCC,KAAOF,SAASC,eAAe,uBAG/BE,gBAAkBH,SAASC,eAAe,gCAC1CG,iBAAmBJ,SAASC,eAAe,iCAC3CI,iBAAmBL,SAASC,eAAe,iCAC3CK,eAAiBN,SAASC,eAAe,+BACzCM,sBAAwBP,SAASC,eAAe,uCAChDO,qBAAuBR,SAASC,eAAe,sCAG/CQ,SAAWT,SAASU,cAAc,2BAClCD,UACAA,SAASE,aAAa,QAAS,sBAI/BC,SAAWZ,SAASU,cAAc,4BAClCE,UAEAC,YAAW,KACPD,SAASE,cAAc,IAAIC,MAAM,YAClC,KAIHX,mBAEAA,iBAAiBY,iBAAiB,UAAU,kCAC7BC,cAActB,2BAA4BS,iBAAiBc,OAClEd,iBAAiBc,MAAO,KACpBC,KAAOnB,SAASC,eAAe,OAASN,4BACxCwB,OACAA,KAAKC,YAAchB,iBAAiBM,cAAc,iBAAmBN,iBAAiBc,MAAQ,MAAME,iBAEpGC,MAAQrB,SAASC,eAAe,QAAUN,4BAC1C0B,QACAA,MAAMD,YACFhB,iBAAiBM,cAAc,iBAAmBN,iBAAiBc,MAAQ,MAAME,aAEzFpB,SAASC,eAAeN,4BAA4B2B,UAAUC,OAAO,+BAErEvB,SAASC,eAAeN,4BAA4B2B,UAAUE,IAAI,0BAEtEC,mBACAC,gBAGJrB,iBAAiBW,iBAAiB,UAAU,WACpCX,iBAAiBsB,4BACNC,iBAAiBjC,gDAEjBkC,oBAAoBlC,4BAEnC+B,gBAGJpB,eAAeU,iBAAiB,UAAU,WAClCV,eAAeqB,6BACJG,eAAenC,4BAC1BK,SAASC,eAAeN,4BAA4B2B,UAAUE,IAAI,iDAEvDO,kBAAkBpC,4BAC7BK,SAASC,eAAeN,4BAA4B2B,UAAUC,OAAO,4BAEzEG,qBAKJM,gBAAkBhC,SAASiC,kBAAkB,cAAc,MAC3DD,qCACWE,SAASF,gBAAgBd,OAIxCO,mBAGIjB,qBAAsB,KAClB2B,iBAAmBnC,SAASC,eAAe,iCAC/CO,qBAAqBQ,iBAAiB,SAAS,WACK,OAA5CmB,iBAAiBC,aAAa,UAC9BC,wBAEAF,iBAAiBG,gBAAgB,UACjCC,0BAGJC,sBAAwBxC,SAASC,eAAe,uCAChDuC,uBACAA,sBAAsBxB,iBAAiB,SAAS,WAC5CmB,iBAAiBxB,aAAa,SAAU,OAIhD8B,sBAAsB,YAAaC,oBAAWC,aAAcD,oBAAWE,cACvEH,sBAAsB,eAAgBC,oBAAWG,gBAAiBH,oBAAWI,iBAC7EL,sBAAsB,QAASC,oBAAWK,SAAUL,oBAAWM,UAC/DP,sBAAsB,QAASC,oBAAWO,SAAUP,oBAAWQ,UAC/DT,sBAAsB,UAAWC,oBAAWS,WAAYT,oBAAWU,YACnEX,sBAAsB,aAAcC,oBAAWW,cAAeX,oBAAWY,eACzEb,sBAAsB,WAAYC,oBAAWa,YAAab,oBAAWc,4BA8wBjEC,QAAUC,MAAMC,KAAKvD,iBAAiBwD,qBAAqB,WAC3DC,OAASnB,oBAAWoB,gBACnB,MAAMC,SAASF,UACmC,OAA/C7D,SAASC,eAAe,OAAS8D,MAAMC,IAAc,KACjDC,QAAU,OACT,MAAMC,UAAUT,WACbS,OAAOhD,OAAS6C,MAAMI,eAAgB,CACtCF,QAAUC,OAAO9C,sBAIrBgD,UAAYpE,SAASC,eAAe8D,MAAMC,IAC1CK,SAAWlD,KAAK,OAAS4C,MAAMC,GAAIC,QAASG,UAAUE,GAAGC,QAAQrD,MAAOkD,UAAUI,GAAGD,QAAQrD,OACjGkD,UAAUK,WAAWC,YAAYL,cA1xBzC5B,sBAAsB,YAAaC,oBAAWiC,aAAcjC,oBAAWkC,cACvEnC,sBAAsB,cAAeC,oBAAWmC,eAAgBnC,oBAAWoC,gBAI/EC,kBAAkB,SAAU,QAC5BA,kBAAkB,SAClBA,kBAAkB,WAGd7E,MAAQH,SACRA,OAAOiF,UAAY9E,KAAKgB,OAG5B+D,wCAgqBQC,WAAalF,SAASC,eAAe,gCACrCiF,YACAA,WAAWlE,iBAAiB,QAAQ,WAChCkE,WAAW5C,gBAAgB,cACvB6C,OAASC,SAASF,WAAWG,UAAUF,QACvCG,MAAQJ,WAAWG,UAAUC,0BACtBC,wBAAwBD,MAAOH,QAC1CK,IAAI7E,aAAa,UAAW,OAAS+B,oBAAW4C,MAAQ,IAAM5C,oBAAWyC,QACzED,WAAWvE,aAAa,QAAS2E,OACjCJ,WAAWvE,aAAa,SAAUwE,QAClCzD,gBAzqBZ+D,GACA/D,iBAGI8D,IAAMxF,SAASC,eAAe,sBAAwByC,oBAAWgD,qBAoB5DC,gBAAgBC,MACrBC,cACAxD,uBAEIlC,iBAA4D,OAAzCH,SAASC,eAAe2F,EAAEE,OAAO9B,OAChD4B,EAAEG,UACFH,EAAIA,EAAEG,QAAQ,IAEdH,EAAEE,OAAOxE,UAAU0E,SAAS,qBAAsB,CAClDJ,EAAEE,OAAOxE,UAAUE,IAAI,8CACnByE,WAAavD,oBAAWwD,cAAcN,EAAEE,OAAO9B,IAC/CmC,cAAgBpG,OAAOqG,YAAc,IACzCjG,gBAAgBkG,MAAMC,YAAY,UAAWV,EAAEE,OAAOxB,GAAGC,QAAQrD,MAAQiF,cAAgB,MACzFhG,gBAAgBkG,MAAMC,YAAY,UAAWV,EAAEE,OAAOtB,GAAGD,QAAQrD,MAAQiF,cAAgB,MACzFhG,gBAAgBkG,MAAMC,YAAY,cAAevG,OAAOqG,YAAc,MACtEjG,gBAAgBkG,MAAMC,YAAY,eAAgBvG,OAAOwG,aAAe,MACxEpG,gBAAgBkG,MAAMG,QAAU,QAChCxG,SAASC,eAAe,iCAAiCiB,MAAQ+E,WACjEjG,SAASC,eAAe,iCAAiC0B,QAAUe,oBAAW+D,gBAAgBb,EAAEE,OAAO9B,IACvGhE,SAASC,eAAe,+BAA+B0B,QAAUe,oBAAWgE,cAAcd,EAAEE,OAAO9B,IACnGrE,2BAA6BiG,EAAEE,OAAO9B,GACtCvC,wBAEAc,kBACAF,gCAQHE,sBACDqD,EAAI5F,SAASC,eAAeN,4BAC5BiG,GACAA,EAAEtE,UAAUC,OAAO,0CAEvBpB,gBAAgBkG,MAAMG,QAAU,iBAuCbG,IACnBxH,OAASwH,GACLA,KACAA,GAAG3F,iBAAiB,YAAa4F,WACjCD,GAAG3F,iBAAiB,YAAa6F,MACjCF,GAAG3F,iBAAiB,UAAW8F,SAC/BH,GAAG3F,iBAAiB,aAAc8F,SAClCH,GAAG3F,iBAAiB,uBAiIJ+F,KACZA,IAAIC,YACJD,IAAIE,iBAGJF,IAAIjB,OAAOxE,UAAU0E,SAAS,0BACP,QAAvBe,IAAIjB,OAAOoB,UACY,QAAvBH,IAAIjB,OAAOoB,UAENtH,YAsBDuH,gBAAgBJ,KAChBnH,YAAa,IAtBbA,YAAa,EACbE,UAAY,EACZD,UAAW,EACXgB,YACKkG,MACOjH,UAAY,IAAMD,WACdkH,IAAIhB,UACJgB,IAAMA,IAAIhB,QAAQ,IAEtBJ,gBAAgBoB,QAGxB,IACAA,KAEJlG,YACI,KACIjB,YAAa,IAErB,MAKJgH,UAAUG,MAELnH,YAUDuH,gBAAgBJ,KAChBnH,YAAa,IAVbA,YAAa,EACbC,UAAW,EACXC,UAAY,EACZe,YACI,KACIjB,YAAa,IAErB,SA5KR+G,GAAG3F,iBAAiB,YAAa6F,MACjCF,GAAG3F,iBAAiB,WAAYoG,UAChCT,GAAG3F,iBAAiB,aAAcoG,UAClCT,GAAG3F,iBAAiB,cAAeoG,oBAO9BR,UAAUG,QACXA,IAAIC,YACJD,IAAIE,iBAER7H,wBAA0B,GAC1BC,yBAA2B,GACvB0H,IAAIjB,OAAOxE,UAAU0E,SAAS,yBAC9BzG,gBAAkBwH,IAAIjB,QACtB5G,OAASmI,iBAAiBN,MACnBO,GAAKlC,SAAS7F,gBAAgBgI,eAAe,KAAM,OAC1DrI,OAAOsI,GAAKpC,SAAS7F,gBAAgBgI,eAAe,KAAM,OAE1DnI,wBAA0BsD,oBAAW+E,gBAAgBlI,gBAAgByE,IACrE3E,yBAA2BqD,oBAAWgF,gBAAgBnI,gBAAgByE,SACnE,GAA2B,QAAvB+C,IAAIjB,OAAOoB,SAAoB,KAElCnD,OADJxE,gBAAkBwH,IAAIjB,QACMrB,WAAW/D,cAAc,uBACrDxB,OAASmI,iBAAiBN,MACnBO,GAAKlC,SAAS7F,gBAAgBgI,eAAe,KAAM,OAASxD,MAAMO,GAAGC,QAAQrD,MACpFhC,OAAOsI,GAAKpC,SAAS7F,gBAAgBgI,eAAe,KAAM,OAASxD,MAAMS,GAAGD,QAAQrD,WACjF,GAA2B,QAAvB6F,IAAIjB,OAAOoB,SAAoB,CACtC3H,gBAAkBwH,IAAIjB,OACtB5G,OAASmI,iBAAiBN,SACtBY,UAAYC,qBAAqBb,IAAIc,OAAQd,IAAIe,QACrD5I,OAAOoI,GAAKK,UAAUL,EACtBpI,OAAOsI,GAAKG,UAAUH,YASrBX,KAAKE,QACNA,IAAIC,YACJD,IAAIE,iBAGRnH,YACIP,gBAAiB,KACbwI,MAAQV,iBAAiBN,SACzBzC,GAAKyD,MAAMT,EAAIpI,OAAOoI,EACtB9C,GAAKuD,MAAMP,EAAItI,OAAOsI,KACM,QAA5BjI,gBAAgB2H,SAAoB,KAChCnD,MAAQxE,gBAAgBkF,WAAW/D,cAAc,sBAIjDsH,GAAKD,MAAMT,EAAIpI,OAAOoI,EAAIvD,MAAMO,GAAGC,QAAQrD,MAC3C+G,GAAKF,MAAMP,EAAItI,OAAOsI,EAAIzD,MAAMS,GAAGD,QAAQrD,MAC/C3B,gBAAgB2I,eAAe,KAAM,KAAMF,IAC3CzI,gBAAgB2I,eAAe,KAAM,KAAMD,OAEf,QAA5B1I,gBAAgB2H,UAChB3H,gBAAgBoB,aACZ,IACAwH,sBAAsB5I,gBAAgB6C,aAAa,KAAM2F,MAAMT,EAAGS,MAAMP,EAAGxI,2BAGnD,UAA5BO,gBAAgB2H,SAAsB,CACtC3H,gBAAgB2I,eAAe,KAAM,KAAM5D,IAC3C/E,gBAAgB2I,eAAe,KAAM,KAAM1D,QACvCH,SAAWrE,SAASC,eAAe,OAASV,gBAAgByE,IAC/C,OAAbK,WACAA,SAAS6D,eAAe,KAAM,IAAK5D,IACnCD,SAAS6D,eAAe,KAAM,IAAK1D,KAEvCpF,wBAAwBgJ,SAAQ,SAASC,UACjCC,SAAWtI,SAASC,eAAeoI,KAAKrE,IAC3B,OAAbsE,WACyB,QAArBA,SAASpB,SACToB,SAAS3H,aACL,IACAwH,sBAAsBG,SAASlG,aAAa,KAAMkC,GAAIE,GAAIxF,2BAG9DsJ,SAAS3H,aAAa,KAAM2D,IAC5BgE,SAAS3H,aAAa,KAAM6D,SAKxCnF,yBAAyB+I,SAAQ,SAASC,UAClCC,SAAWtI,SAASC,eAAeoI,KAAKrE,IAC3B,OAAbsE,WACyB,QAArBA,SAASpB,SACToB,SAAS3H,aACL,IACAwH,sBAAsBG,SAASlG,aAAa,KAAMkC,GAAIE,GAAIxF,4BAG9DsJ,SAAS3H,aAAa,KAAM2D,IAC5BgE,SAAS3H,aAAa,KAAM6D,oBAY3CsC,QAAQC,KACTA,IAAIC,YACJD,IAAIE,iBAER1H,gBAAkB,KAClBsG,cACAnE,sBA+DK0F,SAASL,KACdxH,gBAAkB,KAClBM,UAAW,EAEPC,UAAY,GAAKF,WACjB2I,aAAaxB,KAEbD,QAAQC,KAERA,IAAIC,YACJD,IAAIE,0BAYHkB,sBAAsBK,cAAeC,QAASC,aAASC,+DAAU3J,wBAClE4J,MAAQJ,cAAcK,MAAM,KAC5BC,MAAQ,EACRC,MAAQ,EACRC,IAAM,EACNC,IAAM,EACNC,QAAU,EACVC,QAAU,EACVC,SAAWnK,mBAKV,IAAIoK,EAAI,EAAGA,EAAIT,MAAMU,OAAQD,IAEd,KAAZT,MAAMS,KACNP,MAAQ1D,SAASwD,MAAMS,EAAI,IAC3BN,MAAQ3D,SAASwD,MAAMS,EAAI,IAC3BA,GAAK,GAGO,KAAZT,MAAMS,KACNL,IAAM5D,SAASwD,MAAMS,EAAI,IACzBJ,IAAM7D,SAASwD,MAAMS,EAAI,IACzBA,GAAK,GAGO,KAAZT,MAAMS,KACNH,QAAU9D,SAASwD,MAAMS,EAAI,IAC7BF,QAAU/D,SAASwD,MAAMS,EAAI,IAC7BL,IAAM5D,SAASwD,MAAMS,EAAI,IACzBJ,IAAM7D,SAASwD,MAAMS,EAAI,IACzBA,GAAK,EACLD,SAAWnK,kCAIX0J,cACC3J,wBACD8J,MAAQL,QACRM,MAAQL,mBAEP1J,yBACDgK,IAAMP,QACNQ,IAAMP,mBAEL1J,yBAEDkK,QAAoB,EAAVT,QAA8B,IAAfK,MAAQE,KACjCG,QAAoB,EAAVT,QAA8B,IAAfK,MAAQE,KACjCG,SAAWnK,iCAIfmK,UAAYnK,0BACL,KAAO6J,MAAQ,IAAMC,MAAQ,MAAQG,QAAU,IAAMC,QAAU,KAAOH,IAAM,IAAMC,IAElF,KAAOH,MAAQ,IAAMC,MAAQ,MAAQC,IAAM,IAAMC,KA9WpEM,CAAc/D,KAGdgE,YAGIzJ,SACAA,OAAOiB,iBAAiB,WAAYmG,iBACpCpH,OAAOiB,iBAAiB,QAASuH,cAEjCxI,OAAOiB,iBAAiB,eAAe,SAAS4E,GAC5CA,EAAEqB,iBACFtB,gBAAgBC,MACjB,QA8CH6D,mBAAqBzJ,SAASC,eAAe,qCAC7CwJ,mBAAoB,CACL,IAAIC,iBAAiBzE,wBAC3B0E,QAAQF,mBAAoB,CAACG,YAAY,EAAMC,WAAW,EAAMC,SAAS,aAQ7EzC,iBAAiBN,YAClBA,IAAIhB,UACJgB,IAAMA,IAAIhB,QAAQ,IAEf6B,qBAAqBb,IAAIgD,QAAShD,IAAIiD,kBASxCpC,qBAAqBN,EAAGE,OACzByC,IAAM9K,OAAO+K,qBACV,CACH5C,GAAIA,EAAI2C,IAAIrE,GAAKqE,IAAIE,EACrB3C,GAAIA,EAAIyC,IAAIG,GAAKH,IAAII,YA+RpB3I,aACDxB,MAAQH,SACRG,KAAK8E,UAAYjF,OAAOiF,WAExBhD,kBACAhC,SAASiC,kBAAkB,cAAc,GAAGf,MAAQoJ,KAAKC,UAAU7H,oBAAW8H,2BAQ7ErD,gBAAgBsD,OACrBlI,kBACAF,uBACAwD,cACI4E,MAAM3E,OAAOxE,UAAU0E,SAAS,6BAChCyE,MAAM3E,OAAOxE,UAAU0E,SAAS,yCA+GtByE,WACVC,YAAc1K,SAASC,eAAe,eACtC0K,QAAU,IAAMjI,oBAAWkI,QAC3BC,OAAS,IAAMnI,oBAAWkI,YAC1BX,IAAMQ,MAAM3E,OAAOoE,eACnBO,MAAM1E,UACN0E,MAAQA,MAAM1E,QAAQ,QAEtBzB,IAAMmG,MAAMV,QAAUE,IAAIrE,GAAKqE,IAAIE,EACnC3F,IAAMiG,MAAMT,QAAUC,IAAIG,GAAKH,IAAII,EACvCK,YAAYhG,qBA7BFoG,MAAO9G,QAAI3C,6DAAQ,KAAMF,4DAAO,KACtC4J,KAAO/K,SAASgL,gBAAgB,6BAA8B,KAClED,KAAKpK,aAAa,KAAMqD,IACxB+G,KAAKpK,aAAa,aAAc,IAChCoK,KAAKrG,YAAYoG,OACH,OAAVzJ,OACA0J,KAAKrG,YAAYrD,OAER,OAATF,MACA4J,KAAKrG,YAAYvD,aAEd4J,KAmBHA,UAnEQzD,EAAGE,EAAGyD,EAAGC,QAASlH,QAC1BmH,OAASnL,SAASgL,gBAAgB,6BAA8B,iBACpEG,OAAOxK,aAAa,QAASuK,SAC7BC,OAAOxK,aAAa,KAAMqD,IAC1BmH,OAAOxK,aAAa,KAAM2G,GAC1B6D,OAAOxK,aAAa,KAAM6G,GAC1B2D,OAAOxK,aAAa,IAAKsK,GAClBE,OA6DCA,CAAO7G,GAAIE,GA5qBN,GA4qBwB,iEAAkEmG,SAC/FE,gBAxGG7G,QACP3C,MAAQrB,SAASgL,gBAAgB,6BAA8B,gBACnE3J,MAAMV,aAAa,KAAMqD,IAClB3C,MAsGCA,CAAM,QAAUsJ,SAChBxJ,KAAK,OAASwJ,QAAS,GAAIrG,GAAIE,0BAG5B4G,SAAST,QAASE,QAhIzBO,CAASX,OACFA,MAAM3E,OAAOxE,UAAU0E,SAAS,qBACnCtG,YAAc+K,MAAM3E,OAAO9B,IAC3BtE,WAAa,KACb6I,aAAakC,iBAoNJA,WACb1G,MAAQ/D,SAASC,eAAewK,MAAM3E,OAAO9B,IAC7CqH,OAAStH,MAAMU,WAaWT,GAZLyG,MAAM3E,OAAO9B,uBAa3BsH,iBAAiBtH,IAAIoE,SAC5B,SAASxC,GACL2F,WAAW3F,EAAE5B,2BAdVwH,YAAYf,MAAM3E,OAAO9B,IACpCqH,OAAOI,YAAY1H,OACnBsH,OAAO5G,WAAWgH,YAAYJ,QAE9B3J,iBAO8BsC,GAjOtBwH,CAAYf,OAETA,MAAM3E,OAAOxE,UAAU0E,SAAS,qBACvCuF,WAAWd,MAAM3E,OAAO9B,IAE5BtC,sBAsBMP,KAAK6C,GAAIC,QAASqD,EAAGE,OACvBrG,KAAOnB,SAASgL,gBAAgB,6BAA8B,eAClE7J,KAAKR,aAAa,KAAMqD,IACxB7C,KAAKR,aAAa,IAAK2G,GACvBnG,KAAKR,aAAa,IAAK6G,GAEvBrG,KAAKR,aAAa,KAAM+K,IACxBvK,KAAKR,aAAa,KAAM+K,IACxBvK,KAAKC,YAAc6C,QACZ9C,cA8FFoH,aAAakC,UAClBA,MAAMxD,iBACN1E,kBACAF,uBACIoI,MAAM3E,OAAOxE,UAAU0E,SAAS,sBAA4C,OAApBzG,mBACrC,OAAfC,WACAA,WAAaiL,MAAM3E,OAAO9B,GAC1BhE,SAASC,eAAeT,YAAY8B,UAAUE,IAAI,4BAC/C,CACH/B,YAAcgL,MAAM3E,OAAO9B,OACvB2H,IAAMvG,SAAS5F,WAAWoM,QAAQ,IAAK,KACvCC,IAAMzG,SAAS3F,YAAYmM,QAAQ,IAAK,QACxCC,KAAOF,cAGPE,IAAMF,IAAK,KACPG,EAAID,IACRA,IAAMF,IACNA,IAAMG,YAkCLH,IAAKE,SACdE,IAAM,IAAMJ,IAAM,IAAME,OACS,OAAjC7L,SAASC,eAAe8L,KAAe,KACnCC,WAAahM,SAASC,eAAe,cACrCgM,MAAQjM,SAASC,eAAe,IAAM0L,KACtCO,OAASlM,SAASC,eAAe,IAAM4L,KACvCG,YAAcC,OAASC,SACvBF,WAAWtH,qBAzHRyH,GAAIC,GAAIC,GAAIC,GAAIpB,QAASlH,QAChCqE,KAAOrI,SAASgL,gBAAgB,6BAA8B,eAClE3C,KAAK1H,aAAa,QAASuK,SAC3B7C,KAAK1H,aAAa,KAAMqD,IACxBqE,KAAK1H,aAAa,IAAK,KAAOwL,GAAK,IAAMC,GAAK,MAAQC,GAAK,IAAMC,IAC1DjE,KAqHKA,CACI4D,MAAM3H,GAAGC,QAAQrD,MACjB+K,MAAMzH,GAAGD,QAAQrD,MACjBgL,OAAO5H,GAAGC,QAAQrD,MAClBgL,OAAO1H,GAAGD,QAAQrD,MAClB,mBACA6K,0BAGGQ,QAAQR,IAAK,IAAMJ,IAAK,IAAME,OAjDzCU,CAAQZ,IAAKE,SACTI,MAAQjM,SAASC,eAAeT,YAChCyM,OACAA,MAAM3K,UAAUC,OAAO,wBAE3B/B,WAAa,KACbE,WAAaD,YACbA,YAAc,UAGlBoG,cACArG,WAAa,cAOZqG,cACLnC,MAAMC,KAAK3D,SAASwM,uBAAuB,yBAAyBpE,SAAQ,SAASxC,GACjFA,EAAEtE,UAAUC,OAAO,2BAEvBmC,MAAMC,KAAK3D,SAASwM,uBAAuB,2CAA2CpE,SAAQ,SAASxC,GACnGA,EAAEtE,UAAUC,OAAO,sDAgElBgK,WAAWvH,QACZqE,KAAOrI,SAASC,eAAe+D,IACtB,OAATqE,OACAA,KAAK5D,WAAWgH,YAAYpD,0BACjBkD,WAAWvH,cAOrBiB,6BACDwH,aAAezM,SAASwM,uBAAuB,kBAC/CC,aAAanD,OAAS,EAAG,KACrBpE,WAAalF,SAASC,eAAe,gCACrCyM,cAAgBD,aAAa,GAAGrK,aAAa,OAAOyG,MAAM,KAAK,GAG/D4D,aAAa,GAAGrK,aAAa,OAAOyG,MAAM,KAAK,GAAG8D,SAAS,WAC3DD,eAAiB,QAAUD,aAAa,GAAGrK,aAAa,OAAOyG,MAAM,SAAS,IAElF3D,WAAWvE,aAAa,aAAc+L,yBA4BrClD,+BACKoD,iBAAiB,8BAA+BlK,oBAAW8H,iBAChEqC,MAAKC,WAACC,KAACA,KAADC,GAAOA,mCACAC,YAAY,wBAAyBF,KAAMC,IACrDtL,cACO,KAEVwL,OAAMC,KAAM,2BAAiBA,eAO7B1L,uBACD2L,WAAa1K,oBAAW2K,mBACxB5J,QAAUC,MAAMC,KAAKvD,iBAAiBwD,qBAAqB,WAC/DrD,sBAAsBI,aAAa,SAAU,IAC7C8C,QAAQ2E,SAAQ,SAASkF,GACjBF,WAAWT,SAASW,EAAEpM,QACtBoM,EAAEhM,UAAUE,IAAI,6BACZ8L,EAAEC,UAC4C,GAA1CD,EAAElL,aAAa,yBACf7B,sBAAsB+B,gBAAgB,WAI9CgL,EAAEhM,UAAUC,OAAO,yCAUtBwD,kBAAkByI,UAAMC,mEAAc,GACvCC,aAAe1N,SAASC,eAAe,qBAAuBuN,MAC9DE,eACAA,aAAa1M,iBAAiB,UAAU,+BACzB2M,SAASH,KAAME,aAAaxM,OACpB,IAAfuM,iCACWE,SAASF,YAAaC,aAAaxM,OAElDsI,eAEJkE,aAAaxM,MAAQwB,oBAAWkL,SAASJ,gBAWxC/K,sBAAsB+K,KAAMK,QAASC,aAASC,gEAAW,KAC1DC,YAAchO,SAASC,eAAe,gCAAkCuN,MACxEQ,cACAA,YAAYrM,QAAUkM,QAAQI,KAAKvL,qBACnCsL,YAAYhN,iBAAiB,UAAU,WACnC8M,QAAQG,KAAKvL,oBAAYsL,YAAYrM,SACpB,OAAboM,UACAA,WAEJvE,yBA8BHnH,uBACkBrC,SAASC,eAAe,iCAC9BU,aAAa,SAAU"} \ No newline at end of file +{"version":3,"file":"learningmap.min.js","sources":["../src/learningmap.js"],"sourcesContent":["import {exception as displayException} from 'core/notification';\nimport Templates from 'core/templates';\nimport placestore from 'mod_learningmap/placestore';\n\nconst circleRadius = 10;\n\n// Constants for updatePathDeclaration.\nconst targetPoints = {\n firstPoint: 1,\n secondPoint: 2,\n bezierPoint: 3,\n};\n\nconst pathTypes = {\n line: 1,\n quadraticbezier: 2,\n};\n\nexport const init = () => {\n // Load the needed template on startup for better execution speed.\n Templates.prefetchTemplates(['mod_learningmap/cssskeleton']);\n\n // Variable for storing the mouse offset\n var offset;\n\n // Variable for draggable element\n var dragel;\n\n // Variables for storing the paths that need update of the first or\n // the second coordinates.\n var pathsToUpdateFirstPoint, pathsToUpdateSecondPoint;\n\n // Variables for handling the currently selected elements\n var selectedElement = null,\n firstPlace = null,\n secondPlace = null,\n lastTarget = null;\n\n // Variable for storing the selected element for the activity selector\n var elementForActivitySelector = null;\n\n // Variables for simulating double click on touch devices, set when the\n // corresponding events are handled\n var touchstart = false;\n var touchend = false;\n // Counter for touchmove events\n var touchmove = 0;\n\n // DOM nodes for the editor\n let mapdiv = document.getElementById('learningmap-editor-map');\n let code = document.getElementById('id_svgcode');\n\n // DOM nodes for the activity selector\n let activitySetting = document.getElementById('learningmap-activity-setting');\n let activitySelector = document.getElementById('learningmap-activity-selector');\n let activityStarting = document.getElementById('learningmap-activity-starting');\n let activityTarget = document.getElementById('learningmap-activity-target');\n let activityHiddenWarning = document.getElementById('learningmap-activity-hidden-warning');\n let advancedSettingsIcon = document.getElementById('learningmap-advanced-settings-icon');\n\n // Hide tree view as there is no preview file we can attach to\n let treeView = document.querySelector('.fp-viewbar .fp-vb-tree');\n if (treeView) {\n treeView.setAttribute('style', 'display: none;');\n }\n\n // Trigger click event on icon view to ensure that tree view is not active.\n let iconView = document.querySelector('.fp-viewbar .fp-vb-icons');\n if (iconView) {\n // Handle possible delay in form loading.\n setTimeout(() => {\n iconView.dispatchEvent(new Event('click'));\n }, 1000);\n }\n\n // Attach listeners to the activity selector\n if (activitySelector) {\n // Show places that are not linked to an activity\n activitySelector.addEventListener('change', function() {\n placestore.setActivityId(elementForActivitySelector, activitySelector.value);\n if (activitySelector.value) {\n let text = document.getElementById('text' + elementForActivitySelector);\n if (text) {\n text.textContent = activitySelector.querySelector('option[value=\"' + activitySelector.value + '\"]').textContent;\n }\n let title = document.getElementById('title' + elementForActivitySelector);\n if (title) {\n title.textContent =\n activitySelector.querySelector('option[value=\"' + activitySelector.value + '\"]').textContent;\n }\n document.getElementById(elementForActivitySelector).classList.remove('learningmap-emptyplace');\n } else {\n document.getElementById(elementForActivitySelector).classList.add('learningmap-emptyplace');\n }\n updateActivities();\n updateCode();\n });\n // Add / remove a place to the starting places array\n activityStarting.addEventListener('change', function() {\n if (activityStarting.checked) {\n placestore.addStartingPlace(elementForActivitySelector);\n } else {\n placestore.removeStartingPlace(elementForActivitySelector);\n }\n updateCode();\n });\n // Add / remove a place to the target places array\n activityTarget.addEventListener('change', function() {\n if (activityTarget.checked) {\n placestore.addTargetPlace(elementForActivitySelector);\n document.getElementById(elementForActivitySelector).classList.add('learningmap-targetplace');\n } else {\n placestore.removeTargetPlace(elementForActivitySelector);\n document.getElementById(elementForActivitySelector).classList.remove('learningmap-targetplace');\n }\n updateCode();\n });\n }\n\n // Load placestore values from the hidden input field\n let placestoreInput = document.getElementsByName('placestore')[0];\n if (placestoreInput) {\n placestore.loadJSON(placestoreInput.value);\n }\n\n // Mark all activities in the placestore as \"used\".\n updateActivities();\n\n // Attach listeners to the advanced settings div\n if (advancedSettingsIcon) {\n let advancedSettings = document.getElementById('learningmap-advanced-settings');\n advancedSettingsIcon.addEventListener('click', function() {\n if (advancedSettings.getAttribute('hidden') === null) {\n hideAdvancedSettings();\n } else {\n advancedSettings.removeAttribute('hidden');\n hideContextMenu();\n }\n });\n let advancedSettingsClose = document.getElementById('learningmap-advanced-settings-close');\n if (advancedSettingsClose) {\n advancedSettingsClose.addEventListener('click', function() {\n advancedSettings.setAttribute('hidden', '');\n });\n }\n\n advancedSettingsLogic('hidepaths', placestore.getHidePaths, placestore.setHidePaths);\n advancedSettingsLogic('usecheckmark', placestore.getUseCheckmark, placestore.setUseCheckmark);\n advancedSettingsLogic('hover', placestore.getHover, placestore.setHover);\n advancedSettingsLogic('pulse', placestore.getPulse, placestore.setPulse);\n advancedSettingsLogic('showall', placestore.getShowall, placestore.setShowall);\n advancedSettingsLogic('hidestroke', placestore.getHideStroke, placestore.setHideStroke);\n advancedSettingsLogic('showtext', placestore.getShowText, placestore.setShowText, fixPlaceLabels);\n advancedSettingsLogic('slicemode', placestore.getSliceMode, placestore.setSliceMode);\n advancedSettingsLogic('showwaygone', placestore.getShowWayGone, placestore.setShowWayGone);\n }\n\n // Attach listener to the color choosers\n colorChooserLogic('stroke', 'text');\n colorChooserLogic('place');\n colorChooserLogic('visited');\n\n // Get SVG code from the (hidden) textarea field\n if (code && mapdiv) {\n mapdiv.innerHTML = code.value;\n }\n // Reload background image to get the correct width and height values\n refreshBackgroundImage();\n registerBackgroundListener();\n updateCode();\n\n // Enable dragging of places\n let svg = document.getElementById('learningmap-svgmap-' + placestore.getMapid());\n makeDraggable(svg);\n\n // Refresh stylesheet values from placestore\n updateCSS();\n\n // Add listeners for clicking and context menu\n if (mapdiv) {\n mapdiv.addEventListener('dblclick', dblclickHandler);\n mapdiv.addEventListener('click', clickHandler);\n\n mapdiv.addEventListener('contextmenu', function(e) {\n e.preventDefault();\n showContextMenu(e);\n }, false);\n }\n /**\n * Shows the context menu at the current mouse position\n * @param {*} e\n */\n function showContextMenu(e) {\n unselectAll();\n hideAdvancedSettings();\n // Check for the existence of the target (could have vanished since the event started).\n if (activitySetting && document.getElementById(e.target.id) !== null) {\n if (e.touches) {\n e = e.touches[0];\n }\n if (e.target.classList.contains('learningmap-place')) {\n e.target.classList.add('learningmap-selected-activity-selector');\n let activityId = placestore.getActivityId(e.target.id);\n let scalingFactor = mapdiv.clientWidth / 800;\n activitySetting.style.setProperty('--pos-x', e.target.cx.baseVal.value * scalingFactor + 'px');\n activitySetting.style.setProperty('--pos-y', e.target.cy.baseVal.value * scalingFactor + 'px');\n activitySetting.style.setProperty('--map-width', mapdiv.clientWidth + 'px');\n activitySetting.style.setProperty('--map-height', mapdiv.clientHeight + 'px');\n activitySetting.style.display = 'block';\n document.getElementById('learningmap-activity-selector').value = activityId;\n document.getElementById('learningmap-activity-starting').checked = placestore.isStartingPlace(e.target.id);\n document.getElementById('learningmap-activity-target').checked = placestore.isTargetPlace(e.target.id);\n elementForActivitySelector = e.target.id;\n updateActivities();\n } else {\n hideContextMenu();\n hideAdvancedSettings();\n }\n }\n }\n\n /**\n * Hides the context menu\n */\n function hideContextMenu() {\n let e = document.getElementById(elementForActivitySelector);\n if (e) {\n e.classList.remove('learningmap-selected-activity-selector');\n }\n activitySetting.style.display = 'none';\n }\n\n let backgroundfileNode = document.getElementById('id_backgroundfile_fieldset');\n if (backgroundfileNode) {\n let observer = new MutationObserver(refreshBackgroundImage);\n observer.observe(backgroundfileNode, {attributes: true, childList: true, subtree: true});\n }\n\n /**\n * Helper function for getting the right coordinates from the mouse\n * @param {*} evt\n * @returns {object}\n */\n function getMousePosition(evt) {\n if (evt.touches) {\n evt = evt.touches[0];\n }\n return transformCoordinates(evt.clientX, evt.clientY);\n }\n\n /**\n * Transforms client coordinates to SVG coordinates\n * @param {number} x x coordinate to transform\n * @param {number} y y coordinate to transform\n * @returns {object} Object containing transformed x and y coordinate\n */\n function transformCoordinates(x, y) {\n var CTM = dragel.getScreenCTM();\n return {\n x: (x - CTM.e) / CTM.a,\n y: (y - CTM.f) / CTM.d\n };\n }\n\n /**\n * Enables dragging on an DOM node\n * @param {*} el\n */\n function makeDraggable(el) {\n dragel = el;\n if (el) {\n el.addEventListener('mousedown', startDrag);\n el.addEventListener('mousemove', drag);\n el.addEventListener('mouseup', endDrag);\n el.addEventListener('mouseleave', endDrag);\n el.addEventListener('touchstart', startTouch);\n el.addEventListener('touchmove', drag);\n el.addEventListener('touchend', endTouch);\n el.addEventListener('touchleave', endTouch);\n el.addEventListener('touchcancel', endTouch);\n }\n\n /**\n * Function called whenn dragging starts.\n * @param {*} evt\n */\n function startDrag(evt) {\n if (evt.cancelable) {\n evt.preventDefault();\n }\n pathsToUpdateFirstPoint = [];\n pathsToUpdateSecondPoint = [];\n if (evt.target.classList.contains('learningmap-draggable')) {\n selectedElement = evt.target;\n offset = getMousePosition(evt);\n offset.x -= parseInt(selectedElement.getAttributeNS(null, \"cx\"));\n offset.y -= parseInt(selectedElement.getAttributeNS(null, \"cy\"));\n // Get paths that need to be updated.\n pathsToUpdateFirstPoint = placestore.getPathsWithFid(selectedElement.id);\n pathsToUpdateSecondPoint = placestore.getPathsWithSid(selectedElement.id);\n } else if (evt.target.nodeName == 'text') {\n selectedElement = evt.target;\n let place = selectedElement.parentNode.querySelector('.learningmap-place');\n offset = getMousePosition(evt);\n offset.x -= parseInt(selectedElement.getAttributeNS(null, \"dx\")) + place.cx.baseVal.value;\n offset.y -= parseInt(selectedElement.getAttributeNS(null, \"dy\")) + place.cy.baseVal.value;\n } else if (evt.target.nodeName == 'path') {\n selectedElement = evt.target;\n offset = getMousePosition(evt);\n let pathPoint = transformCoordinates(evt.layerX, evt.layerY);\n offset.x += pathPoint.x;\n offset.y += pathPoint.y;\n }\n }\n\n /**\n * Function called during dragging. Continuously updates circles center coordinates and the\n * coordinates of the touching paths.\n * @param {*} evt\n */\n function drag(evt) {\n if (evt.cancelable) {\n evt.preventDefault();\n }\n // Count touchmove events\n touchmove++;\n if (selectedElement) {\n var coord = getMousePosition(evt);\n let cx = coord.x - offset.x;\n let cy = coord.y - offset.y;\n if (selectedElement.nodeName == 'text') {\n let place = selectedElement.parentNode.querySelector('.learningmap-place');\n // Calculate the delta from the current mouse position to the corresponding place.\n // coord: current mouse position\n // offset: delta from the mouse position to the coordinates of the text node\n let dx = coord.x - offset.x - place.cx.baseVal.value;\n let dy = coord.y - offset.y - place.cy.baseVal.value;\n selectedElement.setAttributeNS(null, \"dx\", dx);\n selectedElement.setAttributeNS(null, \"dy\", dy);\n }\n if (selectedElement.nodeName == 'path') {\n selectedElement.setAttribute(\n 'd',\n updatePathDeclaration(selectedElement.getAttribute('d'), coord.x, coord.y, targetPoints.bezierPoint)\n );\n }\n if (selectedElement.nodeName == 'circle') {\n selectedElement.setAttributeNS(null, \"cx\", cx);\n selectedElement.setAttributeNS(null, \"cy\", cy);\n let textNode = document.getElementById('text' + selectedElement.id);\n if (textNode !== null) {\n textNode.setAttributeNS(null, 'x', cx);\n textNode.setAttributeNS(null, 'y', cy);\n }\n pathsToUpdateFirstPoint.forEach(function(path) {\n let pathNode = document.getElementById(path.id);\n if (pathNode !== null) {\n if (pathNode.nodeName == 'path') {\n pathNode.setAttribute(\n 'd',\n updatePathDeclaration(pathNode.getAttribute('d'), cx, cy, targetPoints.firstPoint)\n );\n } else {\n pathNode.setAttribute('x1', cx);\n pathNode.setAttribute('y1', cy);\n }\n }\n });\n\n pathsToUpdateSecondPoint.forEach(function(path) {\n let pathNode = document.getElementById(path.id);\n if (pathNode !== null) {\n if (pathNode.nodeName == 'path') {\n pathNode.setAttribute(\n 'd',\n updatePathDeclaration(pathNode.getAttribute('d'), cx, cy, targetPoints.secondPoint)\n );\n } else {\n pathNode.setAttribute('x2', cx);\n pathNode.setAttribute('y2', cy);\n }\n }\n });\n }\n }\n }\n\n /**\n * Function called when dragging ends.\n * @param {*} evt\n */\n function endDrag(evt) {\n if (evt.cancelable) {\n evt.preventDefault();\n }\n selectedElement = null;\n unselectAll();\n updateCode();\n }\n\n /**\n * Function called when touchstart event occurs.\n * @param {*} evt\n */\n function startTouch(evt) {\n if (evt.cancelable) {\n evt.preventDefault();\n }\n if (\n evt.target.classList.contains('learningmap-draggable') ||\n evt.target.nodeName == 'text' ||\n evt.target.nodeName == 'path'\n ) {\n if (!touchstart) {\n touchstart = true;\n touchmove = 0;\n touchend = false;\n setTimeout(\n (evt) => {\n if (touchmove < 3 && !touchend) {\n if (evt.touches) {\n evt = evt.touches[0];\n }\n showContextMenu(evt);\n }\n },\n 2000,\n evt\n );\n setTimeout(\n () => {\n touchstart = false;\n },\n 300);\n } else {\n dblclickHandler(evt);\n touchstart = false;\n }\n startDrag(evt);\n } else {\n if (!touchstart) {\n touchstart = true;\n touchend = false;\n touchmove = 0;\n setTimeout(\n () => {\n touchstart = false;\n },\n 300);\n } else {\n dblclickHandler(evt);\n touchstart = false;\n }\n }\n }\n\n /**\n * Function called when touchend, touchleave or touchcancel event occurs.\n * @param {*} evt\n */\n function endTouch(evt) {\n selectedElement = null;\n touchend = true;\n // If there was only a small move (<3 move events), this also counts as a click.\n if (touchmove < 3 && touchstart) {\n clickHandler(evt);\n } else {\n endDrag(evt);\n }\n if (evt.cancelable) {\n evt.preventDefault();\n }\n }\n\n /**\n * Updates the path declaration of lines and quadratic bezier curves setting one of the points.\n * @param {string} oldDefinition SVG path definition string\n * @param {number} targetX x coordinate of the point to set\n * @param {number} targetY y coordinate of the point to set\n * @param {number} targetP Which point to change (you can use the targetPoints constants here)\n * @returns {string} Updated SVG path definition\n */\n function updatePathDeclaration(oldDefinition, targetX, targetY, targetP = targetPoints.firstPoint) {\n let parts = oldDefinition.split(' ');\n let fromX = 0;\n let fromY = 0;\n let toX = 0;\n let toY = 0;\n let bezierX = 0;\n let bezierY = 0;\n let pathType = pathTypes.line;\n\n // The d attribute of an SVG path in a learning map can have two different formats (in this version):\n // \"M x1 y1 L x2 y2\" Line from x1, y1 to x2, y2\n // \"M x1 y2 Q x3 y3 x2 y2\" Quadratic bezier curve inside the triangle defined by x1, y1, x2, y2 and x3, y3.\n for (let i = 0; i < parts.length; i++) {\n // Every path contains the first point in that way.\n if (parts[i] == 'M') {\n fromX = parseInt(parts[i + 1]);\n fromY = parseInt(parts[i + 2]);\n i += 2;\n }\n // This path is a direct line, so there are only two points in total.\n if (parts[i] == 'L') {\n toX = parseInt(parts[i + 1]);\n toY = parseInt(parts[i + 2]);\n i += 2;\n }\n // This path is a bezier curve, there are three points in total.\n if (parts[i] == 'Q') {\n bezierX = parseInt(parts[i + 1]);\n bezierY = parseInt(parts[i + 2]);\n toX = parseInt(parts[i + 3]);\n toY = parseInt(parts[i + 4]);\n i += 4;\n pathType = pathTypes.quadraticbezier;\n }\n }\n\n switch (targetP) {\n case targetPoints.firstPoint:\n fromX = targetX;\n fromY = targetY;\n break;\n case targetPoints.secondPoint:\n toX = targetX;\n toY = targetY;\n break;\n case targetPoints.bezierPoint:\n // Calculate the third triangle point for the bezier curve.\n bezierX = targetX * 2 - (fromX + toX) * 0.5;\n bezierY = targetY * 2 - (fromY + toY) * 0.5;\n pathType = pathTypes.quadraticbezier;\n break;\n }\n\n if (pathType == pathTypes.quadraticbezier) {\n return 'M ' + fromX + ' ' + fromY + ' Q ' + bezierX + ' ' + bezierY + ', ' + toX + ' ' + toY;\n } else {\n return 'M ' + fromX + ' ' + fromY + ' L ' + toX + ' ' + toY;\n }\n }\n }\n\n /**\n * Updates the form fields for the SVG code and the placestore from the editor.\n */\n function updateCode() {\n if (code && mapdiv) {\n code.innerHTML = mapdiv.innerHTML;\n }\n if (placestoreInput) {\n document.getElementsByName('placestore')[0].value = JSON.stringify(placestore.getPlacestore());\n }\n }\n\n /**\n * Handles double clicks on the map\n * @param {*} event\n */\n function dblclickHandler(event) {\n hideContextMenu();\n hideAdvancedSettings();\n unselectAll();\n if (event.target.classList.contains('learningmap-mapcontainer') ||\n event.target.classList.contains('learningmap-background-image')) {\n addPlace(event);\n } else if (event.target.classList.contains('learningmap-place')) {\n if (lastTarget == event.target.id) {\n lastTarget = null;\n clickHandler(event);\n } else {\n removePlace(event);\n }\n } else if (event.target.classList.contains('learningmap-path')) {\n removePath(event.target.id);\n }\n updateCode();\n }\n\n /**\n * Returns an empty title tag with the given id.\n * @param {*} id id for the title\n * @returns {any}\n */\n function title(id) {\n let title = document.createElementNS('http://www.w3.org/2000/svg', 'title');\n title.setAttribute('id', id);\n return title;\n }\n\n /**\n * Returns an text tag with the given id.\n * @param {*} id id for the text\n * @param {*} content content of the tag\n * @param {*} x x coordinate of the text\n * @param {*} y y coordinate of the text\n * @returns {any}\n */\n function text(id, content, x, y) {\n let text = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n text.setAttribute('id', id);\n text.setAttribute('x', x);\n text.setAttribute('y', y);\n // Default value for delta: Circle radius * 1.5 (as a padding)\n text.setAttribute('dx', circleRadius * 1.5);\n text.setAttribute('dy', circleRadius * 1.5);\n text.textContent = content;\n return text;\n }\n\n /**\n * Returns a circle tag with the given dimensions.\n * @param {*} x x coordinate of the center\n * @param {*} y y coordinate of the center\n * @param {*} r radius\n * @param {*} classes classes to add\n * @param {*} id id of the circle\n * @returns {any}\n */\n function circle(x, y, r, classes, id) {\n let circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n circle.setAttribute('class', classes);\n circle.setAttribute('id', id);\n circle.setAttribute('cx', x);\n circle.setAttribute('cy', y);\n circle.setAttribute('r', r);\n return circle;\n }\n\n /**\n * Returns a path between two points.\n * @param {*} x1 x coordinate of the first point\n * @param {*} y1 y coordinate of the first point\n * @param {*} x2 x coordinate of the second point\n * @param {*} y2 y coordinate of the second point\n * @param {*} classes CSS classes to set\n * @param {*} id id of the path\n * @returns {any}\n */\n function path(x1, y1, x2, y2, classes, id) {\n let path = document.createElementNS('http://www.w3.org/2000/svg', 'path');\n path.setAttribute('class', classes);\n path.setAttribute('id', id);\n path.setAttribute('d', 'M ' + x1 + ' ' + y1 + ' L ' + x2 + ' ' + y2);\n return path;\n }\n\n /**\n * Returns a link around a given child element. This function also adds a title element next\n * to the child for accessibility.\n * @param {*} child child item to set the link on\n * @param {*} id id of the link\n * @param {*} title title of the link\n * @param {*} text text to describe the link\n * @returns {any}\n */\n function link(child, id, title = null, text = null) {\n let link = document.createElementNS('http://www.w3.org/2000/svg', 'a');\n link.setAttribute('id', id);\n link.setAttribute('xlink:href', '');\n link.appendChild(child);\n if (title !== null) {\n link.appendChild(title);\n }\n if (text !== null) {\n link.appendChild(text);\n }\n return link;\n }\n\n /**\n * Adds a place on the SVG map. This function also prepares the code for linking activities\n * and adding titles (for accessibility).\n * @param {*} event event causing the command\n */\n function addPlace(event) {\n let placesgroup = document.getElementById('placesGroup');\n let placeId = 'p' + placestore.getId();\n let linkId = 'a' + placestore.getId();\n var CTM = event.target.getScreenCTM();\n if (event.touches) {\n event = event.touches[0];\n }\n let cx = (event.clientX - CTM.e) / CTM.a;\n let cy = (event.clientY - CTM.f) / CTM.d;\n placesgroup.appendChild(\n link(\n circle(cx, cy, circleRadius, 'learningmap-place learningmap-draggable learningmap-emptyplace', placeId),\n linkId,\n title('title' + placeId),\n text('text' + placeId, '', cx, cy)\n )\n );\n placestore.addPlace(placeId, linkId);\n }\n\n /**\n * Handles single clicks on the background image.\n * @param {*} event click event\n * @returns {void}\n */\n function clickHandler(event) {\n event.preventDefault();\n hideContextMenu();\n hideAdvancedSettings();\n if (event.target.classList.contains('learningmap-place') && selectedElement === null) {\n if (firstPlace === null) {\n firstPlace = event.target.id;\n document.getElementById(firstPlace).classList.add('learningmap-selected');\n } else {\n secondPlace = event.target.id;\n let fid = parseInt(firstPlace.replace('p', ''));\n let sid = parseInt(secondPlace.replace('p', ''));\n if (sid == fid) {\n return;\n }\n if (sid < fid) {\n let z = sid;\n sid = fid;\n fid = z;\n }\n addPath(fid, sid);\n let first = document.getElementById(firstPlace);\n if (first) {\n first.classList.remove('learningmap-selected');\n }\n firstPlace = null;\n lastTarget = secondPlace;\n secondPlace = null;\n }\n } else {\n unselectAll();\n firstPlace = null;\n }\n }\n /**\n * Removes the classes 'learningmap-selected' and 'learningmap-selectet-activity-selector'\n * from all nodes\n */\n function unselectAll() {\n Array.from(document.getElementsByClassName('learningmap-selected')).forEach(function(e) {\n e.classList.remove('learningmap-selected');\n });\n Array.from(document.getElementsByClassName('learningmap-selected-activity-selector')).forEach(function(e) {\n e.classList.remove('learningmap-selected-activity-selector');\n });\n }\n\n /**\n * Adds a path between two places.\n * @param {number} fid id of the first place (meant to be the smaller one)\n * @param {number} sid id of the second place (meant to be the bigger one)\n */\n function addPath(fid, sid) {\n let pid = 'p' + fid + '_' + sid;\n if (document.getElementById(pid) === null) {\n let pathsgroup = document.getElementById('pathsGroup');\n let first = document.getElementById('p' + fid);\n let second = document.getElementById('p' + sid);\n if (pathsgroup && first && second) {\n pathsgroup.appendChild(\n path(\n first.cx.baseVal.value,\n first.cy.baseVal.value,\n second.cx.baseVal.value,\n second.cy.baseVal.value,\n 'learningmap-path',\n pid\n )\n );\n placestore.addPath(pid, 'p' + fid, 'p' + sid);\n }\n }\n }\n\n /**\n * Removes a place from the SVG and the placestore. This function also removes all\n * touching paths and entries in statringplaces / targetplaces linking to the removed\n * place.\n * @param {any} event event causing the remove order\n */\n function removePlace(event) {\n let place = document.getElementById(event.target.id);\n let parent = place.parentNode;\n removePathsTouchingPlace(event.target.id);\n placestore.removePlace(event.target.id);\n parent.removeChild(place);\n parent.parentNode.removeChild(parent);\n\n updateCode();\n }\n\n /**\n * Removes all paths touching a certain place\n * @param {number} id id of the place\n */\n function removePathsTouchingPlace(id) {\n placestore.getTouchingPaths(id).forEach(\n function(e) {\n removePath(e.id);\n }\n );\n }\n\n /**\n * Removes a path from the SVG and from the placestore\n * @param {number} id id of the path\n */\n function removePath(id) {\n let path = document.getElementById(id);\n if (path !== null) {\n path.parentNode.removeChild(path);\n placestore.removePath(id);\n }\n }\n\n /**\n * Sets the background image of the SVG to the current image in filemanager.\n */\n function refreshBackgroundImage() {\n let previewimage = document.getElementsByClassName('realpreview');\n if (previewimage.length > 0) {\n let background = document.getElementById('learningmap-background-image');\n let backgroundurl = previewimage[0].getAttribute('src').split('?')[0];\n // If the uploaded file reuses the filename of a previously uploaded image, they differ\n // only in the oid. So one has to append the oid to the url.\n if (previewimage[0].getAttribute('src').split('?')[1].includes('&oid=')) {\n backgroundurl += '?oid=' + previewimage[0].getAttribute('src').split('&oid=')[1];\n }\n background.setAttribute('xlink:href', backgroundurl);\n }\n }\n\n /**\n * Adds an eventListener to the background image for watching file changes and updating\n * height and width of the image.\n */\n function registerBackgroundListener() {\n let background = document.getElementById('learningmap-background-image');\n if (background) {\n background.addEventListener('load', function() {\n background.removeAttribute('height');\n let height = parseInt(background.getBBox().height);\n let width = background.getBBox().width;\n placestore.setBackgroundDimensions(width, height);\n svg.setAttribute('viewBox', '0 0 ' + placestore.width + ' ' + placestore.height);\n background.setAttribute('width', width);\n background.setAttribute('height', height);\n updateCode();\n });\n }\n }\n\n /**\n * Updates CSS code inside the SVG (called, when one of the colors is changed).\n * Calls updateCode() when completed.\n */\n function updateCSS() {\n Templates.renderForPromise('mod_learningmap/cssskeleton', placestore.getPlacestore())\n .then(({html, js}) => {\n Templates.replaceNode('#learningmap-svgstyle', html, js);\n updateCode();\n return true;\n })\n .catch(ex => displayException(ex));\n }\n\n /**\n * Updates the activity selector to highlight the activities already used\n * and to show the alert for hidden activities.\n */\n function updateActivities() {\n let activities = placestore.getAllActivities();\n let options = Array.from(activitySelector.getElementsByTagName('option'));\n activityHiddenWarning.setAttribute('hidden', '');\n options.forEach(function(n) {\n if (activities.includes(n.value)) {\n n.classList.add('learningmap-used-activity');\n if (n.selected) {\n if (n.getAttribute('data-activity-hidden') == true) {\n activityHiddenWarning.removeAttribute('hidden');\n }\n }\n } else {\n n.classList.remove('learningmap-used-activity');\n }\n });\n }\n\n /**\n * Adds the event listener to the color chooser buttons.\n * @param {*} name name of the color\n * @param {*} secondValue name of a second placestore value that has to be changed along\n */\n function colorChooserLogic(name, secondValue = '') {\n let colorChooser = document.getElementById('learningmap-color-' + name);\n if (colorChooser) {\n colorChooser.addEventListener('change', function() {\n placestore.setColor(name, colorChooser.value);\n if (secondValue != '') {\n placestore.setColor(secondValue, colorChooser.value);\n }\n updateCSS();\n });\n colorChooser.value = placestore.getColor(name);\n }\n }\n\n /**\n * Adds the event listener to advanced settings menu items\n * @param {*} name Name of the item\n * @param {*} getCall Method of placestore to call to read value\n * @param {*} setCall Method of placestore to call to save value\n * @param {*} callback Additional callback after value is saved\n */\n function advancedSettingsLogic(name, getCall, setCall, callback = null) {\n let settingItem = document.getElementById('learningmap-advanced-setting-' + name);\n if (settingItem) {\n settingItem.checked = getCall.call(placestore);\n settingItem.addEventListener('change', function() {\n setCall.call(placestore, settingItem.checked);\n if (callback !== null) {\n callback();\n }\n updateCSS();\n });\n }\n }\n\n /**\n * Adds missing text nodes\n */\n function fixPlaceLabels() {\n let options = Array.from(activitySelector.getElementsByTagName('option'));\n let places = placestore.getPlaces();\n for (const place of places) {\n if (document.getElementById('text' + place.id) === null) {\n let content = '';\n for (const option of options) {\n if (option.value == place.linkedActivity) {\n content = option.textContent;\n break;\n }\n }\n let placeNode = document.getElementById(place.id);\n let textNode = text('text' + place.id, content, placeNode.cx.baseVal.value, placeNode.cy.baseVal.value);\n placeNode.parentNode.appendChild(textNode);\n }\n }\n }\n\n /**\n * Hides the advanced settings menu.\n */\n function hideAdvancedSettings() {\n let advancedSettings = document.getElementById('learningmap-advanced-settings');\n advancedSettings.setAttribute('hidden', '');\n }\n};\n"],"names":["targetPoints","pathTypes","offset","dragel","pathsToUpdateFirstPoint","pathsToUpdateSecondPoint","prefetchTemplates","selectedElement","firstPlace","secondPlace","lastTarget","elementForActivitySelector","touchstart","touchend","touchmove","mapdiv","document","getElementById","code","activitySetting","activitySelector","activityStarting","activityTarget","activityHiddenWarning","advancedSettingsIcon","treeView","querySelector","setAttribute","iconView","setTimeout","dispatchEvent","Event","addEventListener","setActivityId","value","text","textContent","title","classList","remove","add","updateActivities","updateCode","checked","addStartingPlace","removeStartingPlace","addTargetPlace","removeTargetPlace","placestoreInput","getElementsByName","loadJSON","advancedSettings","getAttribute","hideAdvancedSettings","removeAttribute","hideContextMenu","advancedSettingsClose","advancedSettingsLogic","placestore","getHidePaths","setHidePaths","getUseCheckmark","setUseCheckmark","getHover","setHover","getPulse","setPulse","getShowall","setShowall","getHideStroke","setHideStroke","getShowText","setShowText","options","Array","from","getElementsByTagName","places","getPlaces","place","id","content","option","linkedActivity","placeNode","textNode","cx","baseVal","cy","parentNode","appendChild","getSliceMode","setSliceMode","getShowWayGone","setShowWayGone","colorChooserLogic","innerHTML","refreshBackgroundImage","background","height","parseInt","getBBox","width","setBackgroundDimensions","svg","registerBackgroundListener","getMapid","showContextMenu","e","unselectAll","target","touches","contains","activityId","getActivityId","scalingFactor","clientWidth","style","setProperty","clientHeight","display","isStartingPlace","isTargetPlace","el","startDrag","drag","endDrag","evt","cancelable","preventDefault","nodeName","dblclickHandler","endTouch","getMousePosition","x","getAttributeNS","y","getPathsWithFid","getPathsWithSid","pathPoint","transformCoordinates","layerX","layerY","coord","dx","dy","setAttributeNS","updatePathDeclaration","forEach","path","pathNode","clickHandler","oldDefinition","targetX","targetY","targetP","parts","split","fromX","fromY","toX","toY","bezierX","bezierY","pathType","i","length","makeDraggable","updateCSS","backgroundfileNode","MutationObserver","observe","attributes","childList","subtree","clientX","clientY","CTM","getScreenCTM","a","f","d","JSON","stringify","getPlacestore","event","placesgroup","placeId","getId","linkId","child","link","createElementNS","r","classes","circle","addPlace","parent","getTouchingPaths","removePath","removePlace","removeChild","circleRadius","fid","replace","sid","z","pid","pathsgroup","first","second","x1","y1","x2","y2","addPath","getElementsByClassName","previewimage","backgroundurl","includes","renderForPromise","then","_ref","html","js","replaceNode","catch","ex","activities","getAllActivities","n","selected","name","secondValue","colorChooser","setColor","getColor","getCall","setCall","callback","settingItem","call"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/placestore.min.js b/amd/build/placestore.min.js index 0fa23fb..1da359a 100644 --- a/amd/build/placestore.min.js +++ b/amd/build/placestore.min.js @@ -1,3 +1,3 @@ -define("mod_learningmap/placestore",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;let placestore={version:2024022102,id:0,places:[],paths:[],startingplaces:[],targetplaces:[],placecolor:"#c01c28",strokecolor:"#ffffff",strokeopacity:1,textcolor:"#ffffff",visitedcolor:"#26a269",height:100,width:800,hidepaths:!1,mapid:"",usecheckmark:!1,editmode:!0,pulse:!1,hover:!1,showall:!1,showtext:!1,slicemode:!1,showwaygone:!1,loadJSON:function(json){try{let fromjson=JSON.parse(json);null===fromjson.textcolor&&(fromjson.textcolor=fromjson.strokecolor),Object.assign(this,fromjson)}catch{}this.version=2024022102},buildJSON:function(){return JSON.stringify(this.getPlacestore())},addPlace:function(id,linkId){let linkedActivity=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;this.places.push({id:id,linkId:linkId,linkedActivity:linkedActivity,placecolor:null,visitedcolor:null}),1==this.places.length&&this.addStartingPlace(id),this.id++},removePlace:function(id){this.removeStartingPlace(id),this.removeTargetPlace(id),this.places=this.places.filter((function(p){return p.id!=id}))},addStartingPlace:function(id){this.startingplaces.push(id)},removeStartingPlace:function(id){this.startingplaces=this.startingplaces.filter((function(e){return e!=id}))},isStartingPlace:function(id){return this.startingplaces.includes(id)},addTargetPlace:function(id){this.targetplaces.push(id)},removeTargetPlace:function(id){this.targetplaces=this.targetplaces.filter((function(e){return e!=id}))},isTargetPlace:function(id){return this.targetplaces.includes(id)},addPath:function(pid,fid,sid){this.paths.push({id:pid,fid:fid,sid:sid,strokecolor:null,strokedasharray:null,hidepath:null})},removePath:function(id){this.paths=this.paths.filter((function(p){return p.id!=id}))},getTouchingPaths:function(id){return this.paths.filter((function(p){return p.fid==id||p.sid==id}))},getActivityId:function(id){let place=this.places.filter((function(e){return id==e.id}));return place.length>0?place[0].linkedActivity:null},setActivityId:function(id,linkedActivity){let place=this.places.filter((function(e){return id==e.id}));place.length>0&&(place[0].linkedActivity=linkedActivity)},setColor:function(type,color){switch(type){case"stroke":this.strokecolor=color;break;case"place":this.placecolor=color;break;case"visited":this.visitedcolor=color;break;case"text":this.textcolor=color}},getColor:function(type){switch(type){case"stroke":return this.strokecolor;case"place":return this.placecolor;case"visited":return this.visitedcolor;case"text":return this.textcolor}return null},getId:function(){return this.id},setBackgroundDimensions:function(width,height){this.width=width,this.height=height},getPathsWithFid:function(id){return this.paths.filter((function(p){return p.fid==id}))},getPathsWithSid:function(id){return this.paths.filter((function(p){return p.sid==id}))},getPlacestore:function(){return{id:this.id,places:this.places,paths:this.paths,startingplaces:this.startingplaces,targetplaces:this.targetplaces,placecolor:this.placecolor,strokecolor:this.strokecolor,strokeopacity:this.strokeopacity,textcolor:this.textcolor,visitedcolor:this.visitedcolor,height:this.height,width:this.width,hidepaths:this.hidepaths,mapid:this.mapid,usecheckmark:this.usecheckmark,editmode:this.editmode,version:this.version,pulse:this.pulse,hover:this.hover,showall:this.showall,showtext:this.showtext,slicemode:this.slicemode,showwaygone:this.showwaygone}},setHidePaths:function(value){this.hidepaths=value},getHidePaths:function(){return this.hidepaths},setPulse:function(value){this.pulse=value},getPulse:function(){return this.pulse},setHover:function(value){this.hover=value},getHover:function(){return this.hover},setShowall:function(value){this.showall=value},getShowall:function(){return this.showall},getMapid:function(){return this.mapid},getUseCheckmark:function(){return this.usecheckmark},setUseCheckmark:function(value){this.usecheckmark=value},getAllActivities:function(){let activities=[];return this.places.forEach((function(p){p.linkedActivity&&activities.push(p.linkedActivity)})),activities},setStrokeOpacity:function(value){this.strokeopacity=value},getStrokeOpacity:function(){return this.strokeopacity},setHideStroke:function(value){this.strokeopacity=value?0:1},getHideStroke:function(){return this.strokeopacity<1},getShowText:function(){return this.showtext},setShowText:function(value){this.showtext=value},getPlaces:function(){return this.places},getSliceMode:function(){return this.slicemode},setSliceMode:function(value){this.slicemode=value},getShowWayGone:function(){return this.showwaygone},setShowWayGone:function(value){this.showwaygone=value}};var _default=placestore;return _exports.default=_default,_exports.default})); +define("mod_learningmap/placestore",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;let placestore={version:2024072201,id:0,places:[],paths:[],startingplaces:[],targetplaces:[],placecolor:"#c01c28",strokecolor:"#ffffff",strokeopacity:1,textcolor:"#ffffff",visitedcolor:"#26a269",height:100,width:800,hidepaths:!1,mapid:"",usecheckmark:!1,editmode:!0,pulse:!1,hover:!1,showall:!1,showtext:!1,slicemode:!1,showwaygone:!1,loadJSON:function(json){try{let fromjson=JSON.parse(json);null===fromjson.textcolor&&(fromjson.textcolor=fromjson.strokecolor),Object.assign(this,fromjson)}catch{}this.version=2024072201},buildJSON:function(){return JSON.stringify(this.getPlacestore())},addPlace:function(id,linkId){let linkedActivity=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;this.places.push({id:id,linkId:linkId,linkedActivity:linkedActivity,placecolor:null,visitedcolor:null}),1==this.places.length&&this.addStartingPlace(id),this.id++},removePlace:function(id){this.removeStartingPlace(id),this.removeTargetPlace(id),this.places=this.places.filter((function(p){return p.id!=id}))},addStartingPlace:function(id){this.startingplaces.push(id)},removeStartingPlace:function(id){this.startingplaces=this.startingplaces.filter((function(e){return e!=id}))},isStartingPlace:function(id){return this.startingplaces.includes(id)},addTargetPlace:function(id){this.targetplaces.push(id)},removeTargetPlace:function(id){this.targetplaces=this.targetplaces.filter((function(e){return e!=id}))},isTargetPlace:function(id){return this.targetplaces.includes(id)},addPath:function(pid,fid,sid){this.paths.push({id:pid,fid:fid,sid:sid,strokecolor:null,strokedasharray:null,hidepath:null})},removePath:function(id){this.paths=this.paths.filter((function(p){return p.id!=id}))},getTouchingPaths:function(id){return this.paths.filter((function(p){return p.fid==id||p.sid==id}))},getActivityId:function(id){let place=this.places.filter((function(e){return id==e.id}));return place.length>0?place[0].linkedActivity:null},setActivityId:function(id,linkedActivity){let place=this.places.filter((function(e){return id==e.id}));place.length>0&&(place[0].linkedActivity=linkedActivity)},setColor:function(type,color){switch(type){case"stroke":this.strokecolor=color;break;case"place":this.placecolor=color;break;case"visited":this.visitedcolor=color;break;case"text":this.textcolor=color}},getColor:function(type){switch(type){case"stroke":return this.strokecolor;case"place":return this.placecolor;case"visited":return this.visitedcolor;case"text":return this.textcolor}return null},getId:function(){return this.id},setBackgroundDimensions:function(width,height){this.width=width,this.height=height},getPathsWithFid:function(id){return this.paths.filter((function(p){return p.fid==id}))},getPathsWithSid:function(id){return this.paths.filter((function(p){return p.sid==id}))},getPlacestore:function(){return{id:this.id,places:this.places,paths:this.paths,startingplaces:this.startingplaces,targetplaces:this.targetplaces,placecolor:this.placecolor,strokecolor:this.strokecolor,strokeopacity:this.strokeopacity,textcolor:this.textcolor,visitedcolor:this.visitedcolor,height:this.height,width:this.width,hidepaths:this.hidepaths,mapid:this.mapid,usecheckmark:this.usecheckmark,editmode:this.editmode,version:this.version,pulse:this.pulse,hover:this.hover,showall:this.showall,showtext:this.showtext,slicemode:this.slicemode,showwaygone:this.showwaygone}},setHidePaths:function(value){this.hidepaths=value},getHidePaths:function(){return this.hidepaths},setPulse:function(value){this.pulse=value},getPulse:function(){return this.pulse},setHover:function(value){this.hover=value},getHover:function(){return this.hover},setShowall:function(value){this.showall=value},getShowall:function(){return this.showall},getMapid:function(){return this.mapid},getUseCheckmark:function(){return this.usecheckmark},setUseCheckmark:function(value){this.usecheckmark=value},getAllActivities:function(){let activities=[];return this.places.forEach((function(p){p.linkedActivity&&activities.push(p.linkedActivity)})),activities},setStrokeOpacity:function(value){this.strokeopacity=value},getStrokeOpacity:function(){return this.strokeopacity},setHideStroke:function(value){this.strokeopacity=value?0:1},getHideStroke:function(){return this.strokeopacity<1},getShowText:function(){return this.showtext},setShowText:function(value){this.showtext=value},getPlaces:function(){return this.places},getSliceMode:function(){return this.slicemode},setSliceMode:function(value){this.slicemode=value},getShowWayGone:function(){return this.showwaygone},setShowWayGone:function(value){this.showwaygone=value}};var _default=placestore;return _exports.default=_default,_exports.default})); //# sourceMappingURL=placestore.min.js.map \ No newline at end of file diff --git a/amd/build/placestore.min.js.map b/amd/build/placestore.min.js.map index 51fc899..5ddc577 100644 --- a/amd/build/placestore.min.js.map +++ b/amd/build/placestore.min.js.map @@ -1 +1 @@ -{"version":3,"file":"placestore.min.js","sources":["../src/placestore.js"],"sourcesContent":["let placestore = {\n version: 2024022102,\n id: 0,\n places: [],\n paths: [],\n startingplaces: [],\n targetplaces: [],\n placecolor: '#c01c28',\n strokecolor: '#ffffff',\n strokeopacity: 1,\n textcolor: '#ffffff',\n visitedcolor: '#26a269',\n height: 100,\n width: 800,\n hidepaths: false,\n mapid: '',\n usecheckmark: false,\n editmode: true,\n pulse: false,\n hover: false,\n showall: false,\n showtext: false,\n slicemode: false,\n showwaygone: false,\n /**\n * Loads attributes from JSON into placestore\n * @param {*} json\n */\n loadJSON: function(json) {\n try {\n let fromjson = JSON.parse(json);\n if (fromjson.textcolor === null) {\n fromjson.textcolor = fromjson.strokecolor;\n }\n Object.assign(this, fromjson);\n // eslint-disable-next-line no-empty\n } catch { }\n // Update version (only relevant if learning map is saved)\n this.version = 2024022102;\n },\n /**\n * Returns placestore as a JSON string ()\n * @returns {string}\n */\n buildJSON: function() {\n return JSON.stringify(this.getPlacestore());\n },\n /**\n * Adds a place. If it is the only place, it is set as starting place\n * @param {*} id id of the place\n * @param {*} linkId id of the corresponding link\n * @param {*} linkedActivity course module id of linked activity\n */\n addPlace: function(id, linkId, linkedActivity = null) {\n this.places.push({\n id: id,\n linkId: linkId,\n linkedActivity: linkedActivity,\n placecolor: null,\n visitedcolor: null\n });\n if (this.places.length == 1) {\n this.addStartingPlace(id);\n }\n this.id++;\n },\n /**\n * Removes a place\n * @param {*} id id of the place\n */\n removePlace: function(id) {\n this.removeStartingPlace(id);\n this.removeTargetPlace(id);\n this.places = this.places.filter(\n function(p) {\n return p.id != id;\n }\n );\n },\n /**\n * Adds a place to the array of starting places\n * @param {*} id id of the place\n */\n addStartingPlace: function(id) {\n this.startingplaces.push(id);\n },\n /**\n * Removes a place from the array of starting places\n * @param {*} id id of the place\n */\n removeStartingPlace: function(id) {\n this.startingplaces = this.startingplaces.filter(\n function(e) {\n return e != id;\n }\n );\n },\n /**\n * Returns whether a place is in the array of starting places\n * @param {*} id id of the place\n * @returns {boolean}\n */\n isStartingPlace: function(id) {\n return this.startingplaces.includes(id);\n },\n /**\n * Adds a place to the array of target places\n * @param {*} id id of the place\n */\n addTargetPlace: function(id) {\n this.targetplaces.push(id);\n },\n /**\n * Removes a place from the array of target places\n * @param {*} id id of the place\n */\n removeTargetPlace: function(id) {\n this.targetplaces = this.targetplaces.filter(\n function(e) {\n return e != id;\n }\n );\n },\n /**\n * Returns whether a place is in the array of target places\n * @param {number} id id of the place\n * @returns {boolean}\n */\n isTargetPlace: function(id) {\n return this.targetplaces.includes(id);\n },\n /**\n * Adds a path between two places\n * @param {*} pid id of the path\n * @param {*} fid id of the first place\n * @param {*} sid id of the second place\n */\n addPath: function(pid, fid, sid) {\n this.paths.push({\n id: pid,\n fid: fid,\n sid: sid,\n strokecolor: null,\n strokedasharray: null,\n hidepath: null\n });\n },\n /**\n * Removes a path\n * @param {*} id id of the place\n */\n removePath: function(id) {\n this.paths = this.paths.filter(\n function(p) {\n return p.id != id;\n }\n );\n },\n /**\n * Returns an array of paths touching a place\n * @param {*} id id of the place\n * @returns {array}\n */\n getTouchingPaths: function(id) {\n return this.paths.filter(\n function(p) {\n return p.fid == id || p.sid == id;\n }\n );\n },\n /**\n * Returns the course module id linked to a place\n * @param {*} id id of the place\n * @returns {number} id of the linked course module\n */\n getActivityId: function(id) {\n let place = this.places.filter(\n function(e) {\n return id == e.id;\n }\n );\n if (place.length > 0) {\n return place[0].linkedActivity;\n } else {\n return null;\n }\n },\n /**\n * Sets the id of the linked course module\n * @param {*} id id of the place\n * @param {*} linkedActivity course module id\n */\n setActivityId: function(id, linkedActivity) {\n let place = this.places.filter(\n function(e) {\n return id == e.id;\n }\n );\n if (place.length > 0) {\n place[0].linkedActivity = linkedActivity;\n }\n },\n /**\n * Sets the color of 'stroke', 'place' or 'visited'\n * @param {*} type type of the color\n * @param {*} color color in hex format\n */\n setColor: function(type, color) {\n switch (type) {\n case 'stroke':\n this.strokecolor = color;\n break;\n case 'place':\n this.placecolor = color;\n break;\n case 'visited':\n this.visitedcolor = color;\n break;\n case 'text':\n this.textcolor = color;\n break;\n }\n },\n /**\n * Gets the color of 'stroke', 'place' or 'visited'\n * @param {*} type type of the color\n * @returns {string} color in hex format\n */\n getColor: function(type) {\n switch (type) {\n case 'stroke':\n return this.strokecolor;\n case 'place':\n return this.placecolor;\n case 'visited':\n return this.visitedcolor;\n case 'text':\n return this.textcolor;\n }\n return null;\n },\n /**\n * Returns the current id\n * @returns {number}\n */\n getId: function() {\n return this.id;\n },\n /**\n * Sets the dimensions of the background image\n * @param {*} width\n * @param {*} height\n */\n setBackgroundDimensions: function(width, height) {\n this.width = width;\n this.height = height;\n },\n /**\n * Returns all paths starting at a place\n * @param {*} id id of the place\n * @returns {array}\n */\n getPathsWithFid: function(id) {\n return this.paths.filter(function(p) {\n return p.fid == id;\n });\n },\n /**\n * Returns all paths ending at a place\n * @param {*} id id of the place\n * @returns {array}\n */\n getPathsWithSid: function(id) {\n return this.paths.filter(function(p) {\n return p.sid == id;\n });\n },\n /**\n * Returns the attributes of placestore\n * @returns {object}\n */\n getPlacestore: function() {\n return {\n id: this.id,\n places: this.places,\n paths: this.paths,\n startingplaces: this.startingplaces,\n targetplaces: this.targetplaces,\n placecolor: this.placecolor,\n strokecolor: this.strokecolor,\n strokeopacity: this.strokeopacity,\n textcolor: this.textcolor,\n visitedcolor: this.visitedcolor,\n height: this.height,\n width: this.width,\n hidepaths: this.hidepaths,\n mapid: this.mapid,\n usecheckmark: this.usecheckmark,\n editmode: this.editmode,\n version: this.version,\n pulse: this.pulse,\n hover: this.hover,\n showall: this.showall,\n showtext: this.showtext,\n slicemode: this.slicemode,\n showwaygone: this.showwaygone,\n };\n },\n /**\n * Sets hidepaths attribute\n * @param {boolean} value\n */\n setHidePaths: function(value) {\n this.hidepaths = value;\n },\n /**\n * Returns the value of hidepaths attribute\n * @returns {boolean}\n */\n getHidePaths: function() {\n return this.hidepaths;\n },\n /**\n * Sets pulse attribute\n * @param {boolean} value\n */\n setPulse: function(value) {\n this.pulse = value;\n },\n /**\n * Returns the value of pulse attribute\n * @returns {boolean}\n */\n getPulse: function() {\n return this.pulse;\n },\n /**\n * Sets hover attribute\n * @param {boolean} value\n */\n setHover: function(value) {\n this.hover = value;\n },\n /**\n * Returns the value of hover attribute\n * @returns {boolean}\n */\n getHover: function() {\n return this.hover;\n },\n /**\n * Sets showall attribute\n * @param {boolean} value\n */\n setShowall: function(value) {\n this.showall = value;\n },\n /**\n * Returns the value of showall attribute\n * @returns {boolean}\n */\n getShowall: function() {\n return this.showall;\n },\n /**\n * Returns the mapid\n * @returns {string}\n */\n getMapid: function() {\n return this.mapid;\n },\n /**\n * Returns the value of usecheckmark attribute\n * @returns {boolean}\n */\n getUseCheckmark: function() {\n return this.usecheckmark;\n },\n /**\n * Sets the value of usecheckmark attribute\n * @param {boolean} value\n */\n setUseCheckmark: function(value) {\n this.usecheckmark = value;\n },\n /**\n * Returns an array with all activity ids\n * @returns {array}\n */\n getAllActivities: function() {\n let activities = [];\n this.places.forEach(function(p) {\n if (p.linkedActivity) {\n activities.push(p.linkedActivity);\n }\n });\n return activities;\n },\n /**\n * Sets stroke opacity\n * @param {number} value\n */\n setStrokeOpacity: function(value) {\n this.strokeopacity = value;\n },\n /**\n * Returns the current stroke opacity\n * @returns {number}\n */\n getStrokeOpacity: function() {\n return this.strokeopacity;\n },\n /**\n * Sets stroke opacity to 0\n * @param {number} value\n */\n setHideStroke: function(value) {\n this.strokeopacity = (value ? 0 : 1);\n },\n /**\n * Returns the current stroke opacity\n * @returns {number}\n */\n getHideStroke: function() {\n return this.strokeopacity < 1;\n },\n /**\n * Returns the value of showtext attribute\n * @returns {boolean}\n */\n getShowText: function() {\n return this.showtext;\n },\n /**\n * Sets the value of showtext attribute\n * @param {boolean} value\n */\n setShowText: function(value) {\n this.showtext = value;\n },\n /**\n * Returns an array with all place identifiers\n * @returns {array}\n */\n getPlaces: function() {\n return this.places;\n },\n /**\n * Returns if slicemode is enabled\n * @returns {boolean}\n */\n getSliceMode: function() {\n return this.slicemode;\n },\n /**\n * Sets state of slicemode\n * @param {boolean} value\n */\n setSliceMode: function(value) {\n this.slicemode = value;\n },\n /**\n * Returns if showwaygone is enabled\n * @returns {boolean}\n */\n getShowWayGone: function() {\n return this.showwaygone;\n },\n /**\n * Sets state of showwaygone\n * @param {boolean} value\n */\n setShowWayGone: function(value) {\n this.showwaygone = value;\n },\n};\n\nexport default placestore;\n"],"names":["placestore","version","id","places","paths","startingplaces","targetplaces","placecolor","strokecolor","strokeopacity","textcolor","visitedcolor","height","width","hidepaths","mapid","usecheckmark","editmode","pulse","hover","showall","showtext","slicemode","showwaygone","loadJSON","json","fromjson","JSON","parse","Object","assign","this","buildJSON","stringify","getPlacestore","addPlace","linkId","linkedActivity","push","length","addStartingPlace","removePlace","removeStartingPlace","removeTargetPlace","filter","p","e","isStartingPlace","includes","addTargetPlace","isTargetPlace","addPath","pid","fid","sid","strokedasharray","hidepath","removePath","getTouchingPaths","getActivityId","place","setActivityId","setColor","type","color","getColor","getId","setBackgroundDimensions","getPathsWithFid","getPathsWithSid","setHidePaths","value","getHidePaths","setPulse","getPulse","setHover","getHover","setShowall","getShowall","getMapid","getUseCheckmark","setUseCheckmark","getAllActivities","activities","forEach","setStrokeOpacity","getStrokeOpacity","setHideStroke","getHideStroke","getShowText","setShowText","getPlaces","getSliceMode","setSliceMode","getShowWayGone","setShowWayGone"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"placestore.min.js","sources":["../src/placestore.js"],"sourcesContent":["let placestore = {\n version: 2024072201,\n id: 0,\n places: [],\n paths: [],\n startingplaces: [],\n targetplaces: [],\n placecolor: '#c01c28',\n strokecolor: '#ffffff',\n strokeopacity: 1,\n textcolor: '#ffffff',\n visitedcolor: '#26a269',\n height: 100,\n width: 800,\n hidepaths: false,\n mapid: '',\n usecheckmark: false,\n editmode: true,\n pulse: false,\n hover: false,\n showall: false,\n showtext: false,\n slicemode: false,\n showwaygone: false,\n /**\n * Loads attributes from JSON into placestore\n * @param {*} json\n */\n loadJSON: function(json) {\n try {\n let fromjson = JSON.parse(json);\n if (fromjson.textcolor === null) {\n fromjson.textcolor = fromjson.strokecolor;\n }\n Object.assign(this, fromjson);\n // eslint-disable-next-line no-empty\n } catch { }\n // Update version (only relevant if learning map is saved)\n this.version = 2024072201;\n },\n /**\n * Returns placestore as a JSON string ()\n * @returns {string}\n */\n buildJSON: function() {\n return JSON.stringify(this.getPlacestore());\n },\n /**\n * Adds a place. If it is the only place, it is set as starting place\n * @param {*} id id of the place\n * @param {*} linkId id of the corresponding link\n * @param {*} linkedActivity course module id of linked activity\n */\n addPlace: function(id, linkId, linkedActivity = null) {\n this.places.push({\n id: id,\n linkId: linkId,\n linkedActivity: linkedActivity,\n placecolor: null,\n visitedcolor: null\n });\n if (this.places.length == 1) {\n this.addStartingPlace(id);\n }\n this.id++;\n },\n /**\n * Removes a place\n * @param {*} id id of the place\n */\n removePlace: function(id) {\n this.removeStartingPlace(id);\n this.removeTargetPlace(id);\n this.places = this.places.filter(\n function(p) {\n return p.id != id;\n }\n );\n },\n /**\n * Adds a place to the array of starting places\n * @param {*} id id of the place\n */\n addStartingPlace: function(id) {\n this.startingplaces.push(id);\n },\n /**\n * Removes a place from the array of starting places\n * @param {*} id id of the place\n */\n removeStartingPlace: function(id) {\n this.startingplaces = this.startingplaces.filter(\n function(e) {\n return e != id;\n }\n );\n },\n /**\n * Returns whether a place is in the array of starting places\n * @param {*} id id of the place\n * @returns {boolean}\n */\n isStartingPlace: function(id) {\n return this.startingplaces.includes(id);\n },\n /**\n * Adds a place to the array of target places\n * @param {*} id id of the place\n */\n addTargetPlace: function(id) {\n this.targetplaces.push(id);\n },\n /**\n * Removes a place from the array of target places\n * @param {*} id id of the place\n */\n removeTargetPlace: function(id) {\n this.targetplaces = this.targetplaces.filter(\n function(e) {\n return e != id;\n }\n );\n },\n /**\n * Returns whether a place is in the array of target places\n * @param {number} id id of the place\n * @returns {boolean}\n */\n isTargetPlace: function(id) {\n return this.targetplaces.includes(id);\n },\n /**\n * Adds a path between two places\n * @param {*} pid id of the path\n * @param {*} fid id of the first place\n * @param {*} sid id of the second place\n */\n addPath: function(pid, fid, sid) {\n this.paths.push({\n id: pid,\n fid: fid,\n sid: sid,\n strokecolor: null,\n strokedasharray: null,\n hidepath: null\n });\n },\n /**\n * Removes a path\n * @param {*} id id of the place\n */\n removePath: function(id) {\n this.paths = this.paths.filter(\n function(p) {\n return p.id != id;\n }\n );\n },\n /**\n * Returns an array of paths touching a place\n * @param {*} id id of the place\n * @returns {array}\n */\n getTouchingPaths: function(id) {\n return this.paths.filter(\n function(p) {\n return p.fid == id || p.sid == id;\n }\n );\n },\n /**\n * Returns the course module id linked to a place\n * @param {*} id id of the place\n * @returns {number} id of the linked course module\n */\n getActivityId: function(id) {\n let place = this.places.filter(\n function(e) {\n return id == e.id;\n }\n );\n if (place.length > 0) {\n return place[0].linkedActivity;\n } else {\n return null;\n }\n },\n /**\n * Sets the id of the linked course module\n * @param {*} id id of the place\n * @param {*} linkedActivity course module id\n */\n setActivityId: function(id, linkedActivity) {\n let place = this.places.filter(\n function(e) {\n return id == e.id;\n }\n );\n if (place.length > 0) {\n place[0].linkedActivity = linkedActivity;\n }\n },\n /**\n * Sets the color of 'stroke', 'place' or 'visited'\n * @param {*} type type of the color\n * @param {*} color color in hex format\n */\n setColor: function(type, color) {\n switch (type) {\n case 'stroke':\n this.strokecolor = color;\n break;\n case 'place':\n this.placecolor = color;\n break;\n case 'visited':\n this.visitedcolor = color;\n break;\n case 'text':\n this.textcolor = color;\n break;\n }\n },\n /**\n * Gets the color of 'stroke', 'place' or 'visited'\n * @param {*} type type of the color\n * @returns {string} color in hex format\n */\n getColor: function(type) {\n switch (type) {\n case 'stroke':\n return this.strokecolor;\n case 'place':\n return this.placecolor;\n case 'visited':\n return this.visitedcolor;\n case 'text':\n return this.textcolor;\n }\n return null;\n },\n /**\n * Returns the current id\n * @returns {number}\n */\n getId: function() {\n return this.id;\n },\n /**\n * Sets the dimensions of the background image\n * @param {*} width\n * @param {*} height\n */\n setBackgroundDimensions: function(width, height) {\n this.width = width;\n this.height = height;\n },\n /**\n * Returns all paths starting at a place\n * @param {*} id id of the place\n * @returns {array}\n */\n getPathsWithFid: function(id) {\n return this.paths.filter(function(p) {\n return p.fid == id;\n });\n },\n /**\n * Returns all paths ending at a place\n * @param {*} id id of the place\n * @returns {array}\n */\n getPathsWithSid: function(id) {\n return this.paths.filter(function(p) {\n return p.sid == id;\n });\n },\n /**\n * Returns the attributes of placestore\n * @returns {object}\n */\n getPlacestore: function() {\n return {\n id: this.id,\n places: this.places,\n paths: this.paths,\n startingplaces: this.startingplaces,\n targetplaces: this.targetplaces,\n placecolor: this.placecolor,\n strokecolor: this.strokecolor,\n strokeopacity: this.strokeopacity,\n textcolor: this.textcolor,\n visitedcolor: this.visitedcolor,\n height: this.height,\n width: this.width,\n hidepaths: this.hidepaths,\n mapid: this.mapid,\n usecheckmark: this.usecheckmark,\n editmode: this.editmode,\n version: this.version,\n pulse: this.pulse,\n hover: this.hover,\n showall: this.showall,\n showtext: this.showtext,\n slicemode: this.slicemode,\n showwaygone: this.showwaygone,\n };\n },\n /**\n * Sets hidepaths attribute\n * @param {boolean} value\n */\n setHidePaths: function(value) {\n this.hidepaths = value;\n },\n /**\n * Returns the value of hidepaths attribute\n * @returns {boolean}\n */\n getHidePaths: function() {\n return this.hidepaths;\n },\n /**\n * Sets pulse attribute\n * @param {boolean} value\n */\n setPulse: function(value) {\n this.pulse = value;\n },\n /**\n * Returns the value of pulse attribute\n * @returns {boolean}\n */\n getPulse: function() {\n return this.pulse;\n },\n /**\n * Sets hover attribute\n * @param {boolean} value\n */\n setHover: function(value) {\n this.hover = value;\n },\n /**\n * Returns the value of hover attribute\n * @returns {boolean}\n */\n getHover: function() {\n return this.hover;\n },\n /**\n * Sets showall attribute\n * @param {boolean} value\n */\n setShowall: function(value) {\n this.showall = value;\n },\n /**\n * Returns the value of showall attribute\n * @returns {boolean}\n */\n getShowall: function() {\n return this.showall;\n },\n /**\n * Returns the mapid\n * @returns {string}\n */\n getMapid: function() {\n return this.mapid;\n },\n /**\n * Returns the value of usecheckmark attribute\n * @returns {boolean}\n */\n getUseCheckmark: function() {\n return this.usecheckmark;\n },\n /**\n * Sets the value of usecheckmark attribute\n * @param {boolean} value\n */\n setUseCheckmark: function(value) {\n this.usecheckmark = value;\n },\n /**\n * Returns an array with all activity ids\n * @returns {array}\n */\n getAllActivities: function() {\n let activities = [];\n this.places.forEach(function(p) {\n if (p.linkedActivity) {\n activities.push(p.linkedActivity);\n }\n });\n return activities;\n },\n /**\n * Sets stroke opacity\n * @param {number} value\n */\n setStrokeOpacity: function(value) {\n this.strokeopacity = value;\n },\n /**\n * Returns the current stroke opacity\n * @returns {number}\n */\n getStrokeOpacity: function() {\n return this.strokeopacity;\n },\n /**\n * Sets stroke opacity to 0\n * @param {number} value\n */\n setHideStroke: function(value) {\n this.strokeopacity = (value ? 0 : 1);\n },\n /**\n * Returns the current stroke opacity\n * @returns {number}\n */\n getHideStroke: function() {\n return this.strokeopacity < 1;\n },\n /**\n * Returns the value of showtext attribute\n * @returns {boolean}\n */\n getShowText: function() {\n return this.showtext;\n },\n /**\n * Sets the value of showtext attribute\n * @param {boolean} value\n */\n setShowText: function(value) {\n this.showtext = value;\n },\n /**\n * Returns an array with all place identifiers\n * @returns {array}\n */\n getPlaces: function() {\n return this.places;\n },\n /**\n * Returns if slicemode is enabled\n * @returns {boolean}\n */\n getSliceMode: function() {\n return this.slicemode;\n },\n /**\n * Sets state of slicemode\n * @param {boolean} value\n */\n setSliceMode: function(value) {\n this.slicemode = value;\n },\n /**\n * Returns if showwaygone is enabled\n * @returns {boolean}\n */\n getShowWayGone: function() {\n return this.showwaygone;\n },\n /**\n * Sets state of showwaygone\n * @param {boolean} value\n */\n setShowWayGone: function(value) {\n this.showwaygone = value;\n },\n};\n\nexport default placestore;\n"],"names":["placestore","version","id","places","paths","startingplaces","targetplaces","placecolor","strokecolor","strokeopacity","textcolor","visitedcolor","height","width","hidepaths","mapid","usecheckmark","editmode","pulse","hover","showall","showtext","slicemode","showwaygone","loadJSON","json","fromjson","JSON","parse","Object","assign","this","buildJSON","stringify","getPlacestore","addPlace","linkId","linkedActivity","push","length","addStartingPlace","removePlace","removeStartingPlace","removeTargetPlace","filter","p","e","isStartingPlace","includes","addTargetPlace","isTargetPlace","addPath","pid","fid","sid","strokedasharray","hidepath","removePath","getTouchingPaths","getActivityId","place","setActivityId","setColor","type","color","getColor","getId","setBackgroundDimensions","getPathsWithFid","getPathsWithSid","setHidePaths","value","getHidePaths","setPulse","getPulse","setHover","getHover","setShowall","getShowall","getMapid","getUseCheckmark","setUseCheckmark","getAllActivities","activities","forEach","setStrokeOpacity","getStrokeOpacity","setHideStroke","getHideStroke","getShowText","setShowText","getPlaces","getSliceMode","setSliceMode","getShowWayGone","setShowWayGone"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/learningmap.js b/amd/src/learningmap.js index cd4d76a..9343c49 100644 --- a/amd/src/learningmap.js +++ b/amd/src/learningmap.js @@ -48,7 +48,7 @@ export const init = () => { // DOM nodes for the editor let mapdiv = document.getElementById('learningmap-editor-map'); - let code = document.getElementById('id_introeditor_text'); + let code = document.getElementById('id_svgcode'); // DOM nodes for the activity selector let activitySetting = document.getElementById('learningmap-activity-setting'); @@ -230,7 +230,7 @@ export const init = () => { activitySetting.style.display = 'none'; } - let backgroundfileNode = document.getElementById('id_introeditor_itemid_fieldset'); + let backgroundfileNode = document.getElementById('id_backgroundfile_fieldset'); if (backgroundfileNode) { let observer = new MutationObserver(refreshBackgroundImage); observer.observe(backgroundfileNode, {attributes: true, childList: true, subtree: true}); diff --git a/amd/src/placestore.js b/amd/src/placestore.js index 41288fe..d825464 100644 --- a/amd/src/placestore.js +++ b/amd/src/placestore.js @@ -1,5 +1,5 @@ let placestore = { - version: 2024022102, + version: 2024072201, id: 0, places: [], paths: [], @@ -36,7 +36,7 @@ let placestore = { // eslint-disable-next-line no-empty } catch { } // Update version (only relevant if learning map is saved) - this.version = 2024022102; + this.version = 2024072201; }, /** * Returns placestore as a JSON string () diff --git a/backup/moodle2/backup_learningmap_stepslib.php b/backup/moodle2/backup_learningmap_stepslib.php index d94a761..609dfe4 100644 --- a/backup/moodle2/backup_learningmap_stepslib.php +++ b/backup/moodle2/backup_learningmap_stepslib.php @@ -32,12 +32,24 @@ protected function define_structure(): backup_nested_element { $learningmap = new backup_nested_element( 'learningmap', ['id'], - ['course', 'name', 'intro', 'introformat', 'timemodified', 'placestore', 'completiontype', 'backlink'] + [ + 'course', + 'name', + 'intro', + 'introformat', + 'timemodified', + 'placestore', + 'completiontype', + 'backlink', + 'svgcode', + 'showmaponcoursepage', + ] ); $learningmap->set_source_table('learningmap', ['id' => backup::VAR_ACTIVITYID]); $learningmap->annotate_files('mod_learningmap', 'intro', null); + $learningmap->annotate_files('mod_learningmap', 'background', null); return $this->prepare_activity_structure($learningmap); } diff --git a/backup/moodle2/restore_learningmap_activity_task.class.php b/backup/moodle2/restore_learningmap_activity_task.class.php index b4b3b56..2b8ee58 100644 --- a/backup/moodle2/restore_learningmap_activity_task.class.php +++ b/backup/moodle2/restore_learningmap_activity_task.class.php @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +use mod_learningmap\migrationhelper; + defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->dirroot . '/mod/learningmap/backup/moodle2/restore_learningmap_stepslib.php'); @@ -53,7 +55,7 @@ protected function define_my_steps(): void { */ public static function define_decode_contents(): array { $contents = []; - $contents[] = new restore_decode_content('learningmap', ['intro'], 'learningmap'); + $contents[] = new restore_decode_content('learningmap', ['intro', 'svgcode'], 'learningmap'); return $contents; } @@ -78,6 +80,7 @@ public function after_restore(): void { $modinfo = get_fast_modinfo($courseid); $item = $DB->get_record('learningmap', ['id' => $this->get_activityid()], '*', MUST_EXIST); + $cm = get_coursemodule_from_instance('learningmap', $item->id, $courseid); $placestore = json_decode($item->placestore); @@ -99,25 +102,30 @@ public function after_restore(): void { } $oldmapid = $placestore->mapid; $newmapid = uniqid(); - $item->intro = str_replace('learningmap-svgmap-' . $oldmapid, 'learningmap-svgmap-' . $newmapid, $item->intro); $placestore->mapid = $newmapid; - if (!isset($placestore->version) || $placestore->version < 2024022102) { - $placestore->version = 2024022102; + if (!isset($placestore->version) || $placestore->version < 2024072201) { + $placestore->version = 2024072201; // Needs 1 as default value (otherwise all place strokes would be hidden). if (!isset($placestore->strokeopacity)) { $placestore->strokeopacity = 1; } - $mapworker = new \mod_learningmap\mapworker($item->intro, (array)$placestore); + if (empty($item->svgcode)) { + $mapcode = $item->intro; + $item->intro = ''; + $item->showmaponcoursepage = $cm->showdescription; + migrationhelper::move_files_to_background_filearea($item->id); + } else { + $mapcode = $item->svgcode; + } + $mapworker = new \mod_learningmap\mapworker($mapcode, (array)$placestore); $mapworker->replace_stylesheet(); $mapworker->replace_defs(); - $item->intro = $mapworker->get_svgcode(); + $item->svgcode = $mapworker->get_svgcode(); } - - $json = json_encode($placestore); - - $DB->set_field('learningmap', 'placestore', $json, ['id' => $this->get_activityid()]); - $DB->set_field('learningmap', 'intro', $item->intro, ['id' => $this->get_activityid()]); - $DB->set_field('learningmap', 'course', $courseid, ['id' => $this->get_activityid()]); + $item->svgcode = str_replace('learningmap-svgmap-' . $oldmapid, 'learningmap-svgmap-' . $newmapid, $item->svgcode); + $item->placestore = json_encode($placestore); + $item->course = $courseid; + $DB->update_record('learningmap', $item); } } diff --git a/backup/moodle2/restore_learningmap_stepslib.php b/backup/moodle2/restore_learningmap_stepslib.php index 451b01d..9de82ff 100644 --- a/backup/moodle2/restore_learningmap_stepslib.php +++ b/backup/moodle2/restore_learningmap_stepslib.php @@ -58,5 +58,6 @@ protected function process_learningmap($data): void { */ protected function after_execute(): void { $this->add_related_files('mod_learningmap', 'intro', null); + $this->add_related_files('mod_learningmap', 'background', null); } } diff --git a/classes/migrationhelper.php b/classes/migrationhelper.php new file mode 100644 index 0000000..5a8b0a7 --- /dev/null +++ b/classes/migrationhelper.php @@ -0,0 +1,119 @@ +. + +namespace mod_learningmap; + +/** + * This class provides helper functions for migration from learningmaps being stored in the intro field to the new column svgcode. + * + * @package mod_learningmap + * @copyright 2024 ISB Bayern + * @author Stefan Hanauska + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class migrationhelper { + /** + * Moves all files from the intro field to the new filearea 'background'. + * This is only executed if there are no files in the background filearea yet. + * @param int $instanceid The id of the learningmap instance. + */ + public static function move_files_to_background_filearea(int $instanceid) { + global $DB; + $fs = get_file_storage(); + + // Getting course modules via DB query as get_coursemodule_from_instance() is not available during upgrade. + $moduleid = $DB->get_field('modules', 'id', ['name' => 'learningmap']); + + if (empty($moduleid)) { + return; + } + + $cm = $DB->get_record('course_modules', ['instance' => $instanceid, 'module' => $moduleid]); + + $contextid = \context_module::instance($cm->id)->id; + + // Check if there are already files in the background filearea. + $backgroundfiles = $fs->get_area_files($contextid, 'mod_learningmap', 'background', 0, 'id', false); + if (count($backgroundfiles) != 0) { + return; + } + + $files = $fs->get_area_files($contextid, 'mod_learningmap', 'intro', 0, 'id', false); + foreach ($files as $file) { + $filerecord = [ + 'contextid' => $file->get_contextid(), + 'component' => 'mod_learningmap', + 'filearea' => 'background', + 'itemid' => 0, + 'filepath' => '/', + 'filename' => $file->get_filename(), + 'timecreated' => time(), + 'timemodified' => time(), + ]; + $fs->create_file_from_storedfile($filerecord, $file); + $file->delete(); + } + } + + /** + * Checks if the given learningmap instance has a SVG code stored in the 'svgcode' column. + * + * @param int $instanceid The id of the learningmap instance. + * @return bool + */ + public static function is_version_without_svgcode(int $instanceid): bool { + global $DB; + $entry = $DB->get_record('learningmap', ['id' => $instanceid]); + return empty($entry->svgcode); + } + + /** + * Update one learningmap instance to have the SVG code stored in the 'svgcode' column. + * + * @param int $instanceid The id of the learningmap instance. If empty, all learningmaps will be updated. + * @return void + */ + public static function update_learningmaps_to_use_svgcode(int $instanceid = 0) { + global $DB; + + // Getting course modules via DB query as get_coursemodule_from_instance() is not available during upgrade. + $moduleid = $DB->get_field('modules', 'id', ['name' => 'learningmap']); + + if (empty($moduleid)) { + return; + } + + $params = []; + $where = 'svgcode IS NULL'; + if (!empty($instanceid)) { + $where .= ' AND id = :id'; + $params['id'] = $instanceid; + } + + $learningmaps = $DB->get_recordset_select('learningmap', $where, $params); + foreach ($learningmaps as $learningmap) { + $cm = $DB->get_record('course_modules', ['instance' => $learningmap->id, 'module' => $moduleid]); + $learningmap->svgcode = $learningmap->intro; + $learningmap->intro = ''; + // To keep a consistent behavior with the old version, we set the showmaponcoursepage to the value of + // showdescription (which was responsible for displaying the map on course page before). + $learningmap->showmaponcoursepage = $cm->showdescription; + $DB->update_record('learningmap', $learningmap); + self::move_files_to_background_filearea($learningmap->id); + } + $learningmaps->close(); + } +} diff --git a/db/install.xml b/db/install.xml index 04e7a38..6c96825 100644 --- a/db/install.xml +++ b/db/install.xml @@ -1,5 +1,5 @@ - @@ -15,6 +15,8 @@ + + diff --git a/db/upgrade.php b/db/upgrade.php index ff4c6fe..dd22c39 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -37,7 +37,7 @@ function xmldb_learningmap_upgrade($oldversion) { if ($entries) { foreach ($entries as $entry) { $placestore = json_decode($entry->placestore, true); - $placestore['version'] = 2024022102; + $placestore['version'] = 2024032401; // Needs 1 as default value (otherwise all place strokes would be hidden). if (!isset($placestore['strokeopacity'])) { $placestore['strokeopacity'] = 1; @@ -67,5 +67,35 @@ function xmldb_learningmap_upgrade($oldversion) { upgrade_mod_savepoint(true, 2024022102, 'learningmap'); } + if ($oldversion < 2024072201) { + // Define field id to be added to learningmap. + $table = new xmldb_table('learningmap'); + $field = new xmldb_field('svgcode', XMLDB_TYPE_TEXT, null, null, null, null, null, 'backlink'); + + // Conditionally launch add field id. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + $field = new xmldb_field('showmaponcoursepage', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'svgcode'); + + // Conditionally launch add field id. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + $field = new xmldb_field('introformat', XMLDB_TYPE_INTEGER, '4', null, null, null, '0', 'intro'); + + // Launch change of default for field introformat. + $dbman->change_field_default($table, $field); + + // Move the learningmap content to the new database fields. This also handles moving the files to the new + // filearea 'background'. + \mod_learningmap\migrationhelper::update_learningmaps_to_use_svgcode(); + + // Learningmap savepoint reached. + upgrade_mod_savepoint(true, 2024072201, 'learningmap'); + } + return true; } diff --git a/index.php b/index.php index c479587..a31bfe9 100644 --- a/index.php +++ b/index.php @@ -18,7 +18,7 @@ * Index.php for mod_learningmap * * @package mod_learningmap - * @copyright 2021-2024, ISB Bayern + * @copyright 2021-2024, ISB Bayern * @author Stefan Hanauska * @license https://www.gnu.org/licenses/agpl-3.0.html GNU AGPL v3 or later */ diff --git a/lang/en/learningmap.php b/lang/en/learningmap.php index 857cd5a..1339f25 100644 --- a/lang/en/learningmap.php +++ b/lang/en/learningmap.php @@ -83,8 +83,8 @@ $string['showall_help'] = 'This option shows all paths and places of the map right from the start. Places and paths not yet reachable are dimmed.'; $string['showbacklink'] = 'Show backlinks on course module pages'; $string['showbacklink_help'] = 'This option shows a link "Back to learning map" on every course module page that belongs to the map.'; -$string['showdescription'] = 'Show map on course page'; -$string['showdescription_help'] = 'If checked, the learning map will be displayed on the course page (like a label). Else there will be a link and the map will be displayed on a separate page.'; +$string['showmaponcoursepage'] = 'Show map on course page'; +$string['showmaponcoursepage_help'] = 'If checked, the learning map will be displayed on the course page (like a label). Else there will be a link and the map will be displayed on a separate page.'; $string['showtext'] = 'Show activity names'; $string['showtext_help'] = 'This option shows the names of the activities as a text besides the places. The text can be dragged around and is automatically updated when the activity name changes.'; $string['showwaygone'] = 'Highlight way'; diff --git a/lib.php b/lib.php index c2ece05..ee4dcbe 100644 --- a/lib.php +++ b/lib.php @@ -23,6 +23,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use core\plugininfo\format; use mod_learningmap\cachemanager; /** @@ -143,7 +144,7 @@ function learningmap_pluginfile($course, $cm, $context, $filearea, $args, $force $fullpath = "/$context->id/mod_learningmap/$filearea/" . implode('/', $args); $fs = get_file_storage(); - if (!$file = $fs->get_file_by_hash(sha1($fullpath)) || $file->is_directory()) { + if ((!$file = $fs->get_file_by_hash(sha1($fullpath))) || $file->is_directory()) { return false; } @@ -186,24 +187,35 @@ function learningmap_get_coursemodule_info($cm): cached_cm_info { * @return void */ function learningmap_cm_info_dynamic(cm_info $cm): void { + global $DB; + $showmaponcoursepage = $DB->get_field('learningmap', 'showmaponcoursepage', ['id' => $cm->instance]); // Decides whether to display the link. - if ($cm->showdescription == 1) { + if (!empty($showmaponcoursepage)) { $cm->set_no_view_link(true); } } /** * Generates course module info, especially the map (as intro). - * If showdescription is not set, this function does nothing. + * If showdescription is set, this function outputs the intro and the map. * * @param cm_info $cm * @return void */ function learningmap_cm_info_view(cm_info $cm): void { - global $CFG, $OUTPUT, $PAGE; - // Only show map on course page if showdescription is set. - if ($cm->showdescription == 1) { - $groupdropdown = ''; + global $DB, $OUTPUT; + + $learningmap = $DB->get_record('learningmap', ['id' => $cm->instance]); + $intro = ''; + $groupdropdown = ''; + $mapcontainer = ''; + + if (!empty($cm->showdescription) && !empty($learningmap->intro)) { + $intro = format_module_intro('learningmap', $learningmap, $cm->id); + } + + // Only show map on course page if showmaponcoursepage is set. + if (!empty($learningmap->showmaponcoursepage)) { if (!empty($cm->groupmode)) { $groupdropdown = groups_print_activity_menu( $cm, @@ -223,30 +235,15 @@ function learningmap_cm_info_view(cm_info $cm): void { ); } - $enableliveupdatercomponent = true; - if ($CFG->branch < 400) { - // Only in moodle <4.0 we call this separate manual completion watcher. - // From moodle 4.0 on this is handled by the mustache loader. - $PAGE->requires->js_call_amd( - 'mod_learningmap/manual-completion-watch', - 'init', - ['coursemodules' => learningmap_get_place_cm($cm)] - ); - // Disable the live updater (a reactive component which only works with moodle >=4.0). - $enableliveupdatercomponent = false; - } - $content = $OUTPUT->render_from_template( + $mapcontainer = $OUTPUT->render_from_template( 'mod_learningmap/rendercontainer', - ['cmId' => $cm->id, 'enableLiveUpdater' => $enableliveupdatercomponent] + ['cmId' => $cm->id, 'enableLiveUpdater' => true] ); - $cm->set_content($groupdropdown . $content, true); - $cm->set_extra_classes('label'); // ToDo: Add extra CSS. - // This method check is needed to provide backwards compatibility to moodle versions below 4.0. - if (method_exists($cm, 'set_custom_cmlist_item')) { - $cm->set_custom_cmlist_item(true); - } + $cm->set_custom_cmlist_item(true); } + + $cm->set_content($groupdropdown . $intro . $mapcontainer, true); } /** @@ -274,19 +271,27 @@ function learningmap_get_place_cm(cm_info $cm): array { * @return string */ function learningmap_get_learningmap(cm_info $cm): string { - global $DB, $OUTPUT, $PAGE; + global $DB, $OUTPUT; $context = context_module::instance($cm->id); $map = $DB->get_record("learningmap", ["id" => $cm->instance]); + if (empty($map->svgcode)) { + $mapcode = $map->intro; + $filearea = 'intro'; + } else { + $mapcode = $map->svgcode; + $filearea = 'background'; + } + $svg = file_rewrite_pluginfile_URLS( - $map->intro, + $mapcode, 'pluginfile.php', $context->id, 'mod_learningmap', - 'intro', - null + $filearea, + 0 ); $placestore = json_decode($map->placestore, true); diff --git a/mod_form.php b/mod_form.php index 2e4c298..39f421e 100644 --- a/mod_form.php +++ b/mod_form.php @@ -75,6 +75,8 @@ public function definition(): void { $mform->addRule('name', null, 'required', null, 'client'); $mform->addHelpButton('name', 'name', 'learningmap'); + $this->standard_intro_elements(); + $mform->addElement( 'html', $OUTPUT->render_from_template( @@ -104,9 +106,9 @@ public function definition(): void { ) ); - $mform->addElement('checkbox', 'showdescription', get_string('showdescription', 'learningmap')); - $mform->setType('showdescription', PARAM_INT); - $mform->addHelpButton('showdescription', 'showdescription', 'learningmap'); + $mform->addElement('advcheckbox', 'showmaponcoursepage', get_string('showmaponcoursepage', 'learningmap')); + $mform->setType('showmaponcoursepage', PARAM_INT); + $mform->addHelpButton('showmaponcoursepage', 'showmaponcoursepage', 'learningmap'); $backlinkallowed = get_config('mod_learningmap', 'backlinkallowed'); @@ -120,7 +122,7 @@ public function definition(): void { $mform->addElement( 'filemanager', - 'introeditor[itemid]', + 'backgroundfile', get_string('backgroundfile', 'learningmap'), null, [ @@ -129,18 +131,15 @@ public function definition(): void { 'subdirs' => 0, ] ); - $mform->addRule('introeditor[itemid]', null, 'required', null, 'client'); - $mform->addHelpButton('introeditor[itemid]', 'backgroundfile', 'learningmap'); + $mform->addRule('backgroundfile', null, 'required', null, 'client'); + $mform->addHelpButton('backgroundfile', 'backgroundfile', 'learningmap'); - $mform->addElement('textarea', 'introeditor[text]', get_string('svgcode', 'learningmap')); - $mform->setType('introeditor[text]', PARAM_RAW); + $mform->addElement('textarea', 'svgcode', get_string('svgcode', 'learningmap')); + $mform->setType('svgcode', PARAM_RAW); $mform->addElement('hidden', 'placestore'); $mform->setType('placestore', PARAM_RAW); - $mform->addElement('hidden', 'introeditor[format]', FORMAT_HTML); - $mform->setType('introeditor[format]', PARAM_INT); - $mform->closeHeaderBefore('header'); $PAGE->requires->js_call_amd('mod_learningmap/learningmap', 'init'); @@ -213,6 +212,8 @@ public function add_completion_rules(): array { public function data_preprocessing(&$defaultvalues): void { global $OUTPUT; + $draftitemid = file_get_submitted_draft_itemid('backgroundfile'); + // Initialize a new learningmap instance. if (!$this->current->instance) { // Every map gets a unique id for applying CSS. @@ -221,42 +222,59 @@ public function data_preprocessing(&$defaultvalues): void { $options = ['editmode' => true, 'mapid' => $mapid]; // Loads the SVG template to the textarea for the introeditor. // The textarea is hidden in the browser. - $defaultvalues['introeditor[text]'] = $OUTPUT->render_from_template( + $defaultvalues['svgcode'] = $OUTPUT->render_from_template( 'mod_learningmap/svgskeleton', $options ); - // Default behaviour is to act as a label. + // Default behaviour is to act as a label. As the user can't see the module description on a view page, + // the description is shown by default. $defaultvalues['showdescription'] = 1; + $defaultvalues['showmaponcoursepage'] = 1; // Encodes the base settings as json. Further default settings are // generated by javascript to avoid duplicate code. $defaultvalues['placestore'] = json_encode($options); } else { $context = context_module::instance($defaultvalues['coursemodule']); - $defaultvalues['intro'] = file_rewrite_pluginfile_URLS( - $defaultvalues['intro'], + if (empty($defaultvalues['svgcode'])) { + $mapcode = $defaultvalues['intro']; + $filearea = 'intro'; + } else { + $mapcode = $defaultvalues['svgcode']; + $filearea = 'background'; + } + + $defaultvalues['svgcode'] = file_rewrite_pluginfile_URLS( + $mapcode, 'pluginfile.php', $context->id, 'mod_learningmap', - 'intro', + $filearea, null ); $modinfo = get_fast_modinfo($defaultvalues['course']); $cm = $modinfo->get_cm($defaultvalues['coursemodule']); // Replace the stylesheet for editing mode. $mapworker = new mapworker( - $defaultvalues['intro'], + $mapcode, json_decode($defaultvalues['placestore'], true), $cm, true ); $mapworker->process_map_objects(); $mapworker->replace_stylesheet(); - $defaultvalues['intro'] = $mapworker->get_svgcode(); - // Make the introeditor use the values of the intro field. - // This is necessary to avoid inconsistencies. - $defaultvalues['introeditor']['text'] = $defaultvalues['intro']; + $defaultvalues['svgcode'] = $mapworker->get_svgcode(); } + $defaultvalues['svgcode'] = file_prepare_draft_area( + $draftitemid, + $context->id, + 'mod_learningmap', + 'background', + 0, + ['subdirs' => 0, 'maxfiles' => 1], + $defaultvalues['svgcode'] + ); + $defaultvalues['backgroundfile'] = $draftitemid; } /** @@ -268,13 +286,23 @@ public function data_preprocessing(&$defaultvalues): void { */ public function data_postprocessing($data): void { // As this form is also used for setting the default completion settings, there might not be an actual learningmap. - if (!empty($data->introeditor['text'])) { + if (!empty($data->svgcode)) { $mapworker = new mapworker( - $data->introeditor['text'], + $data->svgcode, json_decode($data->placestore, true) ); $mapworker->replace_stylesheet(); - $data->introeditor['text'] = $mapworker->get_svgcode(); + $data->svgcode = $mapworker->get_svgcode(); + + $data->svgcode = file_save_draft_area_files( + $data->backgroundfile, + $this->context->id, + 'mod_learningmap', + 'background', + 0, + ['subdirs' => 0, 'maxfiles' => 1], + $data->svgcode + ); } } diff --git a/styles.css b/styles.css index 518b2c2..3bf416a 100644 --- a/styles.css +++ b/styles.css @@ -94,6 +94,10 @@ position: relative; } +#page-mod-learningmap-mod #fitem_id_svgcode { + display: none; +} + .learningmap-used-activity { color: green; } diff --git a/templates/cssskeleton.mustache b/templates/cssskeleton.mustache index 8ee3f93..6413340 100644 --- a/templates/cssskeleton.mustache +++ b/templates/cssskeleton.mustache @@ -42,7 +42,7 @@ "mapid": "63bee8a5f1a1e", "usecheckmark": false, "editmode": true, - "version": 2024022102, + "version": 2024032402, "pulse": false, "hover": false, "showall": false, diff --git a/tests/generator/lib.php b/tests/generator/lib.php index 1fb367c..e385446 100644 --- a/tests/generator/lib.php +++ b/tests/generator/lib.php @@ -18,7 +18,7 @@ * mod_learningmap data generator * * @package mod_learningmap - * @copyright 2021-2024, ISB Bayern + * @copyright 2021-2024, ISB Bayern * @author Stefan Hanauska * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -36,8 +36,10 @@ public function create_instance($record = null, array $options = null): stdClass $record = (array)$record + [ 'name' => 'test map', - 'intro' => file_get_contents($CFG->dirroot . '/mod/learningmap/tests/generator/test.svg'), - 'introformat' => 1, + 'intro' => 'test intro', + 'introformat' => 0, + 'svgcode' => file_get_contents($CFG->dirroot . '/mod/learningmap/tests/generator/test.svg'), + 'showmaponcoursepage' => 1, 'placestore' => file_get_contents($CFG->dirroot . '/mod/learningmap/tests/generator/test.json'), 'completiontype' => 2, ]; diff --git a/tests/generator/test.json b/tests/generator/test.json index 1b0ef17..8021ef3 100644 --- a/tests/generator/test.json +++ b/tests/generator/test.json @@ -157,7 +157,7 @@ "mapid": "63bb1c8edb6af", "usecheckmark": false, "editmode": false, - "version": 2024022102, + "version": 2024032402, "pulse": false, "hover": false, "showall": false, diff --git a/tests/mod_learningmap_activitymanager_test.php b/tests/mod_learningmap_activitymanager_test.php index 33b3f59..ff22483 100644 --- a/tests/mod_learningmap_activitymanager_test.php +++ b/tests/mod_learningmap_activitymanager_test.php @@ -27,7 +27,7 @@ * Unit test for mod_learningmap * * @package mod_learningmap - * @copyright 2021-2024, ISB Bayern + * @copyright 2021-2024, ISB Bayern * @author Stefan Hanauska * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @group mod_learningmap diff --git a/tests/mod_learningmap_generation_test.php b/tests/mod_learningmap_generation_test.php index 9973808..0979b81 100644 --- a/tests/mod_learningmap_generation_test.php +++ b/tests/mod_learningmap_generation_test.php @@ -20,7 +20,7 @@ * Unit test for mod_learningmap * * @package mod_learningmap - * @copyright 2021-2024, ISB Bayern + * @copyright 2021-2024, ISB Bayern * @author Stefan Hanauska * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @group mod_learningmap diff --git a/tests/mod_learningmap_mapworker_test.php b/tests/mod_learningmap_mapworker_test.php index f717ea1..a2487e6 100644 --- a/tests/mod_learningmap_mapworker_test.php +++ b/tests/mod_learningmap_mapworker_test.php @@ -123,7 +123,7 @@ public function test_slicemode(): void { $this->setUser($this->user1); $placestore = json_decode($this->learningmap->placestore, true); $placestore['slicemode'] = true; - $mapworker = new mapworker($this->learningmap->intro, $placestore, $this->cm, false); + $mapworker = new mapworker($this->learningmap->svgcode, $placestore, $this->cm, false); $mapworker->process_map_objects(); // The values the overlay path description is expected to have. $expectedvalues = [ @@ -143,7 +143,7 @@ public function test_slicemode(): void { for ($i = 0; $i < 8; $i++) { $activitycoursemodule = $this->modinfo->get_cm($this->activities[$i]->cmid); $this->completion->set_module_viewed($activitycoursemodule, $this->user1->id); - $mapworker = new mapworker($this->learningmap->intro, $placestore, $this->cm, false); + $mapworker = new mapworker($this->learningmap->svgcode, $placestore, $this->cm, false); $mapworker->process_map_objects(); $overlay = $mapworker->get_attribute('learningmap-overlay', 'd'); $this->assertEquals($expectedvalues[$i], $overlay); @@ -160,7 +160,7 @@ public function test_visibility(): void { $this->setAdminUser(); $this->setUser($this->user1); $placestore = json_decode($this->learningmap->placestore, true); - $mapworker = new mapworker($this->learningmap->intro, $placestore, $this->cm, false); + $mapworker = new mapworker($this->learningmap->svgcode, $placestore, $this->cm, false); $mapworker->process_map_objects(); // Place p0 is a starting place, so it should be visible by default. $this->assertEquals(['p0'], $mapworker->get_active()); @@ -181,7 +181,7 @@ public function test_visibility(): void { for ($i = 0; $i < count($placestore['places']); $i++) { $acm = $this->modinfo->get_cm($placestore['places'][$i]['linkedActivity']); $this->completion->set_module_viewed($acm, $this->user1->id); - $mapworker = new mapworker($this->learningmap->intro, $placestore, $this->cm, false); + $mapworker = new mapworker($this->learningmap->svgcode, $placestore, $this->cm, false); $mapworker->process_map_objects(); // Calling array_unique removes duplicate entries (e.g. for starting places). $active = array_unique($mapworker->get_active()); diff --git a/version.php b/version.php index 81693c3..587c1c7 100644 --- a/version.php +++ b/version.php @@ -18,15 +18,15 @@ * Version information for mod_learningmap * * @package mod_learningmap - * @copyright 2021-2024, ISB Bayern + * @copyright 2021-2024, ISB Bayern * @author Stefan Hanauska * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'mod_learningmap'; -$plugin->release = '0.9.7'; -$plugin->version = 2024041500; +$plugin->release = '0.9.8'; +$plugin->version = 2024072201; $plugin->requires = 2020061500; $plugin->supported = [401, 404]; $plugin->maturity = MATURITY_STABLE; diff --git a/view.php b/view.php index 4e82748..3382dc9 100644 --- a/view.php +++ b/view.php @@ -27,7 +27,7 @@ require_once('lib.php'); $id = required_param('id', PARAM_INT); - [$course, $cm] = get_course_and_cm_from_cmid($id, 'learningmap'); +[$course, $cm] = get_course_and_cm_from_cmid($id, 'learningmap'); require_course_login($course, true, $cm); $context = context_module::instance($cm->id); @@ -38,9 +38,7 @@ $PAGE->set_url(new moodle_url('/mod/learningmap/view.php', ['id' => $id])); $PAGE->set_title(get_string('pluginname', 'mod_learningmap') . ' ' . $map->name); $PAGE->set_heading($map->name); -if ($CFG->branch >= 400) { - $PAGE->activityheader->set_description(''); -} + $completion = new completion_info($course); $completion->set_module_viewed($cm);