diff --git a/course/amd/build/actions.min.js b/course/amd/build/actions.min.js index b10f792241f..4dafca283b5 100644 --- a/course/amd/build/actions.min.js +++ b/course/amd/build/actions.min.js @@ -6,6 +6,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.3 */ -define("core_course/actions",["jquery","core/ajax","core/templates","core/notification","core/str","core/url","core/yui","core/modal_copy_to_clipboard","core/modal_factory","core/modal_events","core/key_codes","core/log","core_courseformat/courseeditor","core/event_dispatcher","core_course/events"],(function($,ajax,templates,notification,str,url,Y,ModalCopyToClipboard,ModalFactory,ModalEvents,KeyCodes,log,editor,EventDispatcher,CourseEvents){const componentActions=["moveSection","moveCm","addSection","deleteSection","cmDelete","cmDuplicate","sectionHide","sectionShow","cmHide","cmShow","cmStealth","sectionHighlight","sectionUnhighlight","cmMoveRight","cmMoveLeft"],courseeditor=editor.getCurrentCourseEditor();let formatname;var CSS_EDITINPROGRESS="editinprogress",CSS_EDITINGMOVE="editing_move",SELECTOR={ACTIVITYLI:"li.activity",ACTIONAREA:".actions",ACTIVITYACTION:"a.cm-edit-action",MENU:".moodle-actionmenu[data-enhance=moodle-core-actionmenu]",TOGGLE:".toggle-display,.dropdown-toggle",SECTIONLI:"li.section",SECTIONACTIONMENU:".section_action_menu",SECTIONITEM:'[data-for="section_title"]',ADDSECTIONS:".changenumsections [data-add-sections]",SECTIONBADGES:'[data-region="sectionbadges"]'};Y.use("moodle-course-coursebase",(function(){var courseformatselector=M.course.format.get_section_selector();courseformatselector&&(SELECTOR.SECTIONLI=courseformatselector)}));const dispatchEvent=function(eventName,detail,container,options){return container instanceof Element||void 0===container.get||(container=container.get(0)),EventDispatcher.dispatchEvent(eventName,detail,container,options)};var getModuleId=function(element){const item=element.get(0);if(item.dataset.id)return item.dataset.id;let id;return Y.use("moodle-course-util",(function(Y){id=Y.Moodle.core_course.util.cm.getId(Y.Node(item))})),id},addActivitySpinner=function(activity){activity.addClass(CSS_EDITINPROGRESS);var actionarea=activity.find(SELECTOR.ACTIONAREA).get(0);if(actionarea){var spinner=M.util.add_spinner(Y,Y.Node(actionarea));return spinner.show(),void 0!==activity.data("id")&&courseeditor.dispatch("cmLock",[activity.data("id")],!0),spinner}return null},addSectionSpinner=function(sectionelement){sectionelement.addClass(CSS_EDITINPROGRESS);var actionarea=sectionelement.find(SELECTOR.SECTIONACTIONMENU).get(0);if(actionarea){var spinner=M.util.add_spinner(Y,Y.Node(actionarea));return spinner.show(),void 0!==sectionelement.data("id")&&courseeditor.dispatch("sectionLock",[sectionelement.data("id")],!0),spinner}return null},addSectionLightbox=function(sectionelement){const item=sectionelement.get(0);var lightbox=M.util.add_lightbox(Y,Y.Node(item));return"section"==item.dataset.for&&item.dataset.id&&(courseeditor.dispatch("sectionLock",[item.dataset.id],!0),lightbox.setAttribute("data-state","section"),lightbox.setAttribute("data-state-id",item.dataset.id)),lightbox.show(),lightbox},removeSpinner=function(element,spinner,delay){window.setTimeout((function(){if(element.removeClass(CSS_EDITINPROGRESS),spinner&&spinner.hide(),void 0!==element.data("id")){const mutation="section"===element.data("for")?"sectionLock":"cmLock";courseeditor.dispatch(mutation,[element.data("id")],!1)}}),delay)},removeLightbox=function(lightbox,delay){lightbox&&window.setTimeout((function(){lightbox.hide(),lightbox.getAttribute("data-state")&&courseeditor.dispatch("".concat(lightbox.getAttribute("data-state"),"Lock"),[lightbox.getAttribute("data-state-id")],!1)}),delay)},initActionMenu=function(elementid){Y.use("moodle-course-coursebase",(function(){M.course.coursebase.invoke_function("setup_for_resource","#"+elementid)})),M.core.actionmenu&&M.core.actionmenu.newDOMNode&&M.core.actionmenu.newDOMNode(Y.one("#"+elementid))},editModule=function(moduleElement,cmid,target){var lightbox,action=target.attr("data-action"),spinner=addActivitySpinner(moduleElement),promises=ajax.call([{methodname:"core_course_edit_module",args:{id:cmid,action:action,sectionreturn:target.attr("data-sectionreturn")?target.attr("data-sectionreturn"):0}}],!0);"duplicate"===action&&(lightbox=addSectionLightbox(target.closest(SELECTOR.SECTIONLI))),$.when.apply($,promises).done((function(data){var mainElement,tabables,isInside,foundElement,elementToFocus=(mainElement=moduleElement,tabables=$("a:visible"),isInside=!1,foundElement=null,tabables.each((function(){if($.contains(mainElement[0],this))isInside=!0;else if(isInside)return foundElement=this,!1;return!0})),foundElement);moduleElement.replaceWith(data);let affectedids=[];$("
"+data+"
").find(SELECTOR.ACTIVITYLI).each((function(index){initActionMenu($(this).attr("id")),0===index&&(!function(elementId,action){var mainelement=$("#"+elementId),selector="[data-action="+action+"]";"groupsseparate"!==action&&"groupsvisible"!==action&&"groupsnone"!==action||(selector="[data-action=groupsseparate],[data-action=groupsvisible],[data-action=groupsnone]"),mainelement.find(selector).is(":visible")?mainelement.find(selector).focus():mainelement.find(SELECTOR.MENU).find(SELECTOR.TOGGLE).focus()}($(this).attr("id"),action),elementToFocus=null),affectedids.push(getModuleId($(this)))})),elementToFocus&&elementToFocus.focus(),removeSpinner(moduleElement,spinner,400),removeLightbox(lightbox,400),moduleElement.trigger($.Event("coursemoduleedited",{ajaxreturn:data,action:action})),courseeditor.dispatch("legacyActivityAction",action,cmid,affectedids)})).fail((function(ex){removeSpinner(moduleElement,spinner),removeLightbox(lightbox);var e=$.Event("coursemoduleeditfailed",{exception:ex,action:action});moduleElement.trigger(e),e.isDefaultPrevented()||notification.exception(ex)}))},refreshModule=function(element,cmid,sectionreturn){void 0===sectionreturn&&(sectionreturn=courseeditor.sectionReturn);const activityElement=$(element);var spinner=addActivitySpinner(activityElement),promises=ajax.call([{methodname:"core_course_get_module",args:{id:cmid,sectionreturn:sectionreturn}}],!0);return new Promise(((resolve,reject)=>{$.when.apply($,promises).done((function(data){removeSpinner(activityElement,spinner,400),replaceActivityHtmlWith(data),resolve(data)})).fail((function(){removeSpinner(activityElement,spinner),reject()}))}))},confirmDeleteModule=function(mainelement,onconfirm){var modtypename=mainelement.attr("class").match(/modtype_([^\s]*)/)[1],modulename=function(element){var name;Y.use("moodle-course-util",(function(Y){name=Y.Moodle.core_course.util.cm.getName(Y.Node(element.get(0)))}));const state=courseeditor.state,cmid=getModuleId(element);var _state$cm$get;return!name&&state&&cmid&&(name=null===(_state$cm$get=state.cm.get(cmid))||void 0===_state$cm$get?void 0:_state$cm$get.name),name}(mainelement);str.get_string("pluginname",modtypename).done((function(pluginname){var plugindata={type:pluginname,name:modulename};str.get_strings([{key:"confirm",component:"core"},{key:null===modulename?"deletechecktype":"deletechecktypename",param:plugindata},{key:"yes"},{key:"no"}]).done((function(s){notification.confirm(s[0],s[1],s[2],s[3],onconfirm)}))}))},replaceActionItem=function(actionitem,image,stringname,stringcomponent,newaction){var stringRequests=[{key:stringname,component:stringcomponent}];return str.get_strings(stringRequests).then((function(strings){return actionitem.find("span.menu-action-text").html(strings[0]),templates.renderPix(image,"core")})).then((function(pixhtml){actionitem.find(".icon").replaceWith(pixhtml),actionitem.attr("data-action",newaction)})).catch(notification.exception)},defaultEditSectionHandler=function(sectionElement,actionItem,data,courseformat,sectionid){var action=actionItem.attr("data-action");if("hide"===action||"show"===action){if("hide"===action?(sectionElement.addClass("hidden"),setSectionBadge(sectionElement[0],"hiddenfromstudents",!0,!1),replaceActionItem(actionItem,"i/show","showfromothers","format_"+courseformat,"show")):(setSectionBadge(sectionElement[0],"hiddenfromstudents",!1,!1),sectionElement.removeClass("hidden"),replaceActionItem(actionItem,"i/hide","hidefromothers","format_"+courseformat,"hide")),void 0!==data.modules)for(var i in data.modules)replaceActivityHtmlWith(data.modules[i]);void 0!==data.section_availability&§ionElement.find(".section_availability").first().replaceWith(data.section_availability);void 0!==courseeditor.state.section.get(sectionid)&&courseeditor.dispatch("sectionState",[sectionid])}else if("setmarker"===action){var oldmarker=$(SELECTOR.SECTIONLI+".current"),oldActionItem=oldmarker.find(SELECTOR.SECTIONACTIONMENU+" a[data-action=removemarker]");oldmarker.removeClass("current"),replaceActionItem(oldActionItem,"i/marker","highlight","core","setmarker"),sectionElement.addClass("current"),replaceActionItem(actionItem,"i/marked","highlightoff","core","removemarker"),courseeditor.dispatch("legacySectionAction",action,sectionid),setSectionBadge(sectionElement[0],"iscurrent",!0,!0)}else"removemarker"===action&&(sectionElement.removeClass("current"),replaceActionItem(actionItem,"i/marker","highlight","core","setmarker"),courseeditor.dispatch("legacySectionAction",action,sectionid),setSectionBadge(sectionElement[0],"iscurrent",!1,!0))};var replaceActivityHtmlWith=function(activityHTML){$("
"+activityHTML+"
").find(SELECTOR.ACTIVITYLI).each((function(){var id=$(this).attr("id");let focusedPath=function(id){const element=document.getElementById(id);if(element&&element.contains(document.activeElement))return element.querySelector(SELECTOR.ACTIONAREA).contains(document.activeElement)?"".concat(SELECTOR.ACTIONAREA,' [tabindex="0"]'):document.activeElement.id?"#".concat(document.activeElement.id):void 0}(id);if($(SELECTOR.ACTIVITYLI+"#"+id).replaceWith(activityHTML),initActionMenu(id),focusedPath){var _newItem$querySelecto;null===(_newItem$querySelecto=document.getElementById(id).querySelector(focusedPath))||void 0===_newItem$querySelecto||_newItem$querySelecto.focus()}}))},editSection=function(sectionElement,sectionid,target,courseformat){var action=target.attr("data-action"),sectionreturn=target.attr("data-sectionreturn")?target.attr("data-sectionreturn"):0;if(courseeditor.supportComponents&&componentActions.includes(action))return!1;var spinner=addSectionSpinner(sectionElement),promises=ajax.call([{methodname:"core_course_edit_section",args:{id:sectionid,action:action,sectionreturn:sectionreturn}}],!0),lightbox=addSectionLightbox(sectionElement);return $.when.apply($,promises).done((function(dataencoded){var data=$.parseJSON(dataencoded);removeSpinner(sectionElement,spinner),removeLightbox(lightbox),sectionElement.find(SELECTOR.SECTIONACTIONMENU).find(SELECTOR.TOGGLE).focus();var e=$.Event("coursesectionedited",{ajaxreturn:data,action:action});sectionElement.trigger(e),e.isDefaultPrevented()||defaultEditSectionHandler(sectionElement,target,data,courseformat,sectionid)})).fail((function(ex){removeSpinner(sectionElement,spinner),removeLightbox(lightbox);var e=$.Event("coursesectioneditfailed",{exception:ex,action:action});sectionElement.trigger(e),e.isDefaultPrevented()||notification.exception(ex)})),!0},setSectionBadge=function(sectionElement,badgetype,add,removeOther){const sectionbadges=sectionElement.querySelector(SELECTOR.SECTIONBADGES);if(!sectionbadges)return;const badge=sectionbadges.querySelector('[data-type="'+badgetype+'"]');badge&&(add?(removeOther&&document.querySelectorAll('[data-type="'+badgetype+'"]').forEach((b=>{b.classList.add("d-none")})),badge.classList.remove("d-none")):badge.classList.add("d-none"))};return Y.use("moodle-course-coursebase",(function(){M.course.coursebase.register_module({set_visibility_resource_ui:function(args){var mainelement=$(args.element.getDOMNode()),cmid=getModuleId(mainelement);if(cmid){var sectionreturn=mainelement.find("."+CSS_EDITINGMOVE).attr("data-sectionreturn");refreshModule(mainelement,cmid,sectionreturn)}},updateMovedCmState:params=>{const cm=courseeditor.state.cm.get(params.cmid);void 0!==cm&&courseeditor.dispatch("sectionState",[cm.sectionid]),courseeditor.dispatch("cmState",[params.cmid])},updateMovedSectionState:()=>{courseeditor.dispatch("courseState")}})})),courseeditor.addMutations({legacyActivityAction:function(statemanager,action,cmid,affectedids){const state=statemanager.state,cm=state.cm.get(cmid);if(void 0===cm)return;const section=state.section.get(cm.sectionid);if(void 0!==section){switch(courseeditor.dispatch("cmLock",[cm.id],!0),statemanager.setReadOnly(!1),cm.locked=!1,action){case"delete":section.cmlist=section.cmlist.reduce(((cmlist,current)=>(current!=cmid&&cmlist.push(current),cmlist)),[]),state.cm.delete(cmid);break;case"hide":case"show":case"duplicate":courseeditor.dispatch("cmState",affectedids)}statemanager.setReadOnly(!0)}},legacySectionAction:function(statemanager,action,sectionid){const state=statemanager.state,section=state.section.get(sectionid);if(void 0!==section){switch(statemanager.setReadOnly(!1),section.locked=!0,statemanager.setReadOnly(!0),statemanager.setReadOnly(!1),section.locked=!1,action){case"setmarker":state.section.forEach((current=>{current.id!=sectionid&&(current.current=!1)})),section.current=!0;break;case"removemarker":section.current=!1}statemanager.setReadOnly(!0)}}}),{initCoursePage:function(courseformat){formatname=courseformat,$("body").on("click keypress",SELECTOR.ACTIVITYLI+" "+SELECTOR.ACTIVITYACTION+"[data-action]",(function(e){if("keypress"!==e.type||13===e.keyCode){var actionItem=$(this),moduleElement=actionItem.closest(SELECTOR.ACTIVITYLI),action=actionItem.attr("data-action"),moduleId=getModuleId(moduleElement);switch(action){case"moveleft":case"moveright":case"delete":case"duplicate":case"hide":case"stealth":case"show":case"groupsseparate":case"groupsvisible":case"groupsnone":break;default:return}moduleId&&(e.preventDefault(),"delete"===action?confirmDeleteModule(moduleElement,(function(){editModule(moduleElement,moduleId,actionItem)})):editModule(moduleElement,moduleId,actionItem))}})),$("body").on("click keypress",SELECTOR.SECTIONLI+" "+SELECTOR.SECTIONACTIONMENU+"[data-sectionid] a[data-action]",(function(e){if("keypress"===e.type&&13!==e.keyCode)return;var actionItem=$(this),sectionElement=actionItem.closest(SELECTOR.SECTIONLI),sectionId=actionItem.closest(SELECTOR.SECTIONACTIONMENU).attr("data-sectionid");if("permalink"===actionItem.attr("data-action"))return e.preventDefault(),void ModalCopyToClipboard.create({text:actionItem.attr("href")},str.get_string("sectionlink","course"));let isExecuted=!0;var message,onconfirm;actionItem.attr("data-confirm")?(message=actionItem.attr("data-confirm"),onconfirm=function(){isExecuted=editSection(sectionElement,sectionId,actionItem,courseformat)},str.get_strings([{key:"confirm"},{key:"yes"},{key:"no"}]).done((function(s){notification.confirm(s[0],message,s[1],s[2],onconfirm)}))):isExecuted=editSection(sectionElement,sectionId,actionItem,courseformat),isExecuted&&e.preventDefault()})),$("body").on("updated","".concat(SELECTOR.SECTIONLI," ").concat(SELECTOR.SECTIONITEM," [data-inplaceeditable]"),(function(e){if(e.ajaxreturn&&e.ajaxreturn.itemid){void 0!==courseeditor.state.section.get(e.ajaxreturn.itemid)&&courseeditor.dispatch("sectionState",[e.ajaxreturn.itemid])}})),$("body").on("updated","".concat(SELECTOR.ACTIVITYLI," [data-inplaceeditable]"),(function(e){e.ajaxreturn&&e.ajaxreturn.itemid&&courseeditor.dispatch("cmState",[e.ajaxreturn.itemid])})),courseeditor.supportComponents&&componentActions.includes("addSection")||str.get_string("numberweeks").done((function(strNumberSections){var trigger=$(SELECTOR.ADDSECTIONS),modalTitle=trigger.attr("data-add-sections"),newSections=trigger.attr("data-new-sections"),modalBody=$('
');modalBody.find("label").html(strNumberSections),ModalFactory.create({title:modalTitle,type:ModalFactory.types.SAVE_CANCEL,body:modalBody.html()},trigger).done((function(modal){var numSections=$(modal.getBody()).find("#add_section_numsections"),addSections=function(){""+parseInt(numSections.val())===numSections.val()&&parseInt(numSections.val())>=1&&(document.location=trigger.attr("href")+"&numsections="+parseInt(numSections.val()))};modal.setSaveButtonText(modalTitle),modal.getRoot().on(ModalEvents.shown,(function(){numSections.focus().select().on("keydown",(function(e){e.keyCode===KeyCodes.enter&&addSections()}))})),modal.getRoot().on(ModalEvents.save,(function(e){e.preventDefault(),addSections()}))}))}))},replaceSectionActionItem:function(sectionelement,selector,image,stringname,stringcomponent,newaction){log.debug("replaceSectionActionItem() is deprecated and will be removed.");var actionitem=sectionelement.find(SELECTOR.SECTIONACTIONMENU+" "+selector);replaceActionItem(actionitem,image,stringname,stringcomponent,newaction)},refreshModule:refreshModule,refreshSection:function(element,sectionid,sectionreturn){void 0===sectionreturn&&(sectionreturn=courseeditor.sectionReturn);const sectionElement=$(element),promises=ajax.call([{methodname:"core_course_edit_section",args:{id:sectionid,action:"refresh",sectionreturn:sectionreturn}}],!0);var spinner=addSectionSpinner(sectionElement);return new Promise(((resolve,reject)=>{$.when.apply($,promises).done((dataencoded=>{removeSpinner(sectionElement,spinner);const data=$.parseJSON(dataencoded),newSectionElement=$(data.content);sectionElement.replaceWith(newSectionElement),$("".concat(SELECTOR.SECTIONLI,"#").concat(sectionid," ").concat(SELECTOR.ACTIVITYLI)).each(((index,activity)=>{initActionMenu(activity.data("id"))}));dispatchEvent(CourseEvents.sectionRefreshed,{ajaxreturn:data,action:"refresh",newSectionElement:newSectionElement.get(0)},newSectionElement).defaultPrevented||defaultEditSectionHandler(newSectionElement,$(SELECTOR.SECTIONLI+"#"+sectionid),data,formatname,sectionid),resolve(data)})).fail((ex=>{dispatchEvent("coursesectionrefreshfailed",{exception:ex,action:"refresh"},sectionElement).defaultPrevented||notification.exception(ex),reject()}))}))}}})); +define("core_course/actions",["jquery","core/ajax","core/templates","core/notification","core/str","core/url","core/yui","core/modal_copy_to_clipboard","core/modal_factory","core/modal_events","core/key_codes","core/log","core_courseformat/courseeditor","core/event_dispatcher","core_course/events"],(function($,ajax,templates,notification,str,url,Y,ModalCopyToClipboard,ModalFactory,ModalEvents,KeyCodes,log,editor,EventDispatcher,CourseEvents){const componentActions=["moveSection","moveCm","addSection","deleteSection","cmDelete","cmDuplicate","sectionHide","sectionShow","cmHide","cmShow","cmStealth","sectionHighlight","sectionUnhighlight","cmMoveRight","cmMoveLeft","cmNoGroups","cmVisibleGroups","cmSeparateGroups"],courseeditor=editor.getCurrentCourseEditor();let formatname;var CSS_EDITINPROGRESS="editinprogress",CSS_EDITINGMOVE="editing_move",SELECTOR={ACTIVITYLI:"li.activity",ACTIONAREA:".actions",ACTIVITYACTION:"a.cm-edit-action",MENU:".moodle-actionmenu[data-enhance=moodle-core-actionmenu]",TOGGLE:".toggle-display,.dropdown-toggle",SECTIONLI:"li.section",SECTIONACTIONMENU:".section_action_menu",SECTIONITEM:'[data-for="section_title"]',ADDSECTIONS:".changenumsections [data-add-sections]",SECTIONBADGES:'[data-region="sectionbadges"]'};Y.use("moodle-course-coursebase",(function(){var courseformatselector=M.course.format.get_section_selector();courseformatselector&&(SELECTOR.SECTIONLI=courseformatselector)}));const dispatchEvent=function(eventName,detail,container,options){return container instanceof Element||void 0===container.get||(container=container.get(0)),EventDispatcher.dispatchEvent(eventName,detail,container,options)};var getModuleId=function(element){const item=element.get(0);if(item.dataset.id)return item.dataset.id;let id;return Y.use("moodle-course-util",(function(Y){id=Y.Moodle.core_course.util.cm.getId(Y.Node(item))})),id},addActivitySpinner=function(activity){activity.addClass(CSS_EDITINPROGRESS);var actionarea=activity.find(SELECTOR.ACTIONAREA).get(0);if(actionarea){var spinner=M.util.add_spinner(Y,Y.Node(actionarea));return spinner.show(),void 0!==activity.data("id")&&courseeditor.dispatch("cmLock",[activity.data("id")],!0),spinner}return null},addSectionSpinner=function(sectionelement){sectionelement.addClass(CSS_EDITINPROGRESS);var actionarea=sectionelement.find(SELECTOR.SECTIONACTIONMENU).get(0);if(actionarea){var spinner=M.util.add_spinner(Y,Y.Node(actionarea));return spinner.show(),void 0!==sectionelement.data("id")&&courseeditor.dispatch("sectionLock",[sectionelement.data("id")],!0),spinner}return null},addSectionLightbox=function(sectionelement){const item=sectionelement.get(0);var lightbox=M.util.add_lightbox(Y,Y.Node(item));return"section"==item.dataset.for&&item.dataset.id&&(courseeditor.dispatch("sectionLock",[item.dataset.id],!0),lightbox.setAttribute("data-state","section"),lightbox.setAttribute("data-state-id",item.dataset.id)),lightbox.show(),lightbox},removeSpinner=function(element,spinner,delay){window.setTimeout((function(){if(element.removeClass(CSS_EDITINPROGRESS),spinner&&spinner.hide(),void 0!==element.data("id")){const mutation="section"===element.data("for")?"sectionLock":"cmLock";courseeditor.dispatch(mutation,[element.data("id")],!1)}}),delay)},removeLightbox=function(lightbox,delay){lightbox&&window.setTimeout((function(){lightbox.hide(),lightbox.getAttribute("data-state")&&courseeditor.dispatch("".concat(lightbox.getAttribute("data-state"),"Lock"),[lightbox.getAttribute("data-state-id")],!1)}),delay)},initActionMenu=function(elementid){Y.use("moodle-course-coursebase",(function(){M.course.coursebase.invoke_function("setup_for_resource","#"+elementid)})),M.core.actionmenu&&M.core.actionmenu.newDOMNode&&M.core.actionmenu.newDOMNode(Y.one("#"+elementid))},editModule=function(moduleElement,cmid,target){var lightbox,action=target.attr("data-action"),spinner=addActivitySpinner(moduleElement),promises=ajax.call([{methodname:"core_course_edit_module",args:{id:cmid,action:action,sectionreturn:target.attr("data-sectionreturn")?target.attr("data-sectionreturn"):0}}],!0);"duplicate"===action&&(lightbox=addSectionLightbox(target.closest(SELECTOR.SECTIONLI))),$.when.apply($,promises).done((function(data){var mainElement,tabables,isInside,foundElement,elementToFocus=(mainElement=moduleElement,tabables=$("a:visible"),isInside=!1,foundElement=null,tabables.each((function(){if($.contains(mainElement[0],this))isInside=!0;else if(isInside)return foundElement=this,!1;return!0})),foundElement);moduleElement.replaceWith(data);let affectedids=[];$("
"+data+"
").find(SELECTOR.ACTIVITYLI).each((function(index){initActionMenu($(this).attr("id")),0===index&&(!function(elementId,action){var mainelement=$("#"+elementId),selector="[data-action="+action+"]";"groupsseparate"!==action&&"groupsvisible"!==action&&"groupsnone"!==action||(selector="[data-action=groupsseparate],[data-action=groupsvisible],[data-action=groupsnone]"),mainelement.find(selector).is(":visible")?mainelement.find(selector).focus():mainelement.find(SELECTOR.MENU).find(SELECTOR.TOGGLE).focus()}($(this).attr("id"),action),elementToFocus=null),affectedids.push(getModuleId($(this)))})),elementToFocus&&elementToFocus.focus(),removeSpinner(moduleElement,spinner,400),removeLightbox(lightbox,400),moduleElement.trigger($.Event("coursemoduleedited",{ajaxreturn:data,action:action})),courseeditor.dispatch("legacyActivityAction",action,cmid,affectedids)})).fail((function(ex){removeSpinner(moduleElement,spinner),removeLightbox(lightbox);var e=$.Event("coursemoduleeditfailed",{exception:ex,action:action});moduleElement.trigger(e),e.isDefaultPrevented()||notification.exception(ex)}))},refreshModule=function(element,cmid,sectionreturn){void 0===sectionreturn&&(sectionreturn=courseeditor.sectionReturn);const activityElement=$(element);var spinner=addActivitySpinner(activityElement),promises=ajax.call([{methodname:"core_course_get_module",args:{id:cmid,sectionreturn:sectionreturn}}],!0);return new Promise(((resolve,reject)=>{$.when.apply($,promises).done((function(data){removeSpinner(activityElement,spinner,400),replaceActivityHtmlWith(data),resolve(data)})).fail((function(){removeSpinner(activityElement,spinner),reject()}))}))},confirmDeleteModule=function(mainelement,onconfirm){var modtypename=mainelement.attr("class").match(/modtype_([^\s]*)/)[1],modulename=function(element){var name;Y.use("moodle-course-util",(function(Y){name=Y.Moodle.core_course.util.cm.getName(Y.Node(element.get(0)))}));const state=courseeditor.state,cmid=getModuleId(element);var _state$cm$get;return!name&&state&&cmid&&(name=null===(_state$cm$get=state.cm.get(cmid))||void 0===_state$cm$get?void 0:_state$cm$get.name),name}(mainelement);str.get_string("pluginname",modtypename).done((function(pluginname){var plugindata={type:pluginname,name:modulename};str.get_strings([{key:"confirm",component:"core"},{key:null===modulename?"deletechecktype":"deletechecktypename",param:plugindata},{key:"yes"},{key:"no"}]).done((function(s){notification.confirm(s[0],s[1],s[2],s[3],onconfirm)}))}))},replaceActionItem=function(actionitem,image,stringname,stringcomponent,newaction){var stringRequests=[{key:stringname,component:stringcomponent}];return str.get_strings(stringRequests).then((function(strings){return actionitem.find("span.menu-action-text").html(strings[0]),templates.renderPix(image,"core")})).then((function(pixhtml){actionitem.find(".icon").replaceWith(pixhtml),actionitem.attr("data-action",newaction)})).catch(notification.exception)},defaultEditSectionHandler=function(sectionElement,actionItem,data,courseformat,sectionid){var action=actionItem.attr("data-action");if("hide"===action||"show"===action){if("hide"===action?(sectionElement.addClass("hidden"),setSectionBadge(sectionElement[0],"hiddenfromstudents",!0,!1),replaceActionItem(actionItem,"i/show","showfromothers","format_"+courseformat,"show")):(setSectionBadge(sectionElement[0],"hiddenfromstudents",!1,!1),sectionElement.removeClass("hidden"),replaceActionItem(actionItem,"i/hide","hidefromothers","format_"+courseformat,"hide")),void 0!==data.modules)for(var i in data.modules)replaceActivityHtmlWith(data.modules[i]);void 0!==data.section_availability&§ionElement.find(".section_availability").first().replaceWith(data.section_availability);void 0!==courseeditor.state.section.get(sectionid)&&courseeditor.dispatch("sectionState",[sectionid])}else if("setmarker"===action){var oldmarker=$(SELECTOR.SECTIONLI+".current"),oldActionItem=oldmarker.find(SELECTOR.SECTIONACTIONMENU+" a[data-action=removemarker]");oldmarker.removeClass("current"),replaceActionItem(oldActionItem,"i/marker","highlight","core","setmarker"),sectionElement.addClass("current"),replaceActionItem(actionItem,"i/marked","highlightoff","core","removemarker"),courseeditor.dispatch("legacySectionAction",action,sectionid),setSectionBadge(sectionElement[0],"iscurrent",!0,!0)}else"removemarker"===action&&(sectionElement.removeClass("current"),replaceActionItem(actionItem,"i/marker","highlight","core","setmarker"),courseeditor.dispatch("legacySectionAction",action,sectionid),setSectionBadge(sectionElement[0],"iscurrent",!1,!0))};var replaceActivityHtmlWith=function(activityHTML){$("
"+activityHTML+"
").find(SELECTOR.ACTIVITYLI).each((function(){var id=$(this).attr("id");let focusedPath=function(id){const element=document.getElementById(id);if(element&&element.contains(document.activeElement))return element.querySelector(SELECTOR.ACTIONAREA).contains(document.activeElement)?"".concat(SELECTOR.ACTIONAREA,' [tabindex="0"]'):document.activeElement.id?"#".concat(document.activeElement.id):void 0}(id);if($(SELECTOR.ACTIVITYLI+"#"+id).replaceWith(activityHTML),initActionMenu(id),focusedPath){var _newItem$querySelecto;null===(_newItem$querySelecto=document.getElementById(id).querySelector(focusedPath))||void 0===_newItem$querySelecto||_newItem$querySelecto.focus()}}))},editSection=function(sectionElement,sectionid,target,courseformat){var action=target.attr("data-action"),sectionreturn=target.attr("data-sectionreturn")?target.attr("data-sectionreturn"):0;if(courseeditor.supportComponents&&componentActions.includes(action))return!1;var spinner=addSectionSpinner(sectionElement),promises=ajax.call([{methodname:"core_course_edit_section",args:{id:sectionid,action:action,sectionreturn:sectionreturn}}],!0),lightbox=addSectionLightbox(sectionElement);return $.when.apply($,promises).done((function(dataencoded){var data=$.parseJSON(dataencoded);removeSpinner(sectionElement,spinner),removeLightbox(lightbox),sectionElement.find(SELECTOR.SECTIONACTIONMENU).find(SELECTOR.TOGGLE).focus();var e=$.Event("coursesectionedited",{ajaxreturn:data,action:action});sectionElement.trigger(e),e.isDefaultPrevented()||defaultEditSectionHandler(sectionElement,target,data,courseformat,sectionid)})).fail((function(ex){removeSpinner(sectionElement,spinner),removeLightbox(lightbox);var e=$.Event("coursesectioneditfailed",{exception:ex,action:action});sectionElement.trigger(e),e.isDefaultPrevented()||notification.exception(ex)})),!0},setSectionBadge=function(sectionElement,badgetype,add,removeOther){const sectionbadges=sectionElement.querySelector(SELECTOR.SECTIONBADGES);if(!sectionbadges)return;const badge=sectionbadges.querySelector('[data-type="'+badgetype+'"]');badge&&(add?(removeOther&&document.querySelectorAll('[data-type="'+badgetype+'"]').forEach((b=>{b.classList.add("d-none")})),badge.classList.remove("d-none")):badge.classList.add("d-none"))};return Y.use("moodle-course-coursebase",(function(){M.course.coursebase.register_module({set_visibility_resource_ui:function(args){var mainelement=$(args.element.getDOMNode()),cmid=getModuleId(mainelement);if(cmid){var sectionreturn=mainelement.find("."+CSS_EDITINGMOVE).attr("data-sectionreturn");refreshModule(mainelement,cmid,sectionreturn)}},updateMovedCmState:params=>{const cm=courseeditor.state.cm.get(params.cmid);void 0!==cm&&courseeditor.dispatch("sectionState",[cm.sectionid]),courseeditor.dispatch("cmState",[params.cmid])},updateMovedSectionState:()=>{courseeditor.dispatch("courseState")}})})),courseeditor.addMutations({legacyActivityAction:function(statemanager,action,cmid,affectedids){const state=statemanager.state,cm=state.cm.get(cmid);if(void 0===cm)return;const section=state.section.get(cm.sectionid);if(void 0!==section){switch(courseeditor.dispatch("cmLock",[cm.id],!0),statemanager.setReadOnly(!1),cm.locked=!1,action){case"delete":section.cmlist=section.cmlist.reduce(((cmlist,current)=>(current!=cmid&&cmlist.push(current),cmlist)),[]),state.cm.delete(cmid);break;case"hide":case"show":case"duplicate":courseeditor.dispatch("cmState",affectedids)}statemanager.setReadOnly(!0)}},legacySectionAction:function(statemanager,action,sectionid){const state=statemanager.state,section=state.section.get(sectionid);if(void 0!==section){switch(statemanager.setReadOnly(!1),section.locked=!0,statemanager.setReadOnly(!0),statemanager.setReadOnly(!1),section.locked=!1,action){case"setmarker":state.section.forEach((current=>{current.id!=sectionid&&(current.current=!1)})),section.current=!0;break;case"removemarker":section.current=!1}statemanager.setReadOnly(!0)}}}),{initCoursePage:function(courseformat){formatname=courseformat,$("body").on("click keypress",SELECTOR.ACTIVITYLI+" "+SELECTOR.ACTIVITYACTION+"[data-action]",(function(e){if("keypress"!==e.type||13===e.keyCode){var actionItem=$(this),moduleElement=actionItem.closest(SELECTOR.ACTIVITYLI),action=actionItem.attr("data-action"),moduleId=getModuleId(moduleElement);switch(action){case"moveleft":case"moveright":case"delete":case"duplicate":case"hide":case"stealth":case"show":case"groupsseparate":case"groupsvisible":case"groupsnone":break;default:return}moduleId&&(e.preventDefault(),"delete"===action?confirmDeleteModule(moduleElement,(function(){editModule(moduleElement,moduleId,actionItem)})):editModule(moduleElement,moduleId,actionItem))}})),$("body").on("click keypress",SELECTOR.SECTIONLI+" "+SELECTOR.SECTIONACTIONMENU+"[data-sectionid] a[data-action]",(function(e){if("keypress"===e.type&&13!==e.keyCode)return;var actionItem=$(this),sectionElement=actionItem.closest(SELECTOR.SECTIONLI),sectionId=actionItem.closest(SELECTOR.SECTIONACTIONMENU).attr("data-sectionid");if("permalink"===actionItem.attr("data-action"))return e.preventDefault(),void ModalCopyToClipboard.create({text:actionItem.attr("href")},str.get_string("sectionlink","course"));let isExecuted=!0;var message,onconfirm;actionItem.attr("data-confirm")?(message=actionItem.attr("data-confirm"),onconfirm=function(){isExecuted=editSection(sectionElement,sectionId,actionItem,courseformat)},str.get_strings([{key:"confirm"},{key:"yes"},{key:"no"}]).done((function(s){notification.confirm(s[0],message,s[1],s[2],onconfirm)}))):isExecuted=editSection(sectionElement,sectionId,actionItem,courseformat),isExecuted&&e.preventDefault()})),$("body").on("updated","".concat(SELECTOR.SECTIONLI," ").concat(SELECTOR.SECTIONITEM," [data-inplaceeditable]"),(function(e){if(e.ajaxreturn&&e.ajaxreturn.itemid){void 0!==courseeditor.state.section.get(e.ajaxreturn.itemid)&&courseeditor.dispatch("sectionState",[e.ajaxreturn.itemid])}})),$("body").on("updated","".concat(SELECTOR.ACTIVITYLI," [data-inplaceeditable]"),(function(e){e.ajaxreturn&&e.ajaxreturn.itemid&&courseeditor.dispatch("cmState",[e.ajaxreturn.itemid])})),courseeditor.supportComponents&&componentActions.includes("addSection")||str.get_string("numberweeks").done((function(strNumberSections){var trigger=$(SELECTOR.ADDSECTIONS),modalTitle=trigger.attr("data-add-sections"),newSections=trigger.attr("data-new-sections"),modalBody=$('
');modalBody.find("label").html(strNumberSections),ModalFactory.create({title:modalTitle,type:ModalFactory.types.SAVE_CANCEL,body:modalBody.html()},trigger).done((function(modal){var numSections=$(modal.getBody()).find("#add_section_numsections"),addSections=function(){""+parseInt(numSections.val())===numSections.val()&&parseInt(numSections.val())>=1&&(document.location=trigger.attr("href")+"&numsections="+parseInt(numSections.val()))};modal.setSaveButtonText(modalTitle),modal.getRoot().on(ModalEvents.shown,(function(){numSections.focus().select().on("keydown",(function(e){e.keyCode===KeyCodes.enter&&addSections()}))})),modal.getRoot().on(ModalEvents.save,(function(e){e.preventDefault(),addSections()}))}))}))},replaceSectionActionItem:function(sectionelement,selector,image,stringname,stringcomponent,newaction){log.debug("replaceSectionActionItem() is deprecated and will be removed.");var actionitem=sectionelement.find(SELECTOR.SECTIONACTIONMENU+" "+selector);replaceActionItem(actionitem,image,stringname,stringcomponent,newaction)},refreshModule:refreshModule,refreshSection:function(element,sectionid,sectionreturn){void 0===sectionreturn&&(sectionreturn=courseeditor.sectionReturn);const sectionElement=$(element),promises=ajax.call([{methodname:"core_course_edit_section",args:{id:sectionid,action:"refresh",sectionreturn:sectionreturn}}],!0);var spinner=addSectionSpinner(sectionElement);return new Promise(((resolve,reject)=>{$.when.apply($,promises).done((dataencoded=>{removeSpinner(sectionElement,spinner);const data=$.parseJSON(dataencoded),newSectionElement=$(data.content);sectionElement.replaceWith(newSectionElement),$("".concat(SELECTOR.SECTIONLI,"#").concat(sectionid," ").concat(SELECTOR.ACTIVITYLI)).each(((index,activity)=>{initActionMenu(activity.data("id"))}));dispatchEvent(CourseEvents.sectionRefreshed,{ajaxreturn:data,action:"refresh",newSectionElement:newSectionElement.get(0)},newSectionElement).defaultPrevented||defaultEditSectionHandler(newSectionElement,$(SELECTOR.SECTIONLI+"#"+sectionid),data,formatname,sectionid),resolve(data)})).fail((ex=>{dispatchEvent("coursesectionrefreshfailed",{exception:ex,action:"refresh"},sectionElement).defaultPrevented||notification.exception(ex),reject()}))}))}}})); //# sourceMappingURL=actions.min.js.map \ No newline at end of file diff --git a/course/amd/build/actions.min.js.map b/course/amd/build/actions.min.js.map index f90729d348a..f4fb0108058 100644 --- a/course/amd/build/actions.min.js.map +++ b/course/amd/build/actions.min.js.map @@ -1 +1 @@ -{"version":3,"file":"actions.min.js","sources":["../src/actions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Various actions on modules and sections in the editing mode - hiding, duplicating, deleting, etc.\n *\n * @module core_course/actions\n * @copyright 2016 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.3\n */\ndefine(\n [\n 'jquery',\n 'core/ajax',\n 'core/templates',\n 'core/notification',\n 'core/str',\n 'core/url',\n 'core/yui',\n 'core/modal_copy_to_clipboard',\n 'core/modal_factory',\n 'core/modal_events',\n 'core/key_codes',\n 'core/log',\n 'core_courseformat/courseeditor',\n 'core/event_dispatcher',\n 'core_course/events'\n ],\n function(\n $,\n ajax,\n templates,\n notification,\n str,\n url,\n Y,\n ModalCopyToClipboard,\n ModalFactory,\n ModalEvents,\n KeyCodes,\n log,\n editor,\n EventDispatcher,\n CourseEvents\n ) {\n\n // Eventually, core_courseformat/local/content/actions will handle all actions for\n // component compatible formats and the default actions.js won't be necessary anymore.\n // Meanwhile, we filter the migrated actions.\n const componentActions = [\n 'moveSection', 'moveCm', 'addSection', 'deleteSection', 'cmDelete', 'cmDuplicate', 'sectionHide', 'sectionShow',\n 'cmHide', 'cmShow', 'cmStealth', 'sectionHighlight', 'sectionUnhighlight', 'cmMoveRight', 'cmMoveLeft',\n ];\n\n // The course reactive instance.\n const courseeditor = editor.getCurrentCourseEditor();\n\n // The current course format name (loaded on init).\n let formatname;\n\n var CSS = {\n EDITINPROGRESS: 'editinprogress',\n SECTIONDRAGGABLE: 'sectiondraggable',\n EDITINGMOVE: 'editing_move'\n };\n var SELECTOR = {\n ACTIVITYLI: 'li.activity',\n ACTIONAREA: '.actions',\n ACTIVITYACTION: 'a.cm-edit-action',\n MENU: '.moodle-actionmenu[data-enhance=moodle-core-actionmenu]',\n TOGGLE: '.toggle-display,.dropdown-toggle',\n SECTIONLI: 'li.section',\n SECTIONACTIONMENU: '.section_action_menu',\n SECTIONITEM: '[data-for=\"section_title\"]',\n ADDSECTIONS: '.changenumsections [data-add-sections]',\n SECTIONBADGES: '[data-region=\"sectionbadges\"]',\n };\n\n Y.use('moodle-course-coursebase', function() {\n var courseformatselector = M.course.format.get_section_selector();\n if (courseformatselector) {\n SELECTOR.SECTIONLI = courseformatselector;\n }\n });\n\n /**\n * Dispatch event wrapper.\n *\n * Old jQuery events will be replaced by native events gradually.\n *\n * @method dispatchEvent\n * @param {String} eventName The name of the event\n * @param {Object} detail Any additional details to pass into the eveent\n * @param {Node|HTMLElement} container The point at which to dispatch the event\n * @param {Object} options\n * @param {Boolean} options.bubbles Whether to bubble up the DOM\n * @param {Boolean} options.cancelable Whether preventDefault() can be called\n * @param {Boolean} options.composed Whether the event can bubble across the ShadowDOM boundary\n * @returns {CustomEvent}\n */\n const dispatchEvent = function(eventName, detail, container, options) {\n // Most actions still uses jQuery node instead of regular HTMLElement.\n if (!(container instanceof Element) && container.get !== undefined) {\n container = container.get(0);\n }\n return EventDispatcher.dispatchEvent(eventName, detail, container, options);\n };\n\n /**\n * Wrapper for Y.Moodle.core_course.util.cm.getId\n *\n * @param {JQuery} element\n * @returns {Integer}\n */\n var getModuleId = function(element) {\n // Check if we have a data-id first.\n const item = element.get(0);\n if (item.dataset.id) {\n return item.dataset.id;\n }\n // Use YUI way if data-id is not present.\n let id;\n Y.use('moodle-course-util', function(Y) {\n id = Y.Moodle.core_course.util.cm.getId(Y.Node(item));\n });\n return id;\n };\n\n /**\n * Wrapper for Y.Moodle.core_course.util.cm.getName\n *\n * @param {JQuery} element\n * @returns {String}\n */\n var getModuleName = function(element) {\n var name;\n Y.use('moodle-course-util', function(Y) {\n name = Y.Moodle.core_course.util.cm.getName(Y.Node(element.get(0)));\n });\n // Check if we have the name in the course state.\n const state = courseeditor.state;\n const cmid = getModuleId(element);\n if (!name && state && cmid) {\n name = state.cm.get(cmid)?.name;\n }\n return name;\n };\n\n /**\n * Wrapper for M.util.add_spinner for an activity\n *\n * @param {JQuery} activity\n * @returns {Node}\n */\n var addActivitySpinner = function(activity) {\n activity.addClass(CSS.EDITINPROGRESS);\n var actionarea = activity.find(SELECTOR.ACTIONAREA).get(0);\n if (actionarea) {\n var spinner = M.util.add_spinner(Y, Y.Node(actionarea));\n spinner.show();\n // Lock the activity state element.\n if (activity.data('id') !== undefined) {\n courseeditor.dispatch('cmLock', [activity.data('id')], true);\n }\n return spinner;\n }\n return null;\n };\n\n /**\n * Wrapper for M.util.add_spinner for a section\n *\n * @param {JQuery} sectionelement\n * @returns {Node}\n */\n var addSectionSpinner = function(sectionelement) {\n sectionelement.addClass(CSS.EDITINPROGRESS);\n var actionarea = sectionelement.find(SELECTOR.SECTIONACTIONMENU).get(0);\n if (actionarea) {\n var spinner = M.util.add_spinner(Y, Y.Node(actionarea));\n spinner.show();\n // Lock the section state element.\n if (sectionelement.data('id') !== undefined) {\n courseeditor.dispatch('sectionLock', [sectionelement.data('id')], true);\n }\n return spinner;\n }\n return null;\n };\n\n /**\n * Wrapper for M.util.add_lightbox\n *\n * @param {JQuery} sectionelement\n * @returns {Node}\n */\n var addSectionLightbox = function(sectionelement) {\n const item = sectionelement.get(0);\n var lightbox = M.util.add_lightbox(Y, Y.Node(item));\n if (item.dataset.for == 'section' && item.dataset.id) {\n courseeditor.dispatch('sectionLock', [item.dataset.id], true);\n lightbox.setAttribute('data-state', 'section');\n lightbox.setAttribute('data-state-id', item.dataset.id);\n }\n lightbox.show();\n return lightbox;\n };\n\n /**\n * Removes the spinner element\n *\n * @param {JQuery} element\n * @param {Node} spinner\n * @param {Number} delay\n */\n var removeSpinner = function(element, spinner, delay) {\n window.setTimeout(function() {\n element.removeClass(CSS.EDITINPROGRESS);\n if (spinner) {\n spinner.hide();\n }\n // Unlock the state element.\n if (element.data('id') !== undefined) {\n const mutation = (element.data('for') === 'section') ? 'sectionLock' : 'cmLock';\n courseeditor.dispatch(mutation, [element.data('id')], false);\n }\n }, delay);\n };\n\n /**\n * Removes the lightbox element\n *\n * @param {Node} lightbox lighbox YUI element returned by addSectionLightbox\n * @param {Number} delay\n */\n var removeLightbox = function(lightbox, delay) {\n if (lightbox) {\n window.setTimeout(function() {\n lightbox.hide();\n // Unlock state if necessary.\n if (lightbox.getAttribute('data-state')) {\n courseeditor.dispatch(\n `${lightbox.getAttribute('data-state')}Lock`,\n [lightbox.getAttribute('data-state-id')],\n false\n );\n }\n }, delay);\n }\n };\n\n /**\n * Initialise action menu for the element (section or module)\n *\n * @param {String} elementid CSS id attribute of the element\n */\n var initActionMenu = function(elementid) {\n // Initialise action menu in the new activity.\n Y.use('moodle-course-coursebase', function() {\n M.course.coursebase.invoke_function('setup_for_resource', '#' + elementid);\n });\n if (M.core.actionmenu && M.core.actionmenu.newDOMNode) {\n M.core.actionmenu.newDOMNode(Y.one('#' + elementid));\n }\n };\n\n /**\n * Returns focus to the element that was clicked or \"Edit\" link if element is no longer visible.\n *\n * @param {String} elementId CSS id attribute of the element\n * @param {String} action data-action property of the element that was clicked\n */\n var focusActionItem = function(elementId, action) {\n var mainelement = $('#' + elementId);\n var selector = '[data-action=' + action + ']';\n if (action === 'groupsseparate' || action === 'groupsvisible' || action === 'groupsnone') {\n // New element will have different data-action.\n selector = '[data-action=groupsseparate],[data-action=groupsvisible],[data-action=groupsnone]';\n }\n if (mainelement.find(selector).is(':visible')) {\n mainelement.find(selector).focus();\n } else {\n // Element not visible, focus the \"Edit\" link.\n mainelement.find(SELECTOR.MENU).find(SELECTOR.TOGGLE).focus();\n }\n };\n\n /**\n * Find next after the element\n *\n * @param {JQuery} mainElement element that is about to be deleted\n * @returns {JQuery}\n */\n var findNextFocusable = function(mainElement) {\n var tabables = $(\"a:visible\");\n var isInside = false;\n var foundElement = null;\n tabables.each(function() {\n if ($.contains(mainElement[0], this)) {\n isInside = true;\n } else if (isInside) {\n foundElement = this;\n return false; // Returning false in .each() is equivalent to \"break;\" inside the loop in php.\n }\n return true;\n });\n return foundElement;\n };\n\n /**\n * Performs an action on a module (moving, deleting, duplicating, hiding, etc.)\n *\n * @param {JQuery} moduleElement activity element we perform action on\n * @param {Number} cmid\n * @param {JQuery} target the element (menu item) that was clicked\n */\n var editModule = function(moduleElement, cmid, target) {\n var action = target.attr('data-action');\n var spinner = addActivitySpinner(moduleElement);\n var promises = ajax.call([{\n methodname: 'core_course_edit_module',\n args: {id: cmid,\n action: action,\n sectionreturn: target.attr('data-sectionreturn') ? target.attr('data-sectionreturn') : 0\n }\n }], true);\n\n var lightbox;\n if (action === 'duplicate') {\n lightbox = addSectionLightbox(target.closest(SELECTOR.SECTIONLI));\n }\n $.when.apply($, promises)\n .done(function(data) {\n var elementToFocus = findNextFocusable(moduleElement);\n moduleElement.replaceWith(data);\n let affectedids = [];\n // Initialise action menu for activity(ies) added as a result of this.\n $('
' + data + '
').find(SELECTOR.ACTIVITYLI).each(function(index) {\n initActionMenu($(this).attr('id'));\n if (index === 0) {\n focusActionItem($(this).attr('id'), action);\n elementToFocus = null;\n }\n // Save any activity id in cmids.\n affectedids.push(getModuleId($(this)));\n });\n // In case of activity deletion focus the next focusable element.\n if (elementToFocus) {\n elementToFocus.focus();\n }\n // Remove spinner and lightbox with a delay.\n removeSpinner(moduleElement, spinner, 400);\n removeLightbox(lightbox, 400);\n // Trigger event that can be observed by course formats.\n moduleElement.trigger($.Event('coursemoduleedited', {ajaxreturn: data, action: action}));\n\n // Modify cm state.\n courseeditor.dispatch('legacyActivityAction', action, cmid, affectedids);\n\n }).fail(function(ex) {\n // Remove spinner and lightbox.\n removeSpinner(moduleElement, spinner);\n removeLightbox(lightbox);\n // Trigger event that can be observed by course formats.\n var e = $.Event('coursemoduleeditfailed', {exception: ex, action: action});\n moduleElement.trigger(e);\n if (!e.isDefaultPrevented()) {\n notification.exception(ex);\n }\n });\n };\n\n /**\n * Requests html for the module via WS core_course_get_module and updates the module on the course page\n *\n * Used after d&d of the module to another section\n *\n * @param {JQuery|Element} element\n * @param {Number} cmid\n * @param {Number} sectionreturn\n * @return {Promise} the refresh promise\n */\n var refreshModule = function(element, cmid, sectionreturn) {\n\n if (sectionreturn === undefined) {\n sectionreturn = courseeditor.sectionReturn;\n }\n\n const activityElement = $(element);\n var spinner = addActivitySpinner(activityElement);\n var promises = ajax.call([{\n methodname: 'core_course_get_module',\n args: {id: cmid, sectionreturn: sectionreturn}\n }], true);\n\n return new Promise((resolve, reject) => {\n $.when.apply($, promises)\n .done(function(data) {\n removeSpinner(activityElement, spinner, 400);\n replaceActivityHtmlWith(data);\n resolve(data);\n }).fail(function() {\n removeSpinner(activityElement, spinner);\n reject();\n });\n });\n };\n\n /**\n * Requests html for the section via WS core_course_edit_section and updates the section on the course page\n *\n * @param {JQuery|Element} element\n * @param {Number} sectionid\n * @param {Number} sectionreturn\n * @return {Promise} the refresh promise\n */\n var refreshSection = function(element, sectionid, sectionreturn) {\n\n if (sectionreturn === undefined) {\n sectionreturn = courseeditor.sectionReturn;\n }\n\n const sectionElement = $(element);\n const action = 'refresh';\n const promises = ajax.call([{\n methodname: 'core_course_edit_section',\n args: {id: sectionid, action, sectionreturn},\n }], true);\n\n var spinner = addSectionSpinner(sectionElement);\n return new Promise((resolve, reject) => {\n $.when.apply($, promises)\n .done(dataencoded => {\n\n removeSpinner(sectionElement, spinner);\n const data = $.parseJSON(dataencoded);\n\n const newSectionElement = $(data.content);\n sectionElement.replaceWith(newSectionElement);\n\n // Init modules menus.\n $(`${SELECTOR.SECTIONLI}#${sectionid} ${SELECTOR.ACTIVITYLI}`).each(\n (index, activity) => {\n initActionMenu(activity.data('id'));\n }\n );\n\n // Trigger event that can be observed by course formats.\n const event = dispatchEvent(\n CourseEvents.sectionRefreshed,\n {\n ajaxreturn: data,\n action: action,\n newSectionElement: newSectionElement.get(0),\n },\n newSectionElement\n );\n\n if (!event.defaultPrevented) {\n defaultEditSectionHandler(\n newSectionElement, $(SELECTOR.SECTIONLI + '#' + sectionid),\n data,\n formatname,\n sectionid\n );\n }\n resolve(data);\n }).fail(ex => {\n // Trigger event that can be observed by course formats.\n const event = dispatchEvent(\n 'coursesectionrefreshfailed',\n {exception: ex, action: action},\n sectionElement\n );\n if (!event.defaultPrevented) {\n notification.exception(ex);\n }\n reject();\n });\n });\n };\n\n /**\n * Displays the delete confirmation to delete a module\n *\n * @param {JQuery} mainelement activity element we perform action on\n * @param {function} onconfirm function to execute on confirm\n */\n var confirmDeleteModule = function(mainelement, onconfirm) {\n var modtypename = mainelement.attr('class').match(/modtype_([^\\s]*)/)[1];\n var modulename = getModuleName(mainelement);\n\n str.get_string('pluginname', modtypename).done(function(pluginname) {\n var plugindata = {\n type: pluginname,\n name: modulename\n };\n str.get_strings([\n {key: 'confirm', component: 'core'},\n {key: modulename === null ? 'deletechecktype' : 'deletechecktypename', param: plugindata},\n {key: 'yes'},\n {key: 'no'}\n ]).done(function(s) {\n notification.confirm(s[0], s[1], s[2], s[3], onconfirm);\n }\n );\n });\n };\n\n /**\n * Displays the delete confirmation to delete a section\n *\n * @param {String} message confirmation message\n * @param {function} onconfirm function to execute on confirm\n */\n var confirmEditSection = function(message, onconfirm) {\n str.get_strings([\n {key: 'confirm'}, // TODO link text\n {key: 'yes'},\n {key: 'no'}\n ]).done(function(s) {\n notification.confirm(s[0], message, s[1], s[2], onconfirm);\n }\n );\n };\n\n /**\n * Replaces an action menu item with another one (for example Show->Hide, Set marker->Remove marker)\n *\n * @param {JQuery} actionitem\n * @param {String} image new image name (\"i/show\", \"i/hide\", etc.)\n * @param {String} stringname new string for the action menu item\n * @param {String} stringcomponent\n * @param {String} newaction new value for data-action attribute of the link\n * @return {Promise} promise which is resolved when the replacement has completed\n */\n var replaceActionItem = function(actionitem, image, stringname,\n stringcomponent, newaction) {\n\n var stringRequests = [{key: stringname, component: stringcomponent}];\n // Do not provide an icon with duplicate, different text to the menu item.\n\n return str.get_strings(stringRequests).then(function(strings) {\n actionitem.find('span.menu-action-text').html(strings[0]);\n\n return templates.renderPix(image, 'core');\n }).then(function(pixhtml) {\n actionitem.find('.icon').replaceWith(pixhtml);\n actionitem.attr('data-action', newaction);\n return;\n }).catch(notification.exception);\n };\n\n /**\n * Default post-processing for section AJAX edit actions.\n *\n * This can be overridden in course formats by listening to event coursesectionedited:\n *\n * $('body').on('coursesectionedited', 'li.section', function(e) {\n * var action = e.action,\n * sectionElement = $(e.target),\n * data = e.ajaxreturn;\n * // ... Do some processing here.\n * e.preventDefault(); // Prevent default handler.\n * });\n *\n * @param {JQuery} sectionElement\n * @param {JQuery} actionItem\n * @param {Object} data\n * @param {String} courseformat\n * @param {Number} sectionid\n */\n var defaultEditSectionHandler = function(sectionElement, actionItem, data, courseformat, sectionid) {\n var action = actionItem.attr('data-action');\n if (action === 'hide' || action === 'show') {\n if (action === 'hide') {\n sectionElement.addClass('hidden');\n setSectionBadge(sectionElement[0], 'hiddenfromstudents', true, false);\n replaceActionItem(actionItem, 'i/show',\n 'showfromothers', 'format_' + courseformat, 'show');\n } else {\n setSectionBadge(sectionElement[0], 'hiddenfromstudents', false, false);\n sectionElement.removeClass('hidden');\n replaceActionItem(actionItem, 'i/hide',\n 'hidefromothers', 'format_' + courseformat, 'hide');\n }\n // Replace the modules with new html (that indicates that they are now hidden or not hidden).\n if (data.modules !== undefined) {\n for (var i in data.modules) {\n replaceActivityHtmlWith(data.modules[i]);\n }\n }\n // Replace the section availability information.\n if (data.section_availability !== undefined) {\n sectionElement.find('.section_availability').first().replaceWith(data.section_availability);\n }\n // Modify course state.\n const section = courseeditor.state.section.get(sectionid);\n if (section !== undefined) {\n courseeditor.dispatch('sectionState', [sectionid]);\n }\n } else if (action === 'setmarker') {\n var oldmarker = $(SELECTOR.SECTIONLI + '.current'),\n oldActionItem = oldmarker.find(SELECTOR.SECTIONACTIONMENU + ' ' + 'a[data-action=removemarker]');\n oldmarker.removeClass('current');\n replaceActionItem(oldActionItem, 'i/marker',\n 'highlight', 'core', 'setmarker');\n sectionElement.addClass('current');\n replaceActionItem(actionItem, 'i/marked',\n 'highlightoff', 'core', 'removemarker');\n courseeditor.dispatch('legacySectionAction', action, sectionid);\n setSectionBadge(sectionElement[0], 'iscurrent', true, true);\n } else if (action === 'removemarker') {\n sectionElement.removeClass('current');\n replaceActionItem(actionItem, 'i/marker',\n 'highlight', 'core', 'setmarker');\n courseeditor.dispatch('legacySectionAction', action, sectionid);\n setSectionBadge(sectionElement[0], 'iscurrent', false, true);\n }\n };\n\n /**\n * Get the focused element path in an activity if any.\n *\n * This method is used to restore focus when the activity HTML is refreshed.\n * Only the main course editor elements can be refocused as they are always present\n * even if the activity content changes.\n *\n * @param {String} id the element id the activity element\n * @return {String|undefined} the inner path of the focused element or undefined\n */\n const getActivityFocusedElement = function(id) {\n const element = document.getElementById(id);\n if (!element || !element.contains(document.activeElement)) {\n return undefined;\n }\n // Check if the actions menu toggler is focused.\n if (element.querySelector(SELECTOR.ACTIONAREA).contains(document.activeElement)) {\n return `${SELECTOR.ACTIONAREA} [tabindex=\"0\"]`;\n }\n // Return the current element id if any.\n if (document.activeElement.id) {\n return `#${document.activeElement.id}`;\n }\n return undefined;\n };\n\n /**\n * Replaces the course module with the new html (used to update module after it was edited or its visibility was changed).\n *\n * @param {String} activityHTML\n */\n var replaceActivityHtmlWith = function(activityHTML) {\n $('
' + activityHTML + '
').find(SELECTOR.ACTIVITYLI).each(function() {\n // Extract id from the new activity html.\n var id = $(this).attr('id');\n // Check if the current focused element is inside the activity.\n let focusedPath = getActivityFocusedElement(id);\n // Find the existing element with the same id and replace its contents with new html.\n $(SELECTOR.ACTIVITYLI + '#' + id).replaceWith(activityHTML);\n // Initialise action menu.\n initActionMenu(id);\n // Re-focus the previous elements.\n if (focusedPath) {\n const newItem = document.getElementById(id);\n newItem.querySelector(focusedPath)?.focus();\n }\n\n });\n };\n\n /**\n * Performs an action on a module (moving, deleting, duplicating, hiding, etc.)\n *\n * @param {JQuery} sectionElement section element we perform action on\n * @param {Nunmber} sectionid\n * @param {JQuery} target the element (menu item) that was clicked\n * @param {String} courseformat\n * @return {boolean} true the action call is sent to the server or false if it is ignored.\n */\n var editSection = function(sectionElement, sectionid, target, courseformat) {\n var action = target.attr('data-action'),\n sectionreturn = target.attr('data-sectionreturn') ? target.attr('data-sectionreturn') : 0;\n\n // Filter direct component handled actions.\n if (courseeditor.supportComponents && componentActions.includes(action)) {\n return false;\n }\n\n var spinner = addSectionSpinner(sectionElement);\n var promises = ajax.call([{\n methodname: 'core_course_edit_section',\n args: {id: sectionid, action: action, sectionreturn: sectionreturn}\n }], true);\n\n var lightbox = addSectionLightbox(sectionElement);\n $.when.apply($, promises)\n .done(function(dataencoded) {\n var data = $.parseJSON(dataencoded);\n removeSpinner(sectionElement, spinner);\n removeLightbox(lightbox);\n sectionElement.find(SELECTOR.SECTIONACTIONMENU).find(SELECTOR.TOGGLE).focus();\n // Trigger event that can be observed by course formats.\n var e = $.Event('coursesectionedited', {ajaxreturn: data, action: action});\n sectionElement.trigger(e);\n if (!e.isDefaultPrevented()) {\n defaultEditSectionHandler(sectionElement, target, data, courseformat, sectionid);\n }\n }).fail(function(ex) {\n // Remove spinner and lightbox.\n removeSpinner(sectionElement, spinner);\n removeLightbox(lightbox);\n // Trigger event that can be observed by course formats.\n var e = $.Event('coursesectioneditfailed', {exception: ex, action: action});\n sectionElement.trigger(e);\n if (!e.isDefaultPrevented()) {\n notification.exception(ex);\n }\n });\n return true;\n };\n\n /**\n * Sets the section badge in the section header.\n *\n * @param {JQuery} sectionElement section element we perform action on\n * @param {String} badgetype the type of badge this is for\n * @param {bool} add true to add, false to remove\n * @param {boolean} removeOther in case of adding a badge, whether to remove all other.\n */\n var setSectionBadge = function(sectionElement, badgetype, add, removeOther) {\n const sectionbadges = sectionElement.querySelector(SELECTOR.SECTIONBADGES);\n if (!sectionbadges) {\n return;\n }\n const badge = sectionbadges.querySelector('[data-type=\"' + badgetype + '\"]');\n if (!badge) {\n return;\n }\n if (add) {\n if (removeOther) {\n document.querySelectorAll('[data-type=\"' + badgetype + '\"]').forEach((b) => {\n b.classList.add('d-none');\n });\n }\n badge.classList.remove('d-none');\n } else {\n badge.classList.add('d-none');\n }\n };\n\n // Register a function to be executed after D&D of an activity.\n Y.use('moodle-course-coursebase', function() {\n M.course.coursebase.register_module({\n // Ignore camelcase eslint rule for the next line because it is an expected name of the callback.\n // eslint-disable-next-line camelcase\n set_visibility_resource_ui: function(args) {\n var mainelement = $(args.element.getDOMNode());\n var cmid = getModuleId(mainelement);\n if (cmid) {\n var sectionreturn = mainelement.find('.' + CSS.EDITINGMOVE).attr('data-sectionreturn');\n refreshModule(mainelement, cmid, sectionreturn);\n }\n },\n /**\n * Update the course state when some cm is moved via YUI.\n * @param {*} params\n */\n updateMovedCmState: (params) => {\n const state = courseeditor.state;\n\n // Update old section.\n const cm = state.cm.get(params.cmid);\n if (cm !== undefined) {\n courseeditor.dispatch('sectionState', [cm.sectionid]);\n }\n // Update cm state.\n courseeditor.dispatch('cmState', [params.cmid]);\n },\n /**\n * Update the course state when some section is moved via YUI.\n */\n updateMovedSectionState: () => {\n courseeditor.dispatch('courseState');\n },\n });\n });\n\n // From Moodle 4.0 all edit actions are being re-implemented as state mutation.\n // This means all method from this \"actions\" module will be deprecated when all the course\n // interface is migrated to reactive components.\n // Most legacy actions did not provide enough information to regenarate the course so they\n // use the mutations courseState, sectionState and cmState to get the updated state from\n // the server. However, some activity actions where we can prevent an extra webservice\n // call by implementing an adhoc mutation.\n courseeditor.addMutations({\n /**\n * Compatibility function to update Moodle 4.0 course state using legacy actions.\n *\n * This method only updates some actions which does not require to use cmState mutation\n * to get updated data form the server.\n *\n * @param {Object} statemanager the current state in read write mode\n * @param {String} action the performed action\n * @param {Number} cmid the affected course module id\n * @param {Array} affectedids all affected cm ids (for duplicate action)\n */\n legacyActivityAction: function(statemanager, action, cmid, affectedids) {\n\n const state = statemanager.state;\n const cm = state.cm.get(cmid);\n if (cm === undefined) {\n return;\n }\n const section = state.section.get(cm.sectionid);\n if (section === undefined) {\n return;\n }\n\n // Send the element is locked.\n courseeditor.dispatch('cmLock', [cm.id], true);\n\n // Now we do the real mutation.\n statemanager.setReadOnly(false);\n\n // This unlocked will take effect when the read only is restored.\n cm.locked = false;\n\n switch (action) {\n case 'delete':\n // Remove from section.\n section.cmlist = section.cmlist.reduce(\n (cmlist, current) => {\n if (current != cmid) {\n cmlist.push(current);\n }\n return cmlist;\n },\n []\n );\n // Delete form list.\n state.cm.delete(cmid);\n break;\n\n case 'hide':\n case 'show':\n case 'duplicate':\n courseeditor.dispatch('cmState', affectedids);\n break;\n }\n statemanager.setReadOnly(true);\n },\n legacySectionAction: function(statemanager, action, sectionid) {\n\n const state = statemanager.state;\n const section = state.section.get(sectionid);\n if (section === undefined) {\n return;\n }\n\n // Send the element is locked. Reactive events are only triggered when the state\n // read only mode is restored. We want to notify the interface the element is\n // locked so we need to do a quick lock operation before performing the rest\n // of the mutation.\n statemanager.setReadOnly(false);\n section.locked = true;\n statemanager.setReadOnly(true);\n\n // Now we do the real mutation.\n statemanager.setReadOnly(false);\n\n // This locked will take effect when the read only is restored.\n section.locked = false;\n\n switch (action) {\n case 'setmarker':\n // Remove previous marker.\n state.section.forEach((current) => {\n if (current.id != sectionid) {\n current.current = false;\n }\n });\n section.current = true;\n break;\n\n case 'removemarker':\n section.current = false;\n break;\n }\n statemanager.setReadOnly(true);\n },\n });\n\n return /** @alias module:core_course/actions */ {\n\n /**\n * Initialises course page\n *\n * @method init\n * @param {String} courseformat name of the current course format (for fetching strings)\n */\n initCoursePage: function(courseformat) {\n\n formatname = courseformat;\n\n // Add a handler for course module actions.\n $('body').on('click keypress', SELECTOR.ACTIVITYLI + ' ' +\n SELECTOR.ACTIVITYACTION + '[data-action]', function(e) {\n if (e.type === 'keypress' && e.keyCode !== 13) {\n return;\n }\n var actionItem = $(this),\n moduleElement = actionItem.closest(SELECTOR.ACTIVITYLI),\n action = actionItem.attr('data-action'),\n moduleId = getModuleId(moduleElement);\n switch (action) {\n case 'moveleft':\n case 'moveright':\n case 'delete':\n case 'duplicate':\n case 'hide':\n case 'stealth':\n case 'show':\n case 'groupsseparate':\n case 'groupsvisible':\n case 'groupsnone':\n break;\n default:\n // Nothing to do here!\n return;\n }\n if (!moduleId) {\n return;\n }\n e.preventDefault();\n if (action === 'delete') {\n // Deleting requires confirmation.\n confirmDeleteModule(moduleElement, function() {\n editModule(moduleElement, moduleId, actionItem);\n });\n } else {\n editModule(moduleElement, moduleId, actionItem);\n }\n });\n\n // Add a handler for section show/hide actions.\n $('body').on('click keypress', SELECTOR.SECTIONLI + ' ' +\n SELECTOR.SECTIONACTIONMENU + '[data-sectionid] ' +\n 'a[data-action]', function(e) {\n if (e.type === 'keypress' && e.keyCode !== 13) {\n return;\n }\n var actionItem = $(this),\n sectionElement = actionItem.closest(SELECTOR.SECTIONLI),\n sectionId = actionItem.closest(SELECTOR.SECTIONACTIONMENU).attr('data-sectionid');\n\n if (actionItem.attr('data-action') === 'permalink') {\n e.preventDefault();\n ModalCopyToClipboard.create({\n text: actionItem.attr('href'),\n }, str.get_string('sectionlink', 'course')\n );\n return;\n }\n\n let isExecuted = true;\n if (actionItem.attr('data-confirm')) {\n // Action requires confirmation.\n confirmEditSection(actionItem.attr('data-confirm'), function() {\n isExecuted = editSection(sectionElement, sectionId, actionItem, courseformat);\n });\n } else {\n isExecuted = editSection(sectionElement, sectionId, actionItem, courseformat);\n }\n // Prevent any other module from capturing the action if it is already in execution.\n if (isExecuted) {\n e.preventDefault();\n }\n });\n\n // The section and activity names are edited using inplace editable.\n // The \"update\" jQuery event must be captured in order to update the course state.\n $('body').on('updated', `${SELECTOR.SECTIONLI} ${SELECTOR.SECTIONITEM} [data-inplaceeditable]`, function(e) {\n if (e.ajaxreturn && e.ajaxreturn.itemid) {\n const state = courseeditor.state;\n const section = state.section.get(e.ajaxreturn.itemid);\n if (section !== undefined) {\n courseeditor.dispatch('sectionState', [e.ajaxreturn.itemid]);\n }\n }\n });\n $('body').on('updated', `${SELECTOR.ACTIVITYLI} [data-inplaceeditable]`, function(e) {\n if (e.ajaxreturn && e.ajaxreturn.itemid) {\n courseeditor.dispatch('cmState', [e.ajaxreturn.itemid]);\n }\n });\n\n // Component-based formats don't use modals to create sections.\n if (courseeditor.supportComponents && componentActions.includes('addSection')) {\n return;\n }\n\n // Add a handler for \"Add sections\" link to ask for a number of sections to add.\n str.get_string('numberweeks').done(function(strNumberSections) {\n var trigger = $(SELECTOR.ADDSECTIONS),\n modalTitle = trigger.attr('data-add-sections'),\n newSections = trigger.attr('data-new-sections');\n var modalBody = $('
' +\n '
');\n modalBody.find('label').html(strNumberSections);\n ModalFactory.create({\n title: modalTitle,\n type: ModalFactory.types.SAVE_CANCEL,\n body: modalBody.html()\n }, trigger)\n .done(function(modal) {\n var numSections = $(modal.getBody()).find('#add_section_numsections'),\n addSections = function() {\n // Check if value of the \"Number of sections\" is a valid positive integer and redirect\n // to adding a section script.\n if ('' + parseInt(numSections.val()) === numSections.val() && parseInt(numSections.val()) >= 1) {\n document.location = trigger.attr('href') + '&numsections=' + parseInt(numSections.val());\n }\n };\n modal.setSaveButtonText(modalTitle);\n modal.getRoot().on(ModalEvents.shown, function() {\n // When modal is shown focus and select the input and add a listener to keypress of \"Enter\".\n numSections.focus().select().on('keydown', function(e) {\n if (e.keyCode === KeyCodes.enter) {\n addSections();\n }\n });\n });\n modal.getRoot().on(ModalEvents.save, function(e) {\n // When modal \"Add\" button is pressed.\n e.preventDefault();\n addSections();\n });\n });\n });\n },\n\n /**\n * Replaces a section action menu item with another one (for example Show->Hide, Set marker->Remove marker)\n *\n * This method can be used by course formats in their listener to the coursesectionedited event\n *\n * @deprecated since Moodle 3.9\n * @param {JQuery} sectionelement\n * @param {String} selector CSS selector inside the section element, for example \"a[data-action=show]\"\n * @param {String} image new image name (\"i/show\", \"i/hide\", etc.)\n * @param {String} stringname new string for the action menu item\n * @param {String} stringcomponent\n * @param {String} newaction new value for data-action attribute of the link\n */\n replaceSectionActionItem: function(sectionelement, selector, image, stringname,\n stringcomponent, newaction) {\n log.debug('replaceSectionActionItem() is deprecated and will be removed.');\n var actionitem = sectionelement.find(SELECTOR.SECTIONACTIONMENU + ' ' + selector);\n replaceActionItem(actionitem, image, stringname, stringcomponent, newaction);\n },\n // Method to refresh a module.\n refreshModule,\n refreshSection,\n };\n });\n"],"names":["define","$","ajax","templates","notification","str","url","Y","ModalCopyToClipboard","ModalFactory","ModalEvents","KeyCodes","log","editor","EventDispatcher","CourseEvents","componentActions","courseeditor","getCurrentCourseEditor","formatname","CSS","SELECTOR","ACTIVITYLI","ACTIONAREA","ACTIVITYACTION","MENU","TOGGLE","SECTIONLI","SECTIONACTIONMENU","SECTIONITEM","ADDSECTIONS","SECTIONBADGES","use","courseformatselector","M","course","format","get_section_selector","dispatchEvent","eventName","detail","container","options","Element","undefined","get","getModuleId","element","item","dataset","id","Moodle","core_course","util","cm","getId","Node","addActivitySpinner","activity","addClass","actionarea","find","spinner","add_spinner","show","data","dispatch","addSectionSpinner","sectionelement","addSectionLightbox","lightbox","add_lightbox","for","setAttribute","removeSpinner","delay","window","setTimeout","removeClass","hide","mutation","removeLightbox","getAttribute","initActionMenu","elementid","coursebase","invoke_function","core","actionmenu","newDOMNode","one","editModule","moduleElement","cmid","target","action","attr","promises","call","methodname","args","sectionreturn","closest","when","apply","done","mainElement","tabables","isInside","foundElement","elementToFocus","each","contains","this","replaceWith","affectedids","index","elementId","mainelement","selector","is","focus","focusActionItem","push","trigger","Event","ajaxreturn","fail","ex","e","exception","isDefaultPrevented","refreshModule","sectionReturn","activityElement","Promise","resolve","reject","replaceActivityHtmlWith","confirmDeleteModule","onconfirm","modtypename","match","modulename","name","getName","state","_state$cm$get","getModuleName","get_string","pluginname","plugindata","type","get_strings","key","component","param","s","confirm","replaceActionItem","actionitem","image","stringname","stringcomponent","newaction","stringRequests","then","strings","html","renderPix","pixhtml","catch","defaultEditSectionHandler","sectionElement","actionItem","courseformat","sectionid","setSectionBadge","modules","i","section_availability","first","section","oldmarker","oldActionItem","activityHTML","focusedPath","document","getElementById","activeElement","querySelector","getActivityFocusedElement","editSection","supportComponents","includes","dataencoded","parseJSON","badgetype","add","removeOther","sectionbadges","badge","querySelectorAll","forEach","b","classList","remove","register_module","set_visibility_resource_ui","getDOMNode","updateMovedCmState","params","updateMovedSectionState","addMutations","legacyActivityAction","statemanager","setReadOnly","locked","cmlist","reduce","current","delete","legacySectionAction","initCoursePage","on","keyCode","moduleId","preventDefault","sectionId","create","text","isExecuted","message","itemid","strNumberSections","modalTitle","newSections","modalBody","title","types","SAVE_CANCEL","body","modal","numSections","getBody","addSections","parseInt","val","location","setSaveButtonText","getRoot","shown","select","enter","save","replaceSectionActionItem","debug","refreshSection","newSectionElement","content","sectionRefreshed","defaultPrevented"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"actions.min.js","sources":["../src/actions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Various actions on modules and sections in the editing mode - hiding, duplicating, deleting, etc.\n *\n * @module core_course/actions\n * @copyright 2016 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.3\n */\ndefine(\n [\n 'jquery',\n 'core/ajax',\n 'core/templates',\n 'core/notification',\n 'core/str',\n 'core/url',\n 'core/yui',\n 'core/modal_copy_to_clipboard',\n 'core/modal_factory',\n 'core/modal_events',\n 'core/key_codes',\n 'core/log',\n 'core_courseformat/courseeditor',\n 'core/event_dispatcher',\n 'core_course/events'\n ],\n function(\n $,\n ajax,\n templates,\n notification,\n str,\n url,\n Y,\n ModalCopyToClipboard,\n ModalFactory,\n ModalEvents,\n KeyCodes,\n log,\n editor,\n EventDispatcher,\n CourseEvents\n ) {\n\n // Eventually, core_courseformat/local/content/actions will handle all actions for\n // component compatible formats and the default actions.js won't be necessary anymore.\n // Meanwhile, we filter the migrated actions.\n const componentActions = [\n 'moveSection', 'moveCm', 'addSection', 'deleteSection', 'cmDelete', 'cmDuplicate', 'sectionHide', 'sectionShow',\n 'cmHide', 'cmShow', 'cmStealth', 'sectionHighlight', 'sectionUnhighlight', 'cmMoveRight', 'cmMoveLeft',\n 'cmNoGroups', 'cmVisibleGroups', 'cmSeparateGroups',\n ];\n\n // The course reactive instance.\n const courseeditor = editor.getCurrentCourseEditor();\n\n // The current course format name (loaded on init).\n let formatname;\n\n var CSS = {\n EDITINPROGRESS: 'editinprogress',\n SECTIONDRAGGABLE: 'sectiondraggable',\n EDITINGMOVE: 'editing_move'\n };\n var SELECTOR = {\n ACTIVITYLI: 'li.activity',\n ACTIONAREA: '.actions',\n ACTIVITYACTION: 'a.cm-edit-action',\n MENU: '.moodle-actionmenu[data-enhance=moodle-core-actionmenu]',\n TOGGLE: '.toggle-display,.dropdown-toggle',\n SECTIONLI: 'li.section',\n SECTIONACTIONMENU: '.section_action_menu',\n SECTIONITEM: '[data-for=\"section_title\"]',\n ADDSECTIONS: '.changenumsections [data-add-sections]',\n SECTIONBADGES: '[data-region=\"sectionbadges\"]',\n };\n\n Y.use('moodle-course-coursebase', function() {\n var courseformatselector = M.course.format.get_section_selector();\n if (courseformatselector) {\n SELECTOR.SECTIONLI = courseformatselector;\n }\n });\n\n /**\n * Dispatch event wrapper.\n *\n * Old jQuery events will be replaced by native events gradually.\n *\n * @method dispatchEvent\n * @param {String} eventName The name of the event\n * @param {Object} detail Any additional details to pass into the eveent\n * @param {Node|HTMLElement} container The point at which to dispatch the event\n * @param {Object} options\n * @param {Boolean} options.bubbles Whether to bubble up the DOM\n * @param {Boolean} options.cancelable Whether preventDefault() can be called\n * @param {Boolean} options.composed Whether the event can bubble across the ShadowDOM boundary\n * @returns {CustomEvent}\n */\n const dispatchEvent = function(eventName, detail, container, options) {\n // Most actions still uses jQuery node instead of regular HTMLElement.\n if (!(container instanceof Element) && container.get !== undefined) {\n container = container.get(0);\n }\n return EventDispatcher.dispatchEvent(eventName, detail, container, options);\n };\n\n /**\n * Wrapper for Y.Moodle.core_course.util.cm.getId\n *\n * @param {JQuery} element\n * @returns {Integer}\n */\n var getModuleId = function(element) {\n // Check if we have a data-id first.\n const item = element.get(0);\n if (item.dataset.id) {\n return item.dataset.id;\n }\n // Use YUI way if data-id is not present.\n let id;\n Y.use('moodle-course-util', function(Y) {\n id = Y.Moodle.core_course.util.cm.getId(Y.Node(item));\n });\n return id;\n };\n\n /**\n * Wrapper for Y.Moodle.core_course.util.cm.getName\n *\n * @param {JQuery} element\n * @returns {String}\n */\n var getModuleName = function(element) {\n var name;\n Y.use('moodle-course-util', function(Y) {\n name = Y.Moodle.core_course.util.cm.getName(Y.Node(element.get(0)));\n });\n // Check if we have the name in the course state.\n const state = courseeditor.state;\n const cmid = getModuleId(element);\n if (!name && state && cmid) {\n name = state.cm.get(cmid)?.name;\n }\n return name;\n };\n\n /**\n * Wrapper for M.util.add_spinner for an activity\n *\n * @param {JQuery} activity\n * @returns {Node}\n */\n var addActivitySpinner = function(activity) {\n activity.addClass(CSS.EDITINPROGRESS);\n var actionarea = activity.find(SELECTOR.ACTIONAREA).get(0);\n if (actionarea) {\n var spinner = M.util.add_spinner(Y, Y.Node(actionarea));\n spinner.show();\n // Lock the activity state element.\n if (activity.data('id') !== undefined) {\n courseeditor.dispatch('cmLock', [activity.data('id')], true);\n }\n return spinner;\n }\n return null;\n };\n\n /**\n * Wrapper for M.util.add_spinner for a section\n *\n * @param {JQuery} sectionelement\n * @returns {Node}\n */\n var addSectionSpinner = function(sectionelement) {\n sectionelement.addClass(CSS.EDITINPROGRESS);\n var actionarea = sectionelement.find(SELECTOR.SECTIONACTIONMENU).get(0);\n if (actionarea) {\n var spinner = M.util.add_spinner(Y, Y.Node(actionarea));\n spinner.show();\n // Lock the section state element.\n if (sectionelement.data('id') !== undefined) {\n courseeditor.dispatch('sectionLock', [sectionelement.data('id')], true);\n }\n return spinner;\n }\n return null;\n };\n\n /**\n * Wrapper for M.util.add_lightbox\n *\n * @param {JQuery} sectionelement\n * @returns {Node}\n */\n var addSectionLightbox = function(sectionelement) {\n const item = sectionelement.get(0);\n var lightbox = M.util.add_lightbox(Y, Y.Node(item));\n if (item.dataset.for == 'section' && item.dataset.id) {\n courseeditor.dispatch('sectionLock', [item.dataset.id], true);\n lightbox.setAttribute('data-state', 'section');\n lightbox.setAttribute('data-state-id', item.dataset.id);\n }\n lightbox.show();\n return lightbox;\n };\n\n /**\n * Removes the spinner element\n *\n * @param {JQuery} element\n * @param {Node} spinner\n * @param {Number} delay\n */\n var removeSpinner = function(element, spinner, delay) {\n window.setTimeout(function() {\n element.removeClass(CSS.EDITINPROGRESS);\n if (spinner) {\n spinner.hide();\n }\n // Unlock the state element.\n if (element.data('id') !== undefined) {\n const mutation = (element.data('for') === 'section') ? 'sectionLock' : 'cmLock';\n courseeditor.dispatch(mutation, [element.data('id')], false);\n }\n }, delay);\n };\n\n /**\n * Removes the lightbox element\n *\n * @param {Node} lightbox lighbox YUI element returned by addSectionLightbox\n * @param {Number} delay\n */\n var removeLightbox = function(lightbox, delay) {\n if (lightbox) {\n window.setTimeout(function() {\n lightbox.hide();\n // Unlock state if necessary.\n if (lightbox.getAttribute('data-state')) {\n courseeditor.dispatch(\n `${lightbox.getAttribute('data-state')}Lock`,\n [lightbox.getAttribute('data-state-id')],\n false\n );\n }\n }, delay);\n }\n };\n\n /**\n * Initialise action menu for the element (section or module)\n *\n * @param {String} elementid CSS id attribute of the element\n */\n var initActionMenu = function(elementid) {\n // Initialise action menu in the new activity.\n Y.use('moodle-course-coursebase', function() {\n M.course.coursebase.invoke_function('setup_for_resource', '#' + elementid);\n });\n if (M.core.actionmenu && M.core.actionmenu.newDOMNode) {\n M.core.actionmenu.newDOMNode(Y.one('#' + elementid));\n }\n };\n\n /**\n * Returns focus to the element that was clicked or \"Edit\" link if element is no longer visible.\n *\n * @param {String} elementId CSS id attribute of the element\n * @param {String} action data-action property of the element that was clicked\n */\n var focusActionItem = function(elementId, action) {\n var mainelement = $('#' + elementId);\n var selector = '[data-action=' + action + ']';\n if (action === 'groupsseparate' || action === 'groupsvisible' || action === 'groupsnone') {\n // New element will have different data-action.\n selector = '[data-action=groupsseparate],[data-action=groupsvisible],[data-action=groupsnone]';\n }\n if (mainelement.find(selector).is(':visible')) {\n mainelement.find(selector).focus();\n } else {\n // Element not visible, focus the \"Edit\" link.\n mainelement.find(SELECTOR.MENU).find(SELECTOR.TOGGLE).focus();\n }\n };\n\n /**\n * Find next
after the element\n *\n * @param {JQuery} mainElement element that is about to be deleted\n * @returns {JQuery}\n */\n var findNextFocusable = function(mainElement) {\n var tabables = $(\"a:visible\");\n var isInside = false;\n var foundElement = null;\n tabables.each(function() {\n if ($.contains(mainElement[0], this)) {\n isInside = true;\n } else if (isInside) {\n foundElement = this;\n return false; // Returning false in .each() is equivalent to \"break;\" inside the loop in php.\n }\n return true;\n });\n return foundElement;\n };\n\n /**\n * Performs an action on a module (moving, deleting, duplicating, hiding, etc.)\n *\n * @param {JQuery} moduleElement activity element we perform action on\n * @param {Number} cmid\n * @param {JQuery} target the element (menu item) that was clicked\n */\n var editModule = function(moduleElement, cmid, target) {\n var action = target.attr('data-action');\n var spinner = addActivitySpinner(moduleElement);\n var promises = ajax.call([{\n methodname: 'core_course_edit_module',\n args: {id: cmid,\n action: action,\n sectionreturn: target.attr('data-sectionreturn') ? target.attr('data-sectionreturn') : 0\n }\n }], true);\n\n var lightbox;\n if (action === 'duplicate') {\n lightbox = addSectionLightbox(target.closest(SELECTOR.SECTIONLI));\n }\n $.when.apply($, promises)\n .done(function(data) {\n var elementToFocus = findNextFocusable(moduleElement);\n moduleElement.replaceWith(data);\n let affectedids = [];\n // Initialise action menu for activity(ies) added as a result of this.\n $('
' + data + '
').find(SELECTOR.ACTIVITYLI).each(function(index) {\n initActionMenu($(this).attr('id'));\n if (index === 0) {\n focusActionItem($(this).attr('id'), action);\n elementToFocus = null;\n }\n // Save any activity id in cmids.\n affectedids.push(getModuleId($(this)));\n });\n // In case of activity deletion focus the next focusable element.\n if (elementToFocus) {\n elementToFocus.focus();\n }\n // Remove spinner and lightbox with a delay.\n removeSpinner(moduleElement, spinner, 400);\n removeLightbox(lightbox, 400);\n // Trigger event that can be observed by course formats.\n moduleElement.trigger($.Event('coursemoduleedited', {ajaxreturn: data, action: action}));\n\n // Modify cm state.\n courseeditor.dispatch('legacyActivityAction', action, cmid, affectedids);\n\n }).fail(function(ex) {\n // Remove spinner and lightbox.\n removeSpinner(moduleElement, spinner);\n removeLightbox(lightbox);\n // Trigger event that can be observed by course formats.\n var e = $.Event('coursemoduleeditfailed', {exception: ex, action: action});\n moduleElement.trigger(e);\n if (!e.isDefaultPrevented()) {\n notification.exception(ex);\n }\n });\n };\n\n /**\n * Requests html for the module via WS core_course_get_module and updates the module on the course page\n *\n * Used after d&d of the module to another section\n *\n * @param {JQuery|Element} element\n * @param {Number} cmid\n * @param {Number} sectionreturn\n * @return {Promise} the refresh promise\n */\n var refreshModule = function(element, cmid, sectionreturn) {\n\n if (sectionreturn === undefined) {\n sectionreturn = courseeditor.sectionReturn;\n }\n\n const activityElement = $(element);\n var spinner = addActivitySpinner(activityElement);\n var promises = ajax.call([{\n methodname: 'core_course_get_module',\n args: {id: cmid, sectionreturn: sectionreturn}\n }], true);\n\n return new Promise((resolve, reject) => {\n $.when.apply($, promises)\n .done(function(data) {\n removeSpinner(activityElement, spinner, 400);\n replaceActivityHtmlWith(data);\n resolve(data);\n }).fail(function() {\n removeSpinner(activityElement, spinner);\n reject();\n });\n });\n };\n\n /**\n * Requests html for the section via WS core_course_edit_section and updates the section on the course page\n *\n * @param {JQuery|Element} element\n * @param {Number} sectionid\n * @param {Number} sectionreturn\n * @return {Promise} the refresh promise\n */\n var refreshSection = function(element, sectionid, sectionreturn) {\n\n if (sectionreturn === undefined) {\n sectionreturn = courseeditor.sectionReturn;\n }\n\n const sectionElement = $(element);\n const action = 'refresh';\n const promises = ajax.call([{\n methodname: 'core_course_edit_section',\n args: {id: sectionid, action, sectionreturn},\n }], true);\n\n var spinner = addSectionSpinner(sectionElement);\n return new Promise((resolve, reject) => {\n $.when.apply($, promises)\n .done(dataencoded => {\n\n removeSpinner(sectionElement, spinner);\n const data = $.parseJSON(dataencoded);\n\n const newSectionElement = $(data.content);\n sectionElement.replaceWith(newSectionElement);\n\n // Init modules menus.\n $(`${SELECTOR.SECTIONLI}#${sectionid} ${SELECTOR.ACTIVITYLI}`).each(\n (index, activity) => {\n initActionMenu(activity.data('id'));\n }\n );\n\n // Trigger event that can be observed by course formats.\n const event = dispatchEvent(\n CourseEvents.sectionRefreshed,\n {\n ajaxreturn: data,\n action: action,\n newSectionElement: newSectionElement.get(0),\n },\n newSectionElement\n );\n\n if (!event.defaultPrevented) {\n defaultEditSectionHandler(\n newSectionElement, $(SELECTOR.SECTIONLI + '#' + sectionid),\n data,\n formatname,\n sectionid\n );\n }\n resolve(data);\n }).fail(ex => {\n // Trigger event that can be observed by course formats.\n const event = dispatchEvent(\n 'coursesectionrefreshfailed',\n {exception: ex, action: action},\n sectionElement\n );\n if (!event.defaultPrevented) {\n notification.exception(ex);\n }\n reject();\n });\n });\n };\n\n /**\n * Displays the delete confirmation to delete a module\n *\n * @param {JQuery} mainelement activity element we perform action on\n * @param {function} onconfirm function to execute on confirm\n */\n var confirmDeleteModule = function(mainelement, onconfirm) {\n var modtypename = mainelement.attr('class').match(/modtype_([^\\s]*)/)[1];\n var modulename = getModuleName(mainelement);\n\n str.get_string('pluginname', modtypename).done(function(pluginname) {\n var plugindata = {\n type: pluginname,\n name: modulename\n };\n str.get_strings([\n {key: 'confirm', component: 'core'},\n {key: modulename === null ? 'deletechecktype' : 'deletechecktypename', param: plugindata},\n {key: 'yes'},\n {key: 'no'}\n ]).done(function(s) {\n notification.confirm(s[0], s[1], s[2], s[3], onconfirm);\n }\n );\n });\n };\n\n /**\n * Displays the delete confirmation to delete a section\n *\n * @param {String} message confirmation message\n * @param {function} onconfirm function to execute on confirm\n */\n var confirmEditSection = function(message, onconfirm) {\n str.get_strings([\n {key: 'confirm'}, // TODO link text\n {key: 'yes'},\n {key: 'no'}\n ]).done(function(s) {\n notification.confirm(s[0], message, s[1], s[2], onconfirm);\n }\n );\n };\n\n /**\n * Replaces an action menu item with another one (for example Show->Hide, Set marker->Remove marker)\n *\n * @param {JQuery} actionitem\n * @param {String} image new image name (\"i/show\", \"i/hide\", etc.)\n * @param {String} stringname new string for the action menu item\n * @param {String} stringcomponent\n * @param {String} newaction new value for data-action attribute of the link\n * @return {Promise} promise which is resolved when the replacement has completed\n */\n var replaceActionItem = function(actionitem, image, stringname,\n stringcomponent, newaction) {\n\n var stringRequests = [{key: stringname, component: stringcomponent}];\n // Do not provide an icon with duplicate, different text to the menu item.\n\n return str.get_strings(stringRequests).then(function(strings) {\n actionitem.find('span.menu-action-text').html(strings[0]);\n\n return templates.renderPix(image, 'core');\n }).then(function(pixhtml) {\n actionitem.find('.icon').replaceWith(pixhtml);\n actionitem.attr('data-action', newaction);\n return;\n }).catch(notification.exception);\n };\n\n /**\n * Default post-processing for section AJAX edit actions.\n *\n * This can be overridden in course formats by listening to event coursesectionedited:\n *\n * $('body').on('coursesectionedited', 'li.section', function(e) {\n * var action = e.action,\n * sectionElement = $(e.target),\n * data = e.ajaxreturn;\n * // ... Do some processing here.\n * e.preventDefault(); // Prevent default handler.\n * });\n *\n * @param {JQuery} sectionElement\n * @param {JQuery} actionItem\n * @param {Object} data\n * @param {String} courseformat\n * @param {Number} sectionid\n */\n var defaultEditSectionHandler = function(sectionElement, actionItem, data, courseformat, sectionid) {\n var action = actionItem.attr('data-action');\n if (action === 'hide' || action === 'show') {\n if (action === 'hide') {\n sectionElement.addClass('hidden');\n setSectionBadge(sectionElement[0], 'hiddenfromstudents', true, false);\n replaceActionItem(actionItem, 'i/show',\n 'showfromothers', 'format_' + courseformat, 'show');\n } else {\n setSectionBadge(sectionElement[0], 'hiddenfromstudents', false, false);\n sectionElement.removeClass('hidden');\n replaceActionItem(actionItem, 'i/hide',\n 'hidefromothers', 'format_' + courseformat, 'hide');\n }\n // Replace the modules with new html (that indicates that they are now hidden or not hidden).\n if (data.modules !== undefined) {\n for (var i in data.modules) {\n replaceActivityHtmlWith(data.modules[i]);\n }\n }\n // Replace the section availability information.\n if (data.section_availability !== undefined) {\n sectionElement.find('.section_availability').first().replaceWith(data.section_availability);\n }\n // Modify course state.\n const section = courseeditor.state.section.get(sectionid);\n if (section !== undefined) {\n courseeditor.dispatch('sectionState', [sectionid]);\n }\n } else if (action === 'setmarker') {\n var oldmarker = $(SELECTOR.SECTIONLI + '.current'),\n oldActionItem = oldmarker.find(SELECTOR.SECTIONACTIONMENU + ' ' + 'a[data-action=removemarker]');\n oldmarker.removeClass('current');\n replaceActionItem(oldActionItem, 'i/marker',\n 'highlight', 'core', 'setmarker');\n sectionElement.addClass('current');\n replaceActionItem(actionItem, 'i/marked',\n 'highlightoff', 'core', 'removemarker');\n courseeditor.dispatch('legacySectionAction', action, sectionid);\n setSectionBadge(sectionElement[0], 'iscurrent', true, true);\n } else if (action === 'removemarker') {\n sectionElement.removeClass('current');\n replaceActionItem(actionItem, 'i/marker',\n 'highlight', 'core', 'setmarker');\n courseeditor.dispatch('legacySectionAction', action, sectionid);\n setSectionBadge(sectionElement[0], 'iscurrent', false, true);\n }\n };\n\n /**\n * Get the focused element path in an activity if any.\n *\n * This method is used to restore focus when the activity HTML is refreshed.\n * Only the main course editor elements can be refocused as they are always present\n * even if the activity content changes.\n *\n * @param {String} id the element id the activity element\n * @return {String|undefined} the inner path of the focused element or undefined\n */\n const getActivityFocusedElement = function(id) {\n const element = document.getElementById(id);\n if (!element || !element.contains(document.activeElement)) {\n return undefined;\n }\n // Check if the actions menu toggler is focused.\n if (element.querySelector(SELECTOR.ACTIONAREA).contains(document.activeElement)) {\n return `${SELECTOR.ACTIONAREA} [tabindex=\"0\"]`;\n }\n // Return the current element id if any.\n if (document.activeElement.id) {\n return `#${document.activeElement.id}`;\n }\n return undefined;\n };\n\n /**\n * Replaces the course module with the new html (used to update module after it was edited or its visibility was changed).\n *\n * @param {String} activityHTML\n */\n var replaceActivityHtmlWith = function(activityHTML) {\n $('
' + activityHTML + '
').find(SELECTOR.ACTIVITYLI).each(function() {\n // Extract id from the new activity html.\n var id = $(this).attr('id');\n // Check if the current focused element is inside the activity.\n let focusedPath = getActivityFocusedElement(id);\n // Find the existing element with the same id and replace its contents with new html.\n $(SELECTOR.ACTIVITYLI + '#' + id).replaceWith(activityHTML);\n // Initialise action menu.\n initActionMenu(id);\n // Re-focus the previous elements.\n if (focusedPath) {\n const newItem = document.getElementById(id);\n newItem.querySelector(focusedPath)?.focus();\n }\n\n });\n };\n\n /**\n * Performs an action on a module (moving, deleting, duplicating, hiding, etc.)\n *\n * @param {JQuery} sectionElement section element we perform action on\n * @param {Nunmber} sectionid\n * @param {JQuery} target the element (menu item) that was clicked\n * @param {String} courseformat\n * @return {boolean} true the action call is sent to the server or false if it is ignored.\n */\n var editSection = function(sectionElement, sectionid, target, courseformat) {\n var action = target.attr('data-action'),\n sectionreturn = target.attr('data-sectionreturn') ? target.attr('data-sectionreturn') : 0;\n\n // Filter direct component handled actions.\n if (courseeditor.supportComponents && componentActions.includes(action)) {\n return false;\n }\n\n var spinner = addSectionSpinner(sectionElement);\n var promises = ajax.call([{\n methodname: 'core_course_edit_section',\n args: {id: sectionid, action: action, sectionreturn: sectionreturn}\n }], true);\n\n var lightbox = addSectionLightbox(sectionElement);\n $.when.apply($, promises)\n .done(function(dataencoded) {\n var data = $.parseJSON(dataencoded);\n removeSpinner(sectionElement, spinner);\n removeLightbox(lightbox);\n sectionElement.find(SELECTOR.SECTIONACTIONMENU).find(SELECTOR.TOGGLE).focus();\n // Trigger event that can be observed by course formats.\n var e = $.Event('coursesectionedited', {ajaxreturn: data, action: action});\n sectionElement.trigger(e);\n if (!e.isDefaultPrevented()) {\n defaultEditSectionHandler(sectionElement, target, data, courseformat, sectionid);\n }\n }).fail(function(ex) {\n // Remove spinner and lightbox.\n removeSpinner(sectionElement, spinner);\n removeLightbox(lightbox);\n // Trigger event that can be observed by course formats.\n var e = $.Event('coursesectioneditfailed', {exception: ex, action: action});\n sectionElement.trigger(e);\n if (!e.isDefaultPrevented()) {\n notification.exception(ex);\n }\n });\n return true;\n };\n\n /**\n * Sets the section badge in the section header.\n *\n * @param {JQuery} sectionElement section element we perform action on\n * @param {String} badgetype the type of badge this is for\n * @param {bool} add true to add, false to remove\n * @param {boolean} removeOther in case of adding a badge, whether to remove all other.\n */\n var setSectionBadge = function(sectionElement, badgetype, add, removeOther) {\n const sectionbadges = sectionElement.querySelector(SELECTOR.SECTIONBADGES);\n if (!sectionbadges) {\n return;\n }\n const badge = sectionbadges.querySelector('[data-type=\"' + badgetype + '\"]');\n if (!badge) {\n return;\n }\n if (add) {\n if (removeOther) {\n document.querySelectorAll('[data-type=\"' + badgetype + '\"]').forEach((b) => {\n b.classList.add('d-none');\n });\n }\n badge.classList.remove('d-none');\n } else {\n badge.classList.add('d-none');\n }\n };\n\n // Register a function to be executed after D&D of an activity.\n Y.use('moodle-course-coursebase', function() {\n M.course.coursebase.register_module({\n // Ignore camelcase eslint rule for the next line because it is an expected name of the callback.\n // eslint-disable-next-line camelcase\n set_visibility_resource_ui: function(args) {\n var mainelement = $(args.element.getDOMNode());\n var cmid = getModuleId(mainelement);\n if (cmid) {\n var sectionreturn = mainelement.find('.' + CSS.EDITINGMOVE).attr('data-sectionreturn');\n refreshModule(mainelement, cmid, sectionreturn);\n }\n },\n /**\n * Update the course state when some cm is moved via YUI.\n * @param {*} params\n */\n updateMovedCmState: (params) => {\n const state = courseeditor.state;\n\n // Update old section.\n const cm = state.cm.get(params.cmid);\n if (cm !== undefined) {\n courseeditor.dispatch('sectionState', [cm.sectionid]);\n }\n // Update cm state.\n courseeditor.dispatch('cmState', [params.cmid]);\n },\n /**\n * Update the course state when some section is moved via YUI.\n */\n updateMovedSectionState: () => {\n courseeditor.dispatch('courseState');\n },\n });\n });\n\n // From Moodle 4.0 all edit actions are being re-implemented as state mutation.\n // This means all method from this \"actions\" module will be deprecated when all the course\n // interface is migrated to reactive components.\n // Most legacy actions did not provide enough information to regenarate the course so they\n // use the mutations courseState, sectionState and cmState to get the updated state from\n // the server. However, some activity actions where we can prevent an extra webservice\n // call by implementing an adhoc mutation.\n courseeditor.addMutations({\n /**\n * Compatibility function to update Moodle 4.0 course state using legacy actions.\n *\n * This method only updates some actions which does not require to use cmState mutation\n * to get updated data form the server.\n *\n * @param {Object} statemanager the current state in read write mode\n * @param {String} action the performed action\n * @param {Number} cmid the affected course module id\n * @param {Array} affectedids all affected cm ids (for duplicate action)\n */\n legacyActivityAction: function(statemanager, action, cmid, affectedids) {\n\n const state = statemanager.state;\n const cm = state.cm.get(cmid);\n if (cm === undefined) {\n return;\n }\n const section = state.section.get(cm.sectionid);\n if (section === undefined) {\n return;\n }\n\n // Send the element is locked.\n courseeditor.dispatch('cmLock', [cm.id], true);\n\n // Now we do the real mutation.\n statemanager.setReadOnly(false);\n\n // This unlocked will take effect when the read only is restored.\n cm.locked = false;\n\n switch (action) {\n case 'delete':\n // Remove from section.\n section.cmlist = section.cmlist.reduce(\n (cmlist, current) => {\n if (current != cmid) {\n cmlist.push(current);\n }\n return cmlist;\n },\n []\n );\n // Delete form list.\n state.cm.delete(cmid);\n break;\n\n case 'hide':\n case 'show':\n case 'duplicate':\n courseeditor.dispatch('cmState', affectedids);\n break;\n }\n statemanager.setReadOnly(true);\n },\n legacySectionAction: function(statemanager, action, sectionid) {\n\n const state = statemanager.state;\n const section = state.section.get(sectionid);\n if (section === undefined) {\n return;\n }\n\n // Send the element is locked. Reactive events are only triggered when the state\n // read only mode is restored. We want to notify the interface the element is\n // locked so we need to do a quick lock operation before performing the rest\n // of the mutation.\n statemanager.setReadOnly(false);\n section.locked = true;\n statemanager.setReadOnly(true);\n\n // Now we do the real mutation.\n statemanager.setReadOnly(false);\n\n // This locked will take effect when the read only is restored.\n section.locked = false;\n\n switch (action) {\n case 'setmarker':\n // Remove previous marker.\n state.section.forEach((current) => {\n if (current.id != sectionid) {\n current.current = false;\n }\n });\n section.current = true;\n break;\n\n case 'removemarker':\n section.current = false;\n break;\n }\n statemanager.setReadOnly(true);\n },\n });\n\n return /** @alias module:core_course/actions */ {\n\n /**\n * Initialises course page\n *\n * @method init\n * @param {String} courseformat name of the current course format (for fetching strings)\n */\n initCoursePage: function(courseformat) {\n\n formatname = courseformat;\n\n // Add a handler for course module actions.\n $('body').on('click keypress', SELECTOR.ACTIVITYLI + ' ' +\n SELECTOR.ACTIVITYACTION + '[data-action]', function(e) {\n if (e.type === 'keypress' && e.keyCode !== 13) {\n return;\n }\n var actionItem = $(this),\n moduleElement = actionItem.closest(SELECTOR.ACTIVITYLI),\n action = actionItem.attr('data-action'),\n moduleId = getModuleId(moduleElement);\n switch (action) {\n case 'moveleft':\n case 'moveright':\n case 'delete':\n case 'duplicate':\n case 'hide':\n case 'stealth':\n case 'show':\n case 'groupsseparate':\n case 'groupsvisible':\n case 'groupsnone':\n break;\n default:\n // Nothing to do here!\n return;\n }\n if (!moduleId) {\n return;\n }\n e.preventDefault();\n if (action === 'delete') {\n // Deleting requires confirmation.\n confirmDeleteModule(moduleElement, function() {\n editModule(moduleElement, moduleId, actionItem);\n });\n } else {\n editModule(moduleElement, moduleId, actionItem);\n }\n });\n\n // Add a handler for section show/hide actions.\n $('body').on('click keypress', SELECTOR.SECTIONLI + ' ' +\n SELECTOR.SECTIONACTIONMENU + '[data-sectionid] ' +\n 'a[data-action]', function(e) {\n if (e.type === 'keypress' && e.keyCode !== 13) {\n return;\n }\n var actionItem = $(this),\n sectionElement = actionItem.closest(SELECTOR.SECTIONLI),\n sectionId = actionItem.closest(SELECTOR.SECTIONACTIONMENU).attr('data-sectionid');\n\n if (actionItem.attr('data-action') === 'permalink') {\n e.preventDefault();\n ModalCopyToClipboard.create({\n text: actionItem.attr('href'),\n }, str.get_string('sectionlink', 'course')\n );\n return;\n }\n\n let isExecuted = true;\n if (actionItem.attr('data-confirm')) {\n // Action requires confirmation.\n confirmEditSection(actionItem.attr('data-confirm'), function() {\n isExecuted = editSection(sectionElement, sectionId, actionItem, courseformat);\n });\n } else {\n isExecuted = editSection(sectionElement, sectionId, actionItem, courseformat);\n }\n // Prevent any other module from capturing the action if it is already in execution.\n if (isExecuted) {\n e.preventDefault();\n }\n });\n\n // The section and activity names are edited using inplace editable.\n // The \"update\" jQuery event must be captured in order to update the course state.\n $('body').on('updated', `${SELECTOR.SECTIONLI} ${SELECTOR.SECTIONITEM} [data-inplaceeditable]`, function(e) {\n if (e.ajaxreturn && e.ajaxreturn.itemid) {\n const state = courseeditor.state;\n const section = state.section.get(e.ajaxreturn.itemid);\n if (section !== undefined) {\n courseeditor.dispatch('sectionState', [e.ajaxreturn.itemid]);\n }\n }\n });\n $('body').on('updated', `${SELECTOR.ACTIVITYLI} [data-inplaceeditable]`, function(e) {\n if (e.ajaxreturn && e.ajaxreturn.itemid) {\n courseeditor.dispatch('cmState', [e.ajaxreturn.itemid]);\n }\n });\n\n // Component-based formats don't use modals to create sections.\n if (courseeditor.supportComponents && componentActions.includes('addSection')) {\n return;\n }\n\n // Add a handler for \"Add sections\" link to ask for a number of sections to add.\n str.get_string('numberweeks').done(function(strNumberSections) {\n var trigger = $(SELECTOR.ADDSECTIONS),\n modalTitle = trigger.attr('data-add-sections'),\n newSections = trigger.attr('data-new-sections');\n var modalBody = $('
' +\n '
');\n modalBody.find('label').html(strNumberSections);\n ModalFactory.create({\n title: modalTitle,\n type: ModalFactory.types.SAVE_CANCEL,\n body: modalBody.html()\n }, trigger)\n .done(function(modal) {\n var numSections = $(modal.getBody()).find('#add_section_numsections'),\n addSections = function() {\n // Check if value of the \"Number of sections\" is a valid positive integer and redirect\n // to adding a section script.\n if ('' + parseInt(numSections.val()) === numSections.val() && parseInt(numSections.val()) >= 1) {\n document.location = trigger.attr('href') + '&numsections=' + parseInt(numSections.val());\n }\n };\n modal.setSaveButtonText(modalTitle);\n modal.getRoot().on(ModalEvents.shown, function() {\n // When modal is shown focus and select the input and add a listener to keypress of \"Enter\".\n numSections.focus().select().on('keydown', function(e) {\n if (e.keyCode === KeyCodes.enter) {\n addSections();\n }\n });\n });\n modal.getRoot().on(ModalEvents.save, function(e) {\n // When modal \"Add\" button is pressed.\n e.preventDefault();\n addSections();\n });\n });\n });\n },\n\n /**\n * Replaces a section action menu item with another one (for example Show->Hide, Set marker->Remove marker)\n *\n * This method can be used by course formats in their listener to the coursesectionedited event\n *\n * @deprecated since Moodle 3.9\n * @param {JQuery} sectionelement\n * @param {String} selector CSS selector inside the section element, for example \"a[data-action=show]\"\n * @param {String} image new image name (\"i/show\", \"i/hide\", etc.)\n * @param {String} stringname new string for the action menu item\n * @param {String} stringcomponent\n * @param {String} newaction new value for data-action attribute of the link\n */\n replaceSectionActionItem: function(sectionelement, selector, image, stringname,\n stringcomponent, newaction) {\n log.debug('replaceSectionActionItem() is deprecated and will be removed.');\n var actionitem = sectionelement.find(SELECTOR.SECTIONACTIONMENU + ' ' + selector);\n replaceActionItem(actionitem, image, stringname, stringcomponent, newaction);\n },\n // Method to refresh a module.\n refreshModule,\n refreshSection,\n };\n });\n"],"names":["define","$","ajax","templates","notification","str","url","Y","ModalCopyToClipboard","ModalFactory","ModalEvents","KeyCodes","log","editor","EventDispatcher","CourseEvents","componentActions","courseeditor","getCurrentCourseEditor","formatname","CSS","SELECTOR","ACTIVITYLI","ACTIONAREA","ACTIVITYACTION","MENU","TOGGLE","SECTIONLI","SECTIONACTIONMENU","SECTIONITEM","ADDSECTIONS","SECTIONBADGES","use","courseformatselector","M","course","format","get_section_selector","dispatchEvent","eventName","detail","container","options","Element","undefined","get","getModuleId","element","item","dataset","id","Moodle","core_course","util","cm","getId","Node","addActivitySpinner","activity","addClass","actionarea","find","spinner","add_spinner","show","data","dispatch","addSectionSpinner","sectionelement","addSectionLightbox","lightbox","add_lightbox","for","setAttribute","removeSpinner","delay","window","setTimeout","removeClass","hide","mutation","removeLightbox","getAttribute","initActionMenu","elementid","coursebase","invoke_function","core","actionmenu","newDOMNode","one","editModule","moduleElement","cmid","target","action","attr","promises","call","methodname","args","sectionreturn","closest","when","apply","done","mainElement","tabables","isInside","foundElement","elementToFocus","each","contains","this","replaceWith","affectedids","index","elementId","mainelement","selector","is","focus","focusActionItem","push","trigger","Event","ajaxreturn","fail","ex","e","exception","isDefaultPrevented","refreshModule","sectionReturn","activityElement","Promise","resolve","reject","replaceActivityHtmlWith","confirmDeleteModule","onconfirm","modtypename","match","modulename","name","getName","state","_state$cm$get","getModuleName","get_string","pluginname","plugindata","type","get_strings","key","component","param","s","confirm","replaceActionItem","actionitem","image","stringname","stringcomponent","newaction","stringRequests","then","strings","html","renderPix","pixhtml","catch","defaultEditSectionHandler","sectionElement","actionItem","courseformat","sectionid","setSectionBadge","modules","i","section_availability","first","section","oldmarker","oldActionItem","activityHTML","focusedPath","document","getElementById","activeElement","querySelector","getActivityFocusedElement","editSection","supportComponents","includes","dataencoded","parseJSON","badgetype","add","removeOther","sectionbadges","badge","querySelectorAll","forEach","b","classList","remove","register_module","set_visibility_resource_ui","getDOMNode","updateMovedCmState","params","updateMovedSectionState","addMutations","legacyActivityAction","statemanager","setReadOnly","locked","cmlist","reduce","current","delete","legacySectionAction","initCoursePage","on","keyCode","moduleId","preventDefault","sectionId","create","text","isExecuted","message","itemid","strNumberSections","modalTitle","newSections","modalBody","title","types","SAVE_CANCEL","body","modal","numSections","getBody","addSections","parseInt","val","location","setSaveButtonText","getRoot","shown","select","enter","save","replaceSectionActionItem","debug","refreshSection","newSectionElement","content","sectionRefreshed","defaultPrevented"],"mappings":"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"} \ No newline at end of file diff --git a/course/amd/src/actions.js b/course/amd/src/actions.js index bd5a08b0eef..09a7df46277 100644 --- a/course/amd/src/actions.js +++ b/course/amd/src/actions.js @@ -63,6 +63,7 @@ define( const componentActions = [ 'moveSection', 'moveCm', 'addSection', 'deleteSection', 'cmDelete', 'cmDuplicate', 'sectionHide', 'sectionShow', 'cmHide', 'cmShow', 'cmStealth', 'sectionHighlight', 'sectionUnhighlight', 'cmMoveRight', 'cmMoveLeft', + 'cmNoGroups', 'cmVisibleGroups', 'cmSeparateGroups', ]; // The course reactive instance. diff --git a/course/format/amd/build/local/content.min.js b/course/format/amd/build/local/content.min.js index 85b8e80f8d3..f5b6e6f6777 100644 --- a/course/format/amd/build/local/content.min.js +++ b/course/format/amd/build/local/content.min.js @@ -6,6 +6,6 @@ define("core_courseformat/local/content",["exports","core/reactive","core/utils" * @class core_courseformat/local/content * @copyright 2020 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_config=_interopRequireDefault(_config),_inplace_editable=_interopRequireDefault(_inplace_editable),_section=_interopRequireDefault(_section),_cmitem=_interopRequireDefault(_cmitem),_fragment=_interopRequireDefault(_fragment),_templates=_interopRequireDefault(_templates),_actions=_interopRequireDefault(_actions),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending);class Component extends _reactive.BaseComponent{create(descriptor){var _descriptor$sectionRe;this.name="course_format",this.selectors={SECTION:"[data-for='section']",SECTION_ITEM:"[data-for='section_title']",SECTION_CMLIST:"[data-for='cmlist']",COURSE_SECTIONLIST:"[data-for='course_sectionlist']",CM:"[data-for='cmitem']",PAGE:"#page",TOGGLER:'[data-action="togglecoursecontentsection"]',COLLAPSE:'[data-toggle="collapse"]',TOGGLEALL:'[data-toggle="toggleall"]',ACTIVITYTAG:"li",SECTIONTAG:"li"},this.classes={COLLAPSED:"collapsed",ACTIVITY:"activity",STATEDREADY:"stateready",SECTION:"section"},this.dettachedCms={},this.dettachedSections={},this.sections={},this.cms={},this.sectionReturn=null!==(_descriptor$sectionRe=descriptor.sectionReturn)&&void 0!==_descriptor$sectionRe?_descriptor$sectionRe:0,this.debouncedReloads=new Map}static init(target,selectors,sectionReturn){return new Component({element:document.getElementById(target),reactive:(0,_courseeditor.getCurrentCourseEditor)(),selectors:selectors,sectionReturn:sectionReturn})}stateReady(state){this._indexContents(),this.addEventListener(this.element,"click",this._sectionTogglers);const toogleAll=this.getElement(this.selectors.TOGGLEALL);if(toogleAll){const collapseElementIds=[...this.getElements(this.selectors.COLLAPSE)].map((element=>element.id));toogleAll.setAttribute("aria-controls",collapseElementIds.join(" ")),this.addEventListener(toogleAll,"click",this._allSectionToggler),this.addEventListener(toogleAll,"keydown",(e=>{" "===e.key&&this._allSectionToggler(e)})),this._refreshAllSectionsToggler(state)}this.reactive.supportComponents&&(this.reactive.isEditing&&new _actions.default(this),this.element.classList.add(this.classes.STATEDREADY)),this.addEventListener(this.element,CourseEvents.manualCompletionToggled,this._completionHandler),this.addEventListener(document.querySelector(this.selectors.PAGE),"scroll",this._scrollHandler)}_sectionTogglers(event){const sectionlink=event.target.closest(this.selectors.TOGGLER),closestCollapse=event.target.closest(this.selectors.COLLAPSE),isChevron=null==closestCollapse?void 0:closestCollapse.closest(this.selectors.SECTION_ITEM);if(sectionlink||isChevron){var _toggler$classList$co;const section=event.target.closest(this.selectors.SECTION),toggler=section.querySelector(this.selectors.COLLAPSE),isCollapsed=null!==(_toggler$classList$co=null==toggler?void 0:toggler.classList.contains(this.classes.COLLAPSED))&&void 0!==_toggler$classList$co&&_toggler$classList$co;if(isChevron||isCollapsed){const sectionId=section.getAttribute("data-id");this.reactive.dispatch("sectionContentCollapsed",[sectionId],!isCollapsed)}}}_allSectionToggler(event){var _course$sectionlist;event.preventDefault();const isAllCollapsed=event.target.closest(this.selectors.TOGGLEALL).classList.contains(this.classes.COLLAPSED),course=this.reactive.get("course");this.reactive.dispatch("sectionContentCollapsed",null!==(_course$sectionlist=course.sectionlist)&&void 0!==_course$sectionlist?_course$sectionlist:[],!isAllCollapsed)}getWatchers(){return this.reactive.sectionReturn=this.sectionReturn,this.reactive.supportComponents?[{watch:"cm.visible:updated",handler:this._reloadCm},{watch:"cm.stealth:updated",handler:this._reloadCm},{watch:"cm.sectionid:updated",handler:this._reloadCm},{watch:"cm.indent:updated",handler:this._reloadCm},{watch:"section.number:updated",handler:this._refreshSectionNumber},{watch:"section.contentcollapsed:updated",handler:this._refreshSectionCollapsed},{watch:"transaction:start",handler:this._startProcessing},{watch:"course.sectionlist:updated",handler:this._refreshCourseSectionlist},{watch:"section.cmlist:updated",handler:this._refreshSectionCmlist},{watch:"state:updated",handler:this._indexContents}]:[]}_refreshSectionCollapsed(_ref){var _toggler$classList$co2;let{state:state,element:element}=_ref;const target=this.getElement(this.selectors.SECTION,element.id);if(!target)throw new Error("Unknown section with ID ".concat(element.id));const toggler=target.querySelector(this.selectors.COLLAPSE),isCollapsed=null!==(_toggler$classList$co2=null==toggler?void 0:toggler.classList.contains(this.classes.COLLAPSED))&&void 0!==_toggler$classList$co2&&_toggler$classList$co2;if(element.contentcollapsed!==isCollapsed){var _toggler$dataset$targ;let collapsibleId=null!==(_toggler$dataset$targ=toggler.dataset.target)&&void 0!==_toggler$dataset$targ?_toggler$dataset$targ:toggler.getAttribute("href");if(!collapsibleId)return;collapsibleId=collapsibleId.replace("#","");const collapsible=document.getElementById(collapsibleId);if(!collapsible)return;(0,_jquery.default)(collapsible).collapse(element.contentcollapsed?"hide":"show")}this._refreshAllSectionsToggler(state)}_refreshAllSectionsToggler(state){const target=this.getElement(this.selectors.TOGGLEALL);if(!target)return;let allcollapsed=!0,allexpanded=!0;state.section.forEach((section=>{allcollapsed=allcollapsed&§ion.contentcollapsed,allexpanded=allexpanded&&!section.contentcollapsed})),allcollapsed&&(target.classList.add(this.classes.COLLAPSED),target.setAttribute("aria-expanded",!1)),allexpanded&&(target.classList.remove(this.classes.COLLAPSED),target.setAttribute("aria-expanded",!0))}_startProcessing(){this.dettachedCms={},this.dettachedSections={}}_completionHandler(_ref2){let{detail:detail}=_ref2;void 0!==detail&&this.reactive.dispatch("cmCompletion",[detail.cmid],detail.completed)}_scrollHandler(){const pageOffset=document.querySelector(this.selectors.PAGE).scrollTop,items=this.reactive.getExporter().allItemsArray(this.reactive.state);let pageItem=null;items.every((item=>{const index="section"===item.type?this.sections:this.cms;if(void 0===index[item.id])return!0;const element=index[item.id].element;return"cm"!==item.type||item.url||this.reactive.isEditing?(pageItem=item,pageOffset>=element.offsetTop):pageOffset>=element.offsetTop})),pageItem&&this.reactive.dispatch("setPageItem",pageItem.type,pageItem.id)}_refreshSectionNumber(_ref3){let{element:element}=_ref3;const target=this.getElement(this.selectors.SECTION,element.id);if(!target)return;target.id="section-".concat(element.number),target.dataset.sectionid=element.number,target.dataset.number=element.number;const inplace=_inplace_editable.default.getInplaceEditable(target.querySelector(this.selectors.SECTION_ITEM));if(inplace){const currentvalue=inplace.getValue(),currentitemid=inplace.getItemId();""===inplace.getValue()&&(currentitemid!=element.id||currentvalue==element.rawtitle&&""!=element.rawtitle||inplace.setValue(element.rawtitle))}}_refreshSectionCmlist(_ref4){var _element$cmlist;let{element:element}=_ref4;const cmlist=null!==(_element$cmlist=element.cmlist)&&void 0!==_element$cmlist?_element$cmlist:[],section=this.getElement(this.selectors.SECTION,element.id),listparent=null==section?void 0:section.querySelector(this.selectors.SECTION_CMLIST),createCm=this._createCmItem.bind(this);listparent&&this._fixOrder(listparent,cmlist,this.selectors.CM,this.dettachedCms,createCm)}_refreshCourseSectionlist(_ref5){var _element$sectionlist;let{element:element}=_ref5;if(0!=this.reactive.sectionReturn)return;const sectionlist=null!==(_element$sectionlist=element.sectionlist)&&void 0!==_element$sectionlist?_element$sectionlist:[],listparent=this.getElement(this.selectors.COURSE_SECTIONLIST),createSection=this._createSectionItem.bind(this);listparent&&this._fixOrder(listparent,sectionlist,this.selectors.SECTION,this.dettachedSections,createSection)}_indexContents(){this._scanIndex(this.selectors.SECTION,this.sections,(item=>new _section.default(item))),this._scanIndex(this.selectors.CM,this.cms,(item=>new _cmitem.default(item)))}_scanIndex(selector,index,creationhandler){this.getElements("".concat(selector,":not([data-indexed])")).forEach((item=>{var _item$dataset;null!=item&&null!==(_item$dataset=item.dataset)&&void 0!==_item$dataset&&_item$dataset.id&&(void 0!==index[item.dataset.id]&&index[item.dataset.id].unregister(),index[item.dataset.id]=creationhandler({...this,element:item}),item.dataset.indexed=!0)}))}_reloadCm(_ref6){let{element:element}=_ref6;if(!this.getElement(this.selectors.CM,element.id))return;this._getDebouncedReloadCm(element.id)()}_getDebouncedReloadCm(cmId){const pendingKey="courseformat/content:reloadCm_".concat(cmId);let debouncedReload=this.debouncedReloads.get(pendingKey);if(debouncedReload)return debouncedReload;const pendingReload=new _pending.default(pendingKey);return debouncedReload=(0,_utils.debounce)((()=>{var _this$reactive$sectio;this.debouncedReloads.delete(pendingKey);const cmitem=this.getElement(this.selectors.CM,cmId);if(!cmitem)return;_fragment.default.loadFragment("core_courseformat","cmitem",_config.default.courseContextId,{id:cmId,courseid:_config.default.courseId,sr:null!==(_this$reactive$sectio=this.reactive.sectionReturn)&&void 0!==_this$reactive$sectio?_this$reactive$sectio:0}).then(((html,js)=>{_templates.default.replaceNode(cmitem,html,js),this._indexContents(),pendingReload.resolve()})).catch()}),200),this.debouncedReloads.set(pendingKey,debouncedReload),debouncedReload}_reloadSection(_ref7){let{element:element}=_ref7;const pendingReload=new _pending.default("courseformat/content:reloadSection_".concat(element.id)),sectionitem=this.getElement(this.selectors.SECTION,element.id);if(sectionitem){var _this$reactive$sectio2;_fragment.default.loadFragment("core_courseformat","section",_config.default.courseContextId,{id:element.id,courseid:_config.default.courseId,sr:null!==(_this$reactive$sectio2=this.reactive.sectionReturn)&&void 0!==_this$reactive$sectio2?_this$reactive$sectio2:0}).then(((html,js)=>{_templates.default.replaceNode(sectionitem,html,js),this._indexContents(),pendingReload.resolve()})).catch()}}_createCmItem(container,cmid){const newItem=document.createElement(this.selectors.ACTIVITYTAG);return newItem.dataset.for="cmitem",newItem.dataset.id=cmid,newItem.id="module-".concat(cmid),newItem.classList.add(this.classes.ACTIVITY),container.append(newItem),this._reloadCm({element:this.reactive.get("cm",cmid)}),newItem}_createSectionItem(container,sectionid){const section=this.reactive.get("section",sectionid),newItem=document.createElement(this.selectors.SECTIONTAG);return newItem.dataset.for="section",newItem.dataset.id=sectionid,newItem.dataset.number=section.number,newItem.id="section-".concat(sectionid),newItem.classList.add(this.classes.SECTION),container.append(newItem),this._reloadSection({element:section}),newItem}async _fixOrder(container,neworder,selector,dettachedelements,createMethod){if(void 0===container)return;if(!neworder.length)return container.classList.add("hidden"),void(container.innerHTML="");let dndFakeActivity;for(container.classList.remove("hidden"),neworder.forEach(((itemid,index)=>{var _ref8,_this$getElement;let item=null!==(_ref8=null!==(_this$getElement=this.getElement(selector,itemid))&&void 0!==_this$getElement?_this$getElement:dettachedelements[itemid])&&void 0!==_ref8?_ref8:createMethod(container,itemid);if(void 0===item)return;const currentitem=container.children[index];void 0!==currentitem?currentitem!==item&&container.insertBefore(item,currentitem):container.append(item)}));container.children.length>neworder.length;){var _lastchild$classList;const lastchild=container.lastChild;var _lastchild$dataset$id,_lastchild$dataset;if(null!=lastchild&&null!==(_lastchild$classList=lastchild.classList)&&void 0!==_lastchild$classList&&_lastchild$classList.contains("dndupload-preview"))dndFakeActivity=lastchild;else dettachedelements[null!==(_lastchild$dataset$id=null==lastchild||null===(_lastchild$dataset=lastchild.dataset)||void 0===_lastchild$dataset?void 0:_lastchild$dataset.id)&&void 0!==_lastchild$dataset$id?_lastchild$dataset$id:0]=lastchild;container.removeChild(lastchild)}dndFakeActivity&&container.append(dndFakeActivity)}}return _exports.default=Component,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_config=_interopRequireDefault(_config),_inplace_editable=_interopRequireDefault(_inplace_editable),_section=_interopRequireDefault(_section),_cmitem=_interopRequireDefault(_cmitem),_fragment=_interopRequireDefault(_fragment),_templates=_interopRequireDefault(_templates),_actions=_interopRequireDefault(_actions),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending);class Component extends _reactive.BaseComponent{create(descriptor){var _descriptor$sectionRe;this.name="course_format",this.selectors={SECTION:"[data-for='section']",SECTION_ITEM:"[data-for='section_title']",SECTION_CMLIST:"[data-for='cmlist']",COURSE_SECTIONLIST:"[data-for='course_sectionlist']",CM:"[data-for='cmitem']",PAGE:"#page",TOGGLER:'[data-action="togglecoursecontentsection"]',COLLAPSE:'[data-toggle="collapse"]',TOGGLEALL:'[data-toggle="toggleall"]',ACTIVITYTAG:"li",SECTIONTAG:"li"},this.classes={COLLAPSED:"collapsed",ACTIVITY:"activity",STATEDREADY:"stateready",SECTION:"section"},this.dettachedCms={},this.dettachedSections={},this.sections={},this.cms={},this.sectionReturn=null!==(_descriptor$sectionRe=descriptor.sectionReturn)&&void 0!==_descriptor$sectionRe?_descriptor$sectionRe:0,this.debouncedReloads=new Map}static init(target,selectors,sectionReturn){return new Component({element:document.getElementById(target),reactive:(0,_courseeditor.getCurrentCourseEditor)(),selectors:selectors,sectionReturn:sectionReturn})}stateReady(state){this._indexContents(),this.addEventListener(this.element,"click",this._sectionTogglers);const toogleAll=this.getElement(this.selectors.TOGGLEALL);if(toogleAll){const collapseElementIds=[...this.getElements(this.selectors.COLLAPSE)].map((element=>element.id));toogleAll.setAttribute("aria-controls",collapseElementIds.join(" ")),this.addEventListener(toogleAll,"click",this._allSectionToggler),this.addEventListener(toogleAll,"keydown",(e=>{" "===e.key&&this._allSectionToggler(e)})),this._refreshAllSectionsToggler(state)}this.reactive.supportComponents&&(this.reactive.isEditing&&new _actions.default(this),this.element.classList.add(this.classes.STATEDREADY)),this.addEventListener(this.element,CourseEvents.manualCompletionToggled,this._completionHandler),this.addEventListener(document.querySelector(this.selectors.PAGE),"scroll",this._scrollHandler)}_sectionTogglers(event){const sectionlink=event.target.closest(this.selectors.TOGGLER),closestCollapse=event.target.closest(this.selectors.COLLAPSE),isChevron=null==closestCollapse?void 0:closestCollapse.closest(this.selectors.SECTION_ITEM);if(sectionlink||isChevron){var _toggler$classList$co;const section=event.target.closest(this.selectors.SECTION),toggler=section.querySelector(this.selectors.COLLAPSE),isCollapsed=null!==(_toggler$classList$co=null==toggler?void 0:toggler.classList.contains(this.classes.COLLAPSED))&&void 0!==_toggler$classList$co&&_toggler$classList$co;if(isChevron||isCollapsed){const sectionId=section.getAttribute("data-id");this.reactive.dispatch("sectionContentCollapsed",[sectionId],!isCollapsed)}}}_allSectionToggler(event){var _course$sectionlist;event.preventDefault();const isAllCollapsed=event.target.closest(this.selectors.TOGGLEALL).classList.contains(this.classes.COLLAPSED),course=this.reactive.get("course");this.reactive.dispatch("sectionContentCollapsed",null!==(_course$sectionlist=course.sectionlist)&&void 0!==_course$sectionlist?_course$sectionlist:[],!isAllCollapsed)}getWatchers(){return this.reactive.sectionReturn=this.sectionReturn,this.reactive.supportComponents?[{watch:"cm.visible:updated",handler:this._reloadCm},{watch:"cm.stealth:updated",handler:this._reloadCm},{watch:"cm.sectionid:updated",handler:this._reloadCm},{watch:"cm.indent:updated",handler:this._reloadCm},{watch:"cm.groupmode:updated",handler:this._reloadCm},{watch:"section.number:updated",handler:this._refreshSectionNumber},{watch:"section.contentcollapsed:updated",handler:this._refreshSectionCollapsed},{watch:"transaction:start",handler:this._startProcessing},{watch:"course.sectionlist:updated",handler:this._refreshCourseSectionlist},{watch:"section.cmlist:updated",handler:this._refreshSectionCmlist},{watch:"state:updated",handler:this._indexContents}]:[]}_refreshSectionCollapsed(_ref){var _toggler$classList$co2;let{state:state,element:element}=_ref;const target=this.getElement(this.selectors.SECTION,element.id);if(!target)throw new Error("Unknown section with ID ".concat(element.id));const toggler=target.querySelector(this.selectors.COLLAPSE),isCollapsed=null!==(_toggler$classList$co2=null==toggler?void 0:toggler.classList.contains(this.classes.COLLAPSED))&&void 0!==_toggler$classList$co2&&_toggler$classList$co2;if(element.contentcollapsed!==isCollapsed){var _toggler$dataset$targ;let collapsibleId=null!==(_toggler$dataset$targ=toggler.dataset.target)&&void 0!==_toggler$dataset$targ?_toggler$dataset$targ:toggler.getAttribute("href");if(!collapsibleId)return;collapsibleId=collapsibleId.replace("#","");const collapsible=document.getElementById(collapsibleId);if(!collapsible)return;(0,_jquery.default)(collapsible).collapse(element.contentcollapsed?"hide":"show")}this._refreshAllSectionsToggler(state)}_refreshAllSectionsToggler(state){const target=this.getElement(this.selectors.TOGGLEALL);if(!target)return;let allcollapsed=!0,allexpanded=!0;state.section.forEach((section=>{allcollapsed=allcollapsed&§ion.contentcollapsed,allexpanded=allexpanded&&!section.contentcollapsed})),allcollapsed&&(target.classList.add(this.classes.COLLAPSED),target.setAttribute("aria-expanded",!1)),allexpanded&&(target.classList.remove(this.classes.COLLAPSED),target.setAttribute("aria-expanded",!0))}_startProcessing(){this.dettachedCms={},this.dettachedSections={}}_completionHandler(_ref2){let{detail:detail}=_ref2;void 0!==detail&&this.reactive.dispatch("cmCompletion",[detail.cmid],detail.completed)}_scrollHandler(){const pageOffset=document.querySelector(this.selectors.PAGE).scrollTop,items=this.reactive.getExporter().allItemsArray(this.reactive.state);let pageItem=null;items.every((item=>{const index="section"===item.type?this.sections:this.cms;if(void 0===index[item.id])return!0;const element=index[item.id].element;return"cm"!==item.type||item.url||this.reactive.isEditing?(pageItem=item,pageOffset>=element.offsetTop):pageOffset>=element.offsetTop})),pageItem&&this.reactive.dispatch("setPageItem",pageItem.type,pageItem.id)}_refreshSectionNumber(_ref3){let{element:element}=_ref3;const target=this.getElement(this.selectors.SECTION,element.id);if(!target)return;target.id="section-".concat(element.number),target.dataset.sectionid=element.number,target.dataset.number=element.number;const inplace=_inplace_editable.default.getInplaceEditable(target.querySelector(this.selectors.SECTION_ITEM));if(inplace){const currentvalue=inplace.getValue(),currentitemid=inplace.getItemId();""===inplace.getValue()&&(currentitemid!=element.id||currentvalue==element.rawtitle&&""!=element.rawtitle||inplace.setValue(element.rawtitle))}}_refreshSectionCmlist(_ref4){var _element$cmlist;let{element:element}=_ref4;const cmlist=null!==(_element$cmlist=element.cmlist)&&void 0!==_element$cmlist?_element$cmlist:[],section=this.getElement(this.selectors.SECTION,element.id),listparent=null==section?void 0:section.querySelector(this.selectors.SECTION_CMLIST),createCm=this._createCmItem.bind(this);listparent&&this._fixOrder(listparent,cmlist,this.selectors.CM,this.dettachedCms,createCm)}_refreshCourseSectionlist(_ref5){var _element$sectionlist;let{element:element}=_ref5;if(0!=this.reactive.sectionReturn)return;const sectionlist=null!==(_element$sectionlist=element.sectionlist)&&void 0!==_element$sectionlist?_element$sectionlist:[],listparent=this.getElement(this.selectors.COURSE_SECTIONLIST),createSection=this._createSectionItem.bind(this);listparent&&this._fixOrder(listparent,sectionlist,this.selectors.SECTION,this.dettachedSections,createSection)}_indexContents(){this._scanIndex(this.selectors.SECTION,this.sections,(item=>new _section.default(item))),this._scanIndex(this.selectors.CM,this.cms,(item=>new _cmitem.default(item)))}_scanIndex(selector,index,creationhandler){this.getElements("".concat(selector,":not([data-indexed])")).forEach((item=>{var _item$dataset;null!=item&&null!==(_item$dataset=item.dataset)&&void 0!==_item$dataset&&_item$dataset.id&&(void 0!==index[item.dataset.id]&&index[item.dataset.id].unregister(),index[item.dataset.id]=creationhandler({...this,element:item}),item.dataset.indexed=!0)}))}_reloadCm(_ref6){let{element:element}=_ref6;if(!this.getElement(this.selectors.CM,element.id))return;this._getDebouncedReloadCm(element.id)()}_getDebouncedReloadCm(cmId){const pendingKey="courseformat/content:reloadCm_".concat(cmId);let debouncedReload=this.debouncedReloads.get(pendingKey);if(debouncedReload)return debouncedReload;const pendingReload=new _pending.default(pendingKey);return debouncedReload=(0,_utils.debounce)((()=>{var _this$reactive$sectio;this.debouncedReloads.delete(pendingKey);const cmitem=this.getElement(this.selectors.CM,cmId);if(!cmitem)return;_fragment.default.loadFragment("core_courseformat","cmitem",_config.default.courseContextId,{id:cmId,courseid:_config.default.courseId,sr:null!==(_this$reactive$sectio=this.reactive.sectionReturn)&&void 0!==_this$reactive$sectio?_this$reactive$sectio:0}).then(((html,js)=>{_templates.default.replaceNode(cmitem,html,js),this._indexContents(),pendingReload.resolve()})).catch()}),200),this.debouncedReloads.set(pendingKey,debouncedReload),debouncedReload}_reloadSection(_ref7){let{element:element}=_ref7;const pendingReload=new _pending.default("courseformat/content:reloadSection_".concat(element.id)),sectionitem=this.getElement(this.selectors.SECTION,element.id);if(sectionitem){var _this$reactive$sectio2;_fragment.default.loadFragment("core_courseformat","section",_config.default.courseContextId,{id:element.id,courseid:_config.default.courseId,sr:null!==(_this$reactive$sectio2=this.reactive.sectionReturn)&&void 0!==_this$reactive$sectio2?_this$reactive$sectio2:0}).then(((html,js)=>{_templates.default.replaceNode(sectionitem,html,js),this._indexContents(),pendingReload.resolve()})).catch()}}_createCmItem(container,cmid){const newItem=document.createElement(this.selectors.ACTIVITYTAG);return newItem.dataset.for="cmitem",newItem.dataset.id=cmid,newItem.id="module-".concat(cmid),newItem.classList.add(this.classes.ACTIVITY),container.append(newItem),this._reloadCm({element:this.reactive.get("cm",cmid)}),newItem}_createSectionItem(container,sectionid){const section=this.reactive.get("section",sectionid),newItem=document.createElement(this.selectors.SECTIONTAG);return newItem.dataset.for="section",newItem.dataset.id=sectionid,newItem.dataset.number=section.number,newItem.id="section-".concat(sectionid),newItem.classList.add(this.classes.SECTION),container.append(newItem),this._reloadSection({element:section}),newItem}async _fixOrder(container,neworder,selector,dettachedelements,createMethod){if(void 0===container)return;if(!neworder.length)return container.classList.add("hidden"),void(container.innerHTML="");let dndFakeActivity;for(container.classList.remove("hidden"),neworder.forEach(((itemid,index)=>{var _ref8,_this$getElement;let item=null!==(_ref8=null!==(_this$getElement=this.getElement(selector,itemid))&&void 0!==_this$getElement?_this$getElement:dettachedelements[itemid])&&void 0!==_ref8?_ref8:createMethod(container,itemid);if(void 0===item)return;const currentitem=container.children[index];void 0!==currentitem?currentitem!==item&&container.insertBefore(item,currentitem):container.append(item)}));container.children.length>neworder.length;){var _lastchild$classList;const lastchild=container.lastChild;var _lastchild$dataset$id,_lastchild$dataset;if(null!=lastchild&&null!==(_lastchild$classList=lastchild.classList)&&void 0!==_lastchild$classList&&_lastchild$classList.contains("dndupload-preview"))dndFakeActivity=lastchild;else dettachedelements[null!==(_lastchild$dataset$id=null==lastchild||null===(_lastchild$dataset=lastchild.dataset)||void 0===_lastchild$dataset?void 0:_lastchild$dataset.id)&&void 0!==_lastchild$dataset$id?_lastchild$dataset$id:0]=lastchild;container.removeChild(lastchild)}dndFakeActivity&&container.append(dndFakeActivity)}}return _exports.default=Component,_exports.default})); //# sourceMappingURL=content.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/content.min.js.map b/course/format/amd/build/local/content.min.js.map index ac13773d21a..83d6b2bbce8 100644 --- a/course/format/amd/build/local/content.min.js.map +++ b/course/format/amd/build/local/content.min.js.map @@ -1 +1 @@ -{"version":3,"file":"content.min.js","sources":["../../src/local/content.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Course index main component.\n *\n * @module core_courseformat/local/content\n * @class core_courseformat/local/content\n * @copyright 2020 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport {debounce} from 'core/utils';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport Config from 'core/config';\nimport inplaceeditable from 'core/inplace_editable';\nimport Section from 'core_courseformat/local/content/section';\nimport CmItem from 'core_courseformat/local/content/section/cmitem';\nimport Fragment from 'core/fragment';\nimport Templates from 'core/templates';\nimport DispatchActions from 'core_courseformat/local/content/actions';\nimport * as CourseEvents from 'core_course/events';\n// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated.\nimport jQuery from 'jquery';\nimport Pending from 'core/pending';\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n *\n * @param {Object} descriptor the component descriptor\n */\n create(descriptor) {\n // Optional component name for debugging.\n this.name = 'course_format';\n // Default query selectors.\n this.selectors = {\n SECTION: `[data-for='section']`,\n SECTION_ITEM: `[data-for='section_title']`,\n SECTION_CMLIST: `[data-for='cmlist']`,\n COURSE_SECTIONLIST: `[data-for='course_sectionlist']`,\n CM: `[data-for='cmitem']`,\n PAGE: `#page`,\n TOGGLER: `[data-action=\"togglecoursecontentsection\"]`,\n COLLAPSE: `[data-toggle=\"collapse\"]`,\n TOGGLEALL: `[data-toggle=\"toggleall\"]`,\n // Formats can override the activity tag but a default one is needed to create new elements.\n ACTIVITYTAG: 'li',\n SECTIONTAG: 'li',\n };\n // Default classes to toggle on refresh.\n this.classes = {\n COLLAPSED: `collapsed`,\n // Course content classes.\n ACTIVITY: `activity`,\n STATEDREADY: `stateready`,\n SECTION: `section`,\n };\n // Array to save dettached elements during element resorting.\n this.dettachedCms = {};\n this.dettachedSections = {};\n // Index of sections and cms components.\n this.sections = {};\n this.cms = {};\n // The page section return.\n this.sectionReturn = descriptor.sectionReturn ?? 0;\n this.debouncedReloads = new Map();\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @param {number} sectionReturn the content section return\n * @return {Component}\n */\n static init(target, selectors, sectionReturn) {\n return new Component({\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n selectors,\n sectionReturn,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data\n */\n stateReady(state) {\n this._indexContents();\n // Activate section togglers.\n this.addEventListener(this.element, 'click', this._sectionTogglers);\n\n // Collapse/Expand all sections button.\n const toogleAll = this.getElement(this.selectors.TOGGLEALL);\n if (toogleAll) {\n\n // Ensure collapse menu button adds aria-controls attribute referring to each collapsible element.\n const collapseElements = this.getElements(this.selectors.COLLAPSE);\n const collapseElementIds = [...collapseElements].map(element => element.id);\n toogleAll.setAttribute('aria-controls', collapseElementIds.join(' '));\n\n this.addEventListener(toogleAll, 'click', this._allSectionToggler);\n this.addEventListener(toogleAll, 'keydown', e => {\n // Collapse/expand all sections when Space key is pressed on the toggle button.\n if (e.key === ' ') {\n this._allSectionToggler(e);\n }\n });\n this._refreshAllSectionsToggler(state);\n }\n\n if (this.reactive.supportComponents) {\n // Actions are only available in edit mode.\n if (this.reactive.isEditing) {\n new DispatchActions(this);\n }\n\n // Mark content as state ready.\n this.element.classList.add(this.classes.STATEDREADY);\n }\n\n // Capture completion events.\n this.addEventListener(\n this.element,\n CourseEvents.manualCompletionToggled,\n this._completionHandler\n );\n\n // Capture page scroll to update page item.\n this.addEventListener(\n document.querySelector(this.selectors.PAGE),\n \"scroll\",\n this._scrollHandler\n );\n }\n\n /**\n * Setup sections toggler.\n *\n * Toggler click is delegated to the main course content element because new sections can\n * appear at any moment and this way we prevent accidental double bindings.\n *\n * @param {Event} event the triggered event\n */\n _sectionTogglers(event) {\n const sectionlink = event.target.closest(this.selectors.TOGGLER);\n const closestCollapse = event.target.closest(this.selectors.COLLAPSE);\n // Assume that chevron is the only collapse toggler in a section heading;\n // I think this is the most efficient way to verify at the moment.\n const isChevron = closestCollapse?.closest(this.selectors.SECTION_ITEM);\n\n if (sectionlink || isChevron) {\n\n const section = event.target.closest(this.selectors.SECTION);\n const toggler = section.querySelector(this.selectors.COLLAPSE);\n const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n if (isChevron || isCollapsed) {\n // Update the state.\n const sectionId = section.getAttribute('data-id');\n this.reactive.dispatch(\n 'sectionContentCollapsed',\n [sectionId],\n !isCollapsed\n );\n }\n }\n }\n\n /**\n * Handle the collapse/expand all sections button.\n *\n * Toggler click is delegated to the main course content element because new sections can\n * appear at any moment and this way we prevent accidental double bindings.\n *\n * @param {Event} event the triggered event\n */\n _allSectionToggler(event) {\n event.preventDefault();\n\n const target = event.target.closest(this.selectors.TOGGLEALL);\n const isAllCollapsed = target.classList.contains(this.classes.COLLAPSED);\n\n const course = this.reactive.get('course');\n this.reactive.dispatch(\n 'sectionContentCollapsed',\n course.sectionlist ?? [],\n !isAllCollapsed\n );\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n // Section return is a global page variable but most formats define it just before start printing\n // the course content. This is the reason why we define this page setting here.\n this.reactive.sectionReturn = this.sectionReturn;\n\n // Check if the course format is compatible with reactive components.\n if (!this.reactive.supportComponents) {\n return [];\n }\n return [\n // State changes that require to reload some course modules.\n {watch: `cm.visible:updated`, handler: this._reloadCm},\n {watch: `cm.stealth:updated`, handler: this._reloadCm},\n {watch: `cm.sectionid:updated`, handler: this._reloadCm},\n {watch: `cm.indent:updated`, handler: this._reloadCm},\n // Update section number and title.\n {watch: `section.number:updated`, handler: this._refreshSectionNumber},\n // Collapse and expand sections.\n {watch: `section.contentcollapsed:updated`, handler: this._refreshSectionCollapsed},\n // Sections and cm sorting.\n {watch: `transaction:start`, handler: this._startProcessing},\n {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionlist},\n {watch: `section.cmlist:updated`, handler: this._refreshSectionCmlist},\n // Reindex sections and cms.\n {watch: `state:updated`, handler: this._indexContents},\n ];\n }\n\n /**\n * Update section collapsed state via bootstrap 4 if necessary.\n *\n * Formats that do not use bootstrap 4 must override this method in order to keep the section\n * toggling working.\n *\n * @param {object} args\n * @param {Object} args.state The state data\n * @param {Object} args.element The element to update\n */\n _refreshSectionCollapsed({state, element}) {\n const target = this.getElement(this.selectors.SECTION, element.id);\n if (!target) {\n throw new Error(`Unknown section with ID ${element.id}`);\n }\n // Check if it is already done.\n const toggler = target.querySelector(this.selectors.COLLAPSE);\n const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n if (element.contentcollapsed !== isCollapsed) {\n let collapsibleId = toggler.dataset.target ?? toggler.getAttribute(\"href\");\n if (!collapsibleId) {\n return;\n }\n collapsibleId = collapsibleId.replace('#', '');\n const collapsible = document.getElementById(collapsibleId);\n if (!collapsible) {\n return;\n }\n\n // Course index is based on Bootstrap 4 collapsibles. To collapse them we need jQuery to\n // interact with collapsibles methods. Hopefully, this will change in Bootstrap 5 because\n // it does not require jQuery anymore (when MDL-71979 is integrated).\n jQuery(collapsible).collapse(element.contentcollapsed ? 'hide' : 'show');\n }\n\n this._refreshAllSectionsToggler(state);\n }\n\n /**\n * Refresh the collapse/expand all sections element.\n *\n * @param {Object} state The state data\n */\n _refreshAllSectionsToggler(state) {\n const target = this.getElement(this.selectors.TOGGLEALL);\n if (!target) {\n return;\n }\n // Check if we have all sections collapsed/expanded.\n let allcollapsed = true;\n let allexpanded = true;\n state.section.forEach(\n section => {\n allcollapsed = allcollapsed && section.contentcollapsed;\n allexpanded = allexpanded && !section.contentcollapsed;\n }\n );\n if (allcollapsed) {\n target.classList.add(this.classes.COLLAPSED);\n target.setAttribute('aria-expanded', false);\n }\n if (allexpanded) {\n target.classList.remove(this.classes.COLLAPSED);\n target.setAttribute('aria-expanded', true);\n }\n }\n\n /**\n * Setup the component to start a transaction.\n *\n * Some of the course actions replaces the current DOM element with a new one before updating the\n * course state. This means the component cannot preload any index properly until the transaction starts.\n *\n */\n _startProcessing() {\n // During a section or cm sorting, some elements could be dettached from the DOM and we\n // need to store somewhare in case they are needed later.\n this.dettachedCms = {};\n this.dettachedSections = {};\n }\n\n /**\n * Activity manual completion listener.\n *\n * @param {Event} event the custom ecent\n */\n _completionHandler({detail}) {\n if (detail === undefined) {\n return;\n }\n this.reactive.dispatch('cmCompletion', [detail.cmid], detail.completed);\n }\n\n /**\n * Check the current page scroll and update the active element if necessary.\n */\n _scrollHandler() {\n const pageOffset = document.querySelector(this.selectors.PAGE).scrollTop;\n const items = this.reactive.getExporter().allItemsArray(this.reactive.state);\n // Check what is the active element now.\n let pageItem = null;\n items.every(item => {\n const index = (item.type === 'section') ? this.sections : this.cms;\n if (index[item.id] === undefined) {\n return true;\n }\n\n const element = index[item.id].element;\n // Activities without url can only be page items in edit mode.\n if (item.type === 'cm' && !item.url && !this.reactive.isEditing) {\n return pageOffset >= element.offsetTop;\n }\n pageItem = item;\n return pageOffset >= element.offsetTop;\n });\n if (pageItem) {\n this.reactive.dispatch('setPageItem', pageItem.type, pageItem.id);\n }\n }\n\n /**\n * Update a course section when the section number changes.\n *\n * The courseActions module used for most course section tools still depends on css classes and\n * section numbers (not id). To prevent inconsistencies when a section is moved, we need to refresh\n * the\n *\n * Course formats can override the section title rendering so the frontend depends heavily on backend\n * rendering. Luckily in edit mode we can trigger a title update using the inplace_editable module.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details.\n */\n _refreshSectionNumber({element}) {\n // Find the element.\n const target = this.getElement(this.selectors.SECTION, element.id);\n if (!target) {\n // Job done. Nothing to refresh.\n return;\n }\n // Update section numbers in all data, css and YUI attributes.\n target.id = `section-${element.number}`;\n // YUI uses section number as section id in data-sectionid, in principle if a format use components\n // don't need this sectionid attribute anymore, but we keep the compatibility in case some plugin\n // use it for legacy purposes.\n target.dataset.sectionid = element.number;\n // The data-number is the attribute used by components to store the section number.\n target.dataset.number = element.number;\n\n // Update title and title inplace editable, if any.\n const inplace = inplaceeditable.getInplaceEditable(target.querySelector(this.selectors.SECTION_ITEM));\n if (inplace) {\n // The course content HTML can be modified at any moment, so the function need to do some checkings\n // to make sure the inplace editable still represents the same itemid.\n const currentvalue = inplace.getValue();\n const currentitemid = inplace.getItemId();\n // Unnamed sections must be recalculated.\n if (inplace.getValue() === '') {\n // The value to send can be an empty value if it is a default name.\n if (currentitemid == element.id && (currentvalue != element.rawtitle || element.rawtitle == '')) {\n inplace.setValue(element.rawtitle);\n }\n }\n }\n }\n\n /**\n * Refresh a section cm list.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details.\n */\n _refreshSectionCmlist({element}) {\n const cmlist = element.cmlist ?? [];\n const section = this.getElement(this.selectors.SECTION, element.id);\n const listparent = section?.querySelector(this.selectors.SECTION_CMLIST);\n // A method to create a fake element to be replaced when the item is ready.\n const createCm = this._createCmItem.bind(this);\n if (listparent) {\n this._fixOrder(listparent, cmlist, this.selectors.CM, this.dettachedCms, createCm);\n }\n }\n\n /**\n * Refresh the section list.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details.\n */\n _refreshCourseSectionlist({element}) {\n // If we have a section return means we only show a single section so no need to fix order.\n if (this.reactive.sectionReturn != 0) {\n return;\n }\n const sectionlist = element.sectionlist ?? [];\n const listparent = this.getElement(this.selectors.COURSE_SECTIONLIST);\n // For now section cannot be created at a frontend level.\n const createSection = this._createSectionItem.bind(this);\n if (listparent) {\n this._fixOrder(listparent, sectionlist, this.selectors.SECTION, this.dettachedSections, createSection);\n }\n }\n\n /**\n * Regenerate content indexes.\n *\n * This method is used when a legacy action refresh some content element.\n */\n _indexContents() {\n // Find unindexed sections.\n this._scanIndex(\n this.selectors.SECTION,\n this.sections,\n (item) => {\n return new Section(item);\n }\n );\n\n // Find unindexed cms.\n this._scanIndex(\n this.selectors.CM,\n this.cms,\n (item) => {\n return new CmItem(item);\n }\n );\n }\n\n /**\n * Reindex a content (section or cm) of the course content.\n *\n * This method is used internally by _indexContents.\n *\n * @param {string} selector the DOM selector to scan\n * @param {*} index the index attribute to update\n * @param {*} creationhandler method to create a new indexed element\n */\n _scanIndex(selector, index, creationhandler) {\n const items = this.getElements(`${selector}:not([data-indexed])`);\n items.forEach((item) => {\n if (!item?.dataset?.id) {\n return;\n }\n // Delete previous item component.\n if (index[item.dataset.id] !== undefined) {\n index[item.dataset.id].unregister();\n }\n // Create the new component.\n index[item.dataset.id] = creationhandler({\n ...this,\n element: item,\n });\n // Mark as indexed.\n item.dataset.indexed = true;\n });\n }\n\n /**\n * Reload a course module contents.\n *\n * Most course module HTML is still strongly backend dependant.\n * Some changes require to get a new version of the module.\n *\n * @param {object} param0 the watcher details\n * @param {object} param0.element the state object\n */\n _reloadCm({element}) {\n if (!this.getElement(this.selectors.CM, element.id)) {\n return;\n }\n const debouncedReload = this._getDebouncedReloadCm(element.id);\n debouncedReload();\n }\n\n /**\n * Generate or get a reload CM debounced function.\n * @param {Number} cmId\n * @returns {Function} the debounced reload function\n */\n _getDebouncedReloadCm(cmId) {\n const pendingKey = `courseformat/content:reloadCm_${cmId}`;\n let debouncedReload = this.debouncedReloads.get(pendingKey);\n if (debouncedReload) {\n return debouncedReload;\n }\n const pendingReload = new Pending(pendingKey);\n const reload = () => {\n this.debouncedReloads.delete(pendingKey);\n const cmitem = this.getElement(this.selectors.CM, cmId);\n if (!cmitem) {\n return;\n }\n const promise = Fragment.loadFragment(\n 'core_courseformat',\n 'cmitem',\n Config.courseContextId,\n {\n id: cmId,\n courseid: Config.courseId,\n sr: this.reactive.sectionReturn ?? 0,\n }\n );\n promise.then((html, js) => {\n Templates.replaceNode(cmitem, html, js);\n this._indexContents();\n pendingReload.resolve();\n return;\n }).catch();\n };\n debouncedReload = debounce(reload, 200);\n this.debouncedReloads.set(pendingKey, debouncedReload);\n return debouncedReload;\n }\n\n /**\n * Reload a course section contents.\n *\n * Section HTML is still strongly backend dependant.\n * Some changes require to get a new version of the section.\n *\n * @param {details} param0 the watcher details\n * @param {object} param0.element the state object\n */\n _reloadSection({element}) {\n const pendingReload = new Pending(`courseformat/content:reloadSection_${element.id}`);\n const sectionitem = this.getElement(this.selectors.SECTION, element.id);\n if (sectionitem) {\n const promise = Fragment.loadFragment(\n 'core_courseformat',\n 'section',\n Config.courseContextId,\n {\n id: element.id,\n courseid: Config.courseId,\n sr: this.reactive.sectionReturn ?? 0,\n }\n );\n promise.then((html, js) => {\n Templates.replaceNode(sectionitem, html, js);\n this._indexContents();\n pendingReload.resolve();\n return;\n }).catch();\n }\n }\n\n /**\n * Create a new course module item in a section.\n *\n * Thos method will append a fake item in the container and trigger an ajax request to\n * replace the fake element by the real content.\n *\n * @param {Element} container the container element (section)\n * @param {Number} cmid the course-module ID\n * @returns {Element} the created element\n */\n _createCmItem(container, cmid) {\n const newItem = document.createElement(this.selectors.ACTIVITYTAG);\n newItem.dataset.for = 'cmitem';\n newItem.dataset.id = cmid;\n // The legacy actions.js requires a specific ID and class to refresh the CM.\n newItem.id = `module-${cmid}`;\n newItem.classList.add(this.classes.ACTIVITY);\n container.append(newItem);\n this._reloadCm({\n element: this.reactive.get('cm', cmid),\n });\n return newItem;\n }\n\n /**\n * Create a new section item.\n *\n * This method will append a fake item in the container and trigger an ajax request to\n * replace the fake element by the real content.\n *\n * @param {Element} container the container element (section)\n * @param {Number} sectionid the course-module ID\n * @returns {Element} the created element\n */\n _createSectionItem(container, sectionid) {\n const section = this.reactive.get('section', sectionid);\n const newItem = document.createElement(this.selectors.SECTIONTAG);\n newItem.dataset.for = 'section';\n newItem.dataset.id = sectionid;\n newItem.dataset.number = section.number;\n // The legacy actions.js requires a specific ID and class to refresh the section.\n newItem.id = `section-${sectionid}`;\n newItem.classList.add(this.classes.SECTION);\n container.append(newItem);\n this._reloadSection({\n element: section,\n });\n return newItem;\n }\n\n /**\n * Fix/reorder the section or cms order.\n *\n * @param {Element} container the HTML element to reorder.\n * @param {Array} neworder an array with the ids order\n * @param {string} selector the element selector\n * @param {Object} dettachedelements a list of dettached elements\n * @param {function} createMethod method to create missing elements\n */\n async _fixOrder(container, neworder, selector, dettachedelements, createMethod) {\n if (container === undefined) {\n return;\n }\n\n // Empty lists should not be visible.\n if (!neworder.length) {\n container.classList.add('hidden');\n container.innerHTML = '';\n return;\n }\n\n // Grant the list is visible (in case it was empty).\n container.classList.remove('hidden');\n\n // Move the elements in order at the beginning of the list.\n neworder.forEach((itemid, index) => {\n let item = this.getElement(selector, itemid) ?? dettachedelements[itemid] ?? createMethod(container, itemid);\n if (item === undefined) {\n // Missing elements cannot be sorted.\n return;\n }\n // Get the current elemnt at that position.\n const currentitem = container.children[index];\n if (currentitem === undefined) {\n container.append(item);\n return;\n }\n if (currentitem !== item) {\n container.insertBefore(item, currentitem);\n }\n });\n\n // Dndupload add a fake element we need to keep.\n let dndFakeActivity;\n\n // Remove the remaining elements.\n while (container.children.length > neworder.length) {\n const lastchild = container.lastChild;\n if (lastchild?.classList?.contains('dndupload-preview')) {\n dndFakeActivity = lastchild;\n } else {\n dettachedelements[lastchild?.dataset?.id ?? 0] = lastchild;\n }\n container.removeChild(lastchild);\n }\n // Restore dndupload fake element.\n if (dndFakeActivity) {\n container.append(dndFakeActivity);\n }\n }\n}\n"],"names":["Component","BaseComponent","create","descriptor","name","selectors","SECTION","SECTION_ITEM","SECTION_CMLIST","COURSE_SECTIONLIST","CM","PAGE","TOGGLER","COLLAPSE","TOGGLEALL","ACTIVITYTAG","SECTIONTAG","classes","COLLAPSED","ACTIVITY","STATEDREADY","dettachedCms","dettachedSections","sections","cms","sectionReturn","debouncedReloads","Map","target","element","document","getElementById","reactive","stateReady","state","_indexContents","addEventListener","this","_sectionTogglers","toogleAll","getElement","collapseElementIds","getElements","map","id","setAttribute","join","_allSectionToggler","e","key","_refreshAllSectionsToggler","supportComponents","isEditing","DispatchActions","classList","add","CourseEvents","manualCompletionToggled","_completionHandler","querySelector","_scrollHandler","event","sectionlink","closest","closestCollapse","isChevron","section","toggler","isCollapsed","contains","sectionId","getAttribute","dispatch","preventDefault","isAllCollapsed","course","get","sectionlist","getWatchers","watch","handler","_reloadCm","_refreshSectionNumber","_refreshSectionCollapsed","_startProcessing","_refreshCourseSectionlist","_refreshSectionCmlist","Error","contentcollapsed","collapsibleId","dataset","replace","collapsible","collapse","allcollapsed","allexpanded","forEach","remove","detail","undefined","cmid","completed","pageOffset","scrollTop","items","getExporter","allItemsArray","pageItem","every","item","index","type","url","offsetTop","number","sectionid","inplace","inplaceeditable","getInplaceEditable","currentvalue","getValue","currentitemid","getItemId","rawtitle","setValue","cmlist","listparent","createCm","_createCmItem","bind","_fixOrder","createSection","_createSectionItem","_scanIndex","Section","CmItem","selector","creationhandler","_item$dataset","unregister","indexed","_getDebouncedReloadCm","debouncedReload","cmId","pendingKey","pendingReload","Pending","delete","cmitem","Fragment","loadFragment","Config","courseContextId","courseid","courseId","sr","then","html","js","replaceNode","resolve","catch","set","_reloadSection","sectionitem","container","newItem","createElement","for","append","neworder","dettachedelements","createMethod","length","innerHTML","dndFakeActivity","itemid","currentitem","children","insertBefore","lastchild","lastChild","_lastchild$classList","_lastchild$dataset","removeChild"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"content.min.js","sources":["../../src/local/content.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Course index main component.\n *\n * @module core_courseformat/local/content\n * @class core_courseformat/local/content\n * @copyright 2020 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport {debounce} from 'core/utils';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport Config from 'core/config';\nimport inplaceeditable from 'core/inplace_editable';\nimport Section from 'core_courseformat/local/content/section';\nimport CmItem from 'core_courseformat/local/content/section/cmitem';\nimport Fragment from 'core/fragment';\nimport Templates from 'core/templates';\nimport DispatchActions from 'core_courseformat/local/content/actions';\nimport * as CourseEvents from 'core_course/events';\n// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated.\nimport jQuery from 'jquery';\nimport Pending from 'core/pending';\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n *\n * @param {Object} descriptor the component descriptor\n */\n create(descriptor) {\n // Optional component name for debugging.\n this.name = 'course_format';\n // Default query selectors.\n this.selectors = {\n SECTION: `[data-for='section']`,\n SECTION_ITEM: `[data-for='section_title']`,\n SECTION_CMLIST: `[data-for='cmlist']`,\n COURSE_SECTIONLIST: `[data-for='course_sectionlist']`,\n CM: `[data-for='cmitem']`,\n PAGE: `#page`,\n TOGGLER: `[data-action=\"togglecoursecontentsection\"]`,\n COLLAPSE: `[data-toggle=\"collapse\"]`,\n TOGGLEALL: `[data-toggle=\"toggleall\"]`,\n // Formats can override the activity tag but a default one is needed to create new elements.\n ACTIVITYTAG: 'li',\n SECTIONTAG: 'li',\n };\n // Default classes to toggle on refresh.\n this.classes = {\n COLLAPSED: `collapsed`,\n // Course content classes.\n ACTIVITY: `activity`,\n STATEDREADY: `stateready`,\n SECTION: `section`,\n };\n // Array to save dettached elements during element resorting.\n this.dettachedCms = {};\n this.dettachedSections = {};\n // Index of sections and cms components.\n this.sections = {};\n this.cms = {};\n // The page section return.\n this.sectionReturn = descriptor.sectionReturn ?? 0;\n this.debouncedReloads = new Map();\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @param {number} sectionReturn the content section return\n * @return {Component}\n */\n static init(target, selectors, sectionReturn) {\n return new Component({\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n selectors,\n sectionReturn,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data\n */\n stateReady(state) {\n this._indexContents();\n // Activate section togglers.\n this.addEventListener(this.element, 'click', this._sectionTogglers);\n\n // Collapse/Expand all sections button.\n const toogleAll = this.getElement(this.selectors.TOGGLEALL);\n if (toogleAll) {\n\n // Ensure collapse menu button adds aria-controls attribute referring to each collapsible element.\n const collapseElements = this.getElements(this.selectors.COLLAPSE);\n const collapseElementIds = [...collapseElements].map(element => element.id);\n toogleAll.setAttribute('aria-controls', collapseElementIds.join(' '));\n\n this.addEventListener(toogleAll, 'click', this._allSectionToggler);\n this.addEventListener(toogleAll, 'keydown', e => {\n // Collapse/expand all sections when Space key is pressed on the toggle button.\n if (e.key === ' ') {\n this._allSectionToggler(e);\n }\n });\n this._refreshAllSectionsToggler(state);\n }\n\n if (this.reactive.supportComponents) {\n // Actions are only available in edit mode.\n if (this.reactive.isEditing) {\n new DispatchActions(this);\n }\n\n // Mark content as state ready.\n this.element.classList.add(this.classes.STATEDREADY);\n }\n\n // Capture completion events.\n this.addEventListener(\n this.element,\n CourseEvents.manualCompletionToggled,\n this._completionHandler\n );\n\n // Capture page scroll to update page item.\n this.addEventListener(\n document.querySelector(this.selectors.PAGE),\n \"scroll\",\n this._scrollHandler\n );\n }\n\n /**\n * Setup sections toggler.\n *\n * Toggler click is delegated to the main course content element because new sections can\n * appear at any moment and this way we prevent accidental double bindings.\n *\n * @param {Event} event the triggered event\n */\n _sectionTogglers(event) {\n const sectionlink = event.target.closest(this.selectors.TOGGLER);\n const closestCollapse = event.target.closest(this.selectors.COLLAPSE);\n // Assume that chevron is the only collapse toggler in a section heading;\n // I think this is the most efficient way to verify at the moment.\n const isChevron = closestCollapse?.closest(this.selectors.SECTION_ITEM);\n\n if (sectionlink || isChevron) {\n\n const section = event.target.closest(this.selectors.SECTION);\n const toggler = section.querySelector(this.selectors.COLLAPSE);\n const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n if (isChevron || isCollapsed) {\n // Update the state.\n const sectionId = section.getAttribute('data-id');\n this.reactive.dispatch(\n 'sectionContentCollapsed',\n [sectionId],\n !isCollapsed\n );\n }\n }\n }\n\n /**\n * Handle the collapse/expand all sections button.\n *\n * Toggler click is delegated to the main course content element because new sections can\n * appear at any moment and this way we prevent accidental double bindings.\n *\n * @param {Event} event the triggered event\n */\n _allSectionToggler(event) {\n event.preventDefault();\n\n const target = event.target.closest(this.selectors.TOGGLEALL);\n const isAllCollapsed = target.classList.contains(this.classes.COLLAPSED);\n\n const course = this.reactive.get('course');\n this.reactive.dispatch(\n 'sectionContentCollapsed',\n course.sectionlist ?? [],\n !isAllCollapsed\n );\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n // Section return is a global page variable but most formats define it just before start printing\n // the course content. This is the reason why we define this page setting here.\n this.reactive.sectionReturn = this.sectionReturn;\n\n // Check if the course format is compatible with reactive components.\n if (!this.reactive.supportComponents) {\n return [];\n }\n return [\n // State changes that require to reload some course modules.\n {watch: `cm.visible:updated`, handler: this._reloadCm},\n {watch: `cm.stealth:updated`, handler: this._reloadCm},\n {watch: `cm.sectionid:updated`, handler: this._reloadCm},\n {watch: `cm.indent:updated`, handler: this._reloadCm},\n {watch: `cm.groupmode:updated`, handler: this._reloadCm},\n // Update section number and title.\n {watch: `section.number:updated`, handler: this._refreshSectionNumber},\n // Collapse and expand sections.\n {watch: `section.contentcollapsed:updated`, handler: this._refreshSectionCollapsed},\n // Sections and cm sorting.\n {watch: `transaction:start`, handler: this._startProcessing},\n {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionlist},\n {watch: `section.cmlist:updated`, handler: this._refreshSectionCmlist},\n // Reindex sections and cms.\n {watch: `state:updated`, handler: this._indexContents},\n ];\n }\n\n /**\n * Update section collapsed state via bootstrap 4 if necessary.\n *\n * Formats that do not use bootstrap 4 must override this method in order to keep the section\n * toggling working.\n *\n * @param {object} args\n * @param {Object} args.state The state data\n * @param {Object} args.element The element to update\n */\n _refreshSectionCollapsed({state, element}) {\n const target = this.getElement(this.selectors.SECTION, element.id);\n if (!target) {\n throw new Error(`Unknown section with ID ${element.id}`);\n }\n // Check if it is already done.\n const toggler = target.querySelector(this.selectors.COLLAPSE);\n const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;\n\n if (element.contentcollapsed !== isCollapsed) {\n let collapsibleId = toggler.dataset.target ?? toggler.getAttribute(\"href\");\n if (!collapsibleId) {\n return;\n }\n collapsibleId = collapsibleId.replace('#', '');\n const collapsible = document.getElementById(collapsibleId);\n if (!collapsible) {\n return;\n }\n\n // Course index is based on Bootstrap 4 collapsibles. To collapse them we need jQuery to\n // interact with collapsibles methods. Hopefully, this will change in Bootstrap 5 because\n // it does not require jQuery anymore (when MDL-71979 is integrated).\n jQuery(collapsible).collapse(element.contentcollapsed ? 'hide' : 'show');\n }\n\n this._refreshAllSectionsToggler(state);\n }\n\n /**\n * Refresh the collapse/expand all sections element.\n *\n * @param {Object} state The state data\n */\n _refreshAllSectionsToggler(state) {\n const target = this.getElement(this.selectors.TOGGLEALL);\n if (!target) {\n return;\n }\n // Check if we have all sections collapsed/expanded.\n let allcollapsed = true;\n let allexpanded = true;\n state.section.forEach(\n section => {\n allcollapsed = allcollapsed && section.contentcollapsed;\n allexpanded = allexpanded && !section.contentcollapsed;\n }\n );\n if (allcollapsed) {\n target.classList.add(this.classes.COLLAPSED);\n target.setAttribute('aria-expanded', false);\n }\n if (allexpanded) {\n target.classList.remove(this.classes.COLLAPSED);\n target.setAttribute('aria-expanded', true);\n }\n }\n\n /**\n * Setup the component to start a transaction.\n *\n * Some of the course actions replaces the current DOM element with a new one before updating the\n * course state. This means the component cannot preload any index properly until the transaction starts.\n *\n */\n _startProcessing() {\n // During a section or cm sorting, some elements could be dettached from the DOM and we\n // need to store somewhare in case they are needed later.\n this.dettachedCms = {};\n this.dettachedSections = {};\n }\n\n /**\n * Activity manual completion listener.\n *\n * @param {Event} event the custom ecent\n */\n _completionHandler({detail}) {\n if (detail === undefined) {\n return;\n }\n this.reactive.dispatch('cmCompletion', [detail.cmid], detail.completed);\n }\n\n /**\n * Check the current page scroll and update the active element if necessary.\n */\n _scrollHandler() {\n const pageOffset = document.querySelector(this.selectors.PAGE).scrollTop;\n const items = this.reactive.getExporter().allItemsArray(this.reactive.state);\n // Check what is the active element now.\n let pageItem = null;\n items.every(item => {\n const index = (item.type === 'section') ? this.sections : this.cms;\n if (index[item.id] === undefined) {\n return true;\n }\n\n const element = index[item.id].element;\n // Activities without url can only be page items in edit mode.\n if (item.type === 'cm' && !item.url && !this.reactive.isEditing) {\n return pageOffset >= element.offsetTop;\n }\n pageItem = item;\n return pageOffset >= element.offsetTop;\n });\n if (pageItem) {\n this.reactive.dispatch('setPageItem', pageItem.type, pageItem.id);\n }\n }\n\n /**\n * Update a course section when the section number changes.\n *\n * The courseActions module used for most course section tools still depends on css classes and\n * section numbers (not id). To prevent inconsistencies when a section is moved, we need to refresh\n * the\n *\n * Course formats can override the section title rendering so the frontend depends heavily on backend\n * rendering. Luckily in edit mode we can trigger a title update using the inplace_editable module.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details.\n */\n _refreshSectionNumber({element}) {\n // Find the element.\n const target = this.getElement(this.selectors.SECTION, element.id);\n if (!target) {\n // Job done. Nothing to refresh.\n return;\n }\n // Update section numbers in all data, css and YUI attributes.\n target.id = `section-${element.number}`;\n // YUI uses section number as section id in data-sectionid, in principle if a format use components\n // don't need this sectionid attribute anymore, but we keep the compatibility in case some plugin\n // use it for legacy purposes.\n target.dataset.sectionid = element.number;\n // The data-number is the attribute used by components to store the section number.\n target.dataset.number = element.number;\n\n // Update title and title inplace editable, if any.\n const inplace = inplaceeditable.getInplaceEditable(target.querySelector(this.selectors.SECTION_ITEM));\n if (inplace) {\n // The course content HTML can be modified at any moment, so the function need to do some checkings\n // to make sure the inplace editable still represents the same itemid.\n const currentvalue = inplace.getValue();\n const currentitemid = inplace.getItemId();\n // Unnamed sections must be recalculated.\n if (inplace.getValue() === '') {\n // The value to send can be an empty value if it is a default name.\n if (currentitemid == element.id && (currentvalue != element.rawtitle || element.rawtitle == '')) {\n inplace.setValue(element.rawtitle);\n }\n }\n }\n }\n\n /**\n * Refresh a section cm list.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details.\n */\n _refreshSectionCmlist({element}) {\n const cmlist = element.cmlist ?? [];\n const section = this.getElement(this.selectors.SECTION, element.id);\n const listparent = section?.querySelector(this.selectors.SECTION_CMLIST);\n // A method to create a fake element to be replaced when the item is ready.\n const createCm = this._createCmItem.bind(this);\n if (listparent) {\n this._fixOrder(listparent, cmlist, this.selectors.CM, this.dettachedCms, createCm);\n }\n }\n\n /**\n * Refresh the section list.\n *\n * @param {Object} param\n * @param {Object} param.element details the update details.\n */\n _refreshCourseSectionlist({element}) {\n // If we have a section return means we only show a single section so no need to fix order.\n if (this.reactive.sectionReturn != 0) {\n return;\n }\n const sectionlist = element.sectionlist ?? [];\n const listparent = this.getElement(this.selectors.COURSE_SECTIONLIST);\n // For now section cannot be created at a frontend level.\n const createSection = this._createSectionItem.bind(this);\n if (listparent) {\n this._fixOrder(listparent, sectionlist, this.selectors.SECTION, this.dettachedSections, createSection);\n }\n }\n\n /**\n * Regenerate content indexes.\n *\n * This method is used when a legacy action refresh some content element.\n */\n _indexContents() {\n // Find unindexed sections.\n this._scanIndex(\n this.selectors.SECTION,\n this.sections,\n (item) => {\n return new Section(item);\n }\n );\n\n // Find unindexed cms.\n this._scanIndex(\n this.selectors.CM,\n this.cms,\n (item) => {\n return new CmItem(item);\n }\n );\n }\n\n /**\n * Reindex a content (section or cm) of the course content.\n *\n * This method is used internally by _indexContents.\n *\n * @param {string} selector the DOM selector to scan\n * @param {*} index the index attribute to update\n * @param {*} creationhandler method to create a new indexed element\n */\n _scanIndex(selector, index, creationhandler) {\n const items = this.getElements(`${selector}:not([data-indexed])`);\n items.forEach((item) => {\n if (!item?.dataset?.id) {\n return;\n }\n // Delete previous item component.\n if (index[item.dataset.id] !== undefined) {\n index[item.dataset.id].unregister();\n }\n // Create the new component.\n index[item.dataset.id] = creationhandler({\n ...this,\n element: item,\n });\n // Mark as indexed.\n item.dataset.indexed = true;\n });\n }\n\n /**\n * Reload a course module contents.\n *\n * Most course module HTML is still strongly backend dependant.\n * Some changes require to get a new version of the module.\n *\n * @param {object} param0 the watcher details\n * @param {object} param0.element the state object\n */\n _reloadCm({element}) {\n if (!this.getElement(this.selectors.CM, element.id)) {\n return;\n }\n const debouncedReload = this._getDebouncedReloadCm(element.id);\n debouncedReload();\n }\n\n /**\n * Generate or get a reload CM debounced function.\n * @param {Number} cmId\n * @returns {Function} the debounced reload function\n */\n _getDebouncedReloadCm(cmId) {\n const pendingKey = `courseformat/content:reloadCm_${cmId}`;\n let debouncedReload = this.debouncedReloads.get(pendingKey);\n if (debouncedReload) {\n return debouncedReload;\n }\n const pendingReload = new Pending(pendingKey);\n const reload = () => {\n this.debouncedReloads.delete(pendingKey);\n const cmitem = this.getElement(this.selectors.CM, cmId);\n if (!cmitem) {\n return;\n }\n const promise = Fragment.loadFragment(\n 'core_courseformat',\n 'cmitem',\n Config.courseContextId,\n {\n id: cmId,\n courseid: Config.courseId,\n sr: this.reactive.sectionReturn ?? 0,\n }\n );\n promise.then((html, js) => {\n Templates.replaceNode(cmitem, html, js);\n this._indexContents();\n pendingReload.resolve();\n return;\n }).catch();\n };\n debouncedReload = debounce(reload, 200);\n this.debouncedReloads.set(pendingKey, debouncedReload);\n return debouncedReload;\n }\n\n /**\n * Reload a course section contents.\n *\n * Section HTML is still strongly backend dependant.\n * Some changes require to get a new version of the section.\n *\n * @param {details} param0 the watcher details\n * @param {object} param0.element the state object\n */\n _reloadSection({element}) {\n const pendingReload = new Pending(`courseformat/content:reloadSection_${element.id}`);\n const sectionitem = this.getElement(this.selectors.SECTION, element.id);\n if (sectionitem) {\n const promise = Fragment.loadFragment(\n 'core_courseformat',\n 'section',\n Config.courseContextId,\n {\n id: element.id,\n courseid: Config.courseId,\n sr: this.reactive.sectionReturn ?? 0,\n }\n );\n promise.then((html, js) => {\n Templates.replaceNode(sectionitem, html, js);\n this._indexContents();\n pendingReload.resolve();\n return;\n }).catch();\n }\n }\n\n /**\n * Create a new course module item in a section.\n *\n * Thos method will append a fake item in the container and trigger an ajax request to\n * replace the fake element by the real content.\n *\n * @param {Element} container the container element (section)\n * @param {Number} cmid the course-module ID\n * @returns {Element} the created element\n */\n _createCmItem(container, cmid) {\n const newItem = document.createElement(this.selectors.ACTIVITYTAG);\n newItem.dataset.for = 'cmitem';\n newItem.dataset.id = cmid;\n // The legacy actions.js requires a specific ID and class to refresh the CM.\n newItem.id = `module-${cmid}`;\n newItem.classList.add(this.classes.ACTIVITY);\n container.append(newItem);\n this._reloadCm({\n element: this.reactive.get('cm', cmid),\n });\n return newItem;\n }\n\n /**\n * Create a new section item.\n *\n * This method will append a fake item in the container and trigger an ajax request to\n * replace the fake element by the real content.\n *\n * @param {Element} container the container element (section)\n * @param {Number} sectionid the course-module ID\n * @returns {Element} the created element\n */\n _createSectionItem(container, sectionid) {\n const section = this.reactive.get('section', sectionid);\n const newItem = document.createElement(this.selectors.SECTIONTAG);\n newItem.dataset.for = 'section';\n newItem.dataset.id = sectionid;\n newItem.dataset.number = section.number;\n // The legacy actions.js requires a specific ID and class to refresh the section.\n newItem.id = `section-${sectionid}`;\n newItem.classList.add(this.classes.SECTION);\n container.append(newItem);\n this._reloadSection({\n element: section,\n });\n return newItem;\n }\n\n /**\n * Fix/reorder the section or cms order.\n *\n * @param {Element} container the HTML element to reorder.\n * @param {Array} neworder an array with the ids order\n * @param {string} selector the element selector\n * @param {Object} dettachedelements a list of dettached elements\n * @param {function} createMethod method to create missing elements\n */\n async _fixOrder(container, neworder, selector, dettachedelements, createMethod) {\n if (container === undefined) {\n return;\n }\n\n // Empty lists should not be visible.\n if (!neworder.length) {\n container.classList.add('hidden');\n container.innerHTML = '';\n return;\n }\n\n // Grant the list is visible (in case it was empty).\n container.classList.remove('hidden');\n\n // Move the elements in order at the beginning of the list.\n neworder.forEach((itemid, index) => {\n let item = this.getElement(selector, itemid) ?? dettachedelements[itemid] ?? createMethod(container, itemid);\n if (item === undefined) {\n // Missing elements cannot be sorted.\n return;\n }\n // Get the current elemnt at that position.\n const currentitem = container.children[index];\n if (currentitem === undefined) {\n container.append(item);\n return;\n }\n if (currentitem !== item) {\n container.insertBefore(item, currentitem);\n }\n });\n\n // Dndupload add a fake element we need to keep.\n let dndFakeActivity;\n\n // Remove the remaining elements.\n while (container.children.length > neworder.length) {\n const lastchild = container.lastChild;\n if (lastchild?.classList?.contains('dndupload-preview')) {\n dndFakeActivity = lastchild;\n } else {\n dettachedelements[lastchild?.dataset?.id ?? 0] = lastchild;\n }\n container.removeChild(lastchild);\n }\n // Restore dndupload fake element.\n if (dndFakeActivity) {\n container.append(dndFakeActivity);\n }\n }\n}\n"],"names":["Component","BaseComponent","create","descriptor","name","selectors","SECTION","SECTION_ITEM","SECTION_CMLIST","COURSE_SECTIONLIST","CM","PAGE","TOGGLER","COLLAPSE","TOGGLEALL","ACTIVITYTAG","SECTIONTAG","classes","COLLAPSED","ACTIVITY","STATEDREADY","dettachedCms","dettachedSections","sections","cms","sectionReturn","debouncedReloads","Map","target","element","document","getElementById","reactive","stateReady","state","_indexContents","addEventListener","this","_sectionTogglers","toogleAll","getElement","collapseElementIds","getElements","map","id","setAttribute","join","_allSectionToggler","e","key","_refreshAllSectionsToggler","supportComponents","isEditing","DispatchActions","classList","add","CourseEvents","manualCompletionToggled","_completionHandler","querySelector","_scrollHandler","event","sectionlink","closest","closestCollapse","isChevron","section","toggler","isCollapsed","contains","sectionId","getAttribute","dispatch","preventDefault","isAllCollapsed","course","get","sectionlist","getWatchers","watch","handler","_reloadCm","_refreshSectionNumber","_refreshSectionCollapsed","_startProcessing","_refreshCourseSectionlist","_refreshSectionCmlist","Error","contentcollapsed","collapsibleId","dataset","replace","collapsible","collapse","allcollapsed","allexpanded","forEach","remove","detail","undefined","cmid","completed","pageOffset","scrollTop","items","getExporter","allItemsArray","pageItem","every","item","index","type","url","offsetTop","number","sectionid","inplace","inplaceeditable","getInplaceEditable","currentvalue","getValue","currentitemid","getItemId","rawtitle","setValue","cmlist","listparent","createCm","_createCmItem","bind","_fixOrder","createSection","_createSectionItem","_scanIndex","Section","CmItem","selector","creationhandler","_item$dataset","unregister","indexed","_getDebouncedReloadCm","debouncedReload","cmId","pendingKey","pendingReload","Pending","delete","cmitem","Fragment","loadFragment","Config","courseContextId","courseid","courseId","sr","then","html","js","replaceNode","resolve","catch","set","_reloadSection","sectionitem","container","newItem","createElement","for","append","neworder","dettachedelements","createMethod","length","innerHTML","dndFakeActivity","itemid","currentitem","children","insertBefore","lastchild","lastChild","_lastchild$classList","_lastchild$dataset","removeChild"],"mappings":"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"} \ No newline at end of file diff --git a/course/format/amd/build/local/content/actions.min.js b/course/format/amd/build/local/content/actions.min.js index 22a0583053e..dd33654d675 100644 --- a/course/format/amd/build/local/content/actions.min.js +++ b/course/format/amd/build/local/content/actions.min.js @@ -9,6 +9,6 @@ define("core_courseformat/local/content/actions",["exports","core/reactive","cor * @class core_courseformat/local/content/actions * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_templates=_interopRequireDefault(_templates),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_pending=_interopRequireDefault(_pending),_contenttree=_interopRequireDefault(_contenttree),_jquery=_interopRequireDefault(_jquery),(0,_prefetch.prefetchStrings)("core",["movecoursesection","movecoursemodule","confirm","delete"]);const directMutations={sectionHide:"sectionHide",sectionShow:"sectionShow",cmHide:"cmHide",cmShow:"cmShow",cmStealth:"cmStealth",cmMoveRight:"cmMoveRight",cmMoveLeft:"cmMoveLeft"};class _default extends _reactive.BaseComponent{create(){this.name="content_actions",this.selectors={ACTIONLINK:"[data-action]",SECTIONLINK:"[data-for='section']",CMLINK:"[data-for='cm']",SECTIONNODE:"[data-for='sectionnode']",MODALTOGGLER:"[data-toggle='collapse']",ADDSECTION:"[data-action='addSection']",CONTENTTREE:"#destination-selector",ACTIONMENU:".action-menu",ACTIONMENUTOGGLER:'[data-toggle="dropdown"]',OPTIONSRADIO:"[type='radio']"},this.classes={DISABLED:"disabled"}}static addActions(actions){for(const[action,mutationReference]of Object.entries(actions)){if("function"!=typeof mutationReference&&"string"!=typeof mutationReference)throw new Error("".concat(action," action must be a mutation name or a function"));directMutations[action]=mutationReference}}stateReady(state){this.addEventListener(this.element,"click",this._dispatchClick),this._checkSectionlist({state:state}),this.addEventListener(this.element,CourseEvents.sectionRefreshed,(()=>this._checkSectionlist({state:state})))}getWatchers(){return[{watch:"course.sectionlist:updated",handler:this._checkSectionlist}]}_dispatchClick(event){const target=event.target.closest(this.selectors.ACTIONLINK);if(!target)return;if(target.classList.contains(this.classes.DISABLED))return void event.preventDefault();const actionName=target.dataset.action,methodName=this._actionMethodName(actionName);if(void 0===this[methodName])return void 0!==directMutations[actionName]?"function"==typeof directMutations[actionName]?void directMutations[actionName](target,event):void this._requestMutationAction(target,event,directMutations[actionName]):void 0;this[methodName](target,event)}_actionMethodName(name){const requestName=name.charAt(0).toUpperCase()+name.slice(1);return"_request".concat(requestName)}_checkSectionlist(_ref){let{state:state}=_ref;this._setAddSectionLocked(state.course.sectionlist.length>state.course.maxsections)}_getTargetIds(target){var _target$dataset,_target$dataset2;let ids=[];null!=target&&null!==(_target$dataset=target.dataset)&&void 0!==_target$dataset&&_target$dataset.id&&ids.push(target.dataset.id);const bulkType=null==target||null===(_target$dataset2=target.dataset)||void 0===_target$dataset2?void 0:_target$dataset2.bulk;if(!bulkType)return ids;const bulk=this.reactive.get("bulk");return bulk.enabled&&bulk.selectedType===bulkType&&(ids=[...ids,...bulk.selection]),ids}async _requestMoveSection(target,event){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;event.preventDefault();const pendingModalReady=new _pending.default("courseformat/actions:prepareMoveSectionModal"),editTools=this._getClosestActionMenuToogler(target),data=this.reactive.getExporter().course(this.reactive.state);let titleText=null,sectionInfo=null;1==sectionIds.length?(sectionInfo=this.reactive.get("section",sectionIds[0]),data.sectionid=sectionInfo.id,data.sectiontitle=sectionInfo.title,data.information=await this.reactive.getFormatString("sectionmove_info",data.sectiontitle),titleText=this.reactive.getFormatString("sectionmove_title")):(data.information=await this.reactive.getFormatString("sectionsmove_info",sectionIds.length),titleText=this.reactive.getFormatString("sectionsmove_title"));const modalParams={title:titleText,body:_templates.default.render("core_courseformat/local/content/movesection",data)},modal=await this._modalBodyRenderedPromise(modalParams),modalBody=(0,_normalise.getFirst)(modal.getBody());sectionIds.forEach((sectionId=>{const currentElement=modalBody.querySelector("".concat(this.selectors.SECTIONLINK,"[data-id='").concat(sectionId,"']"));this._disableLink(currentElement)})),new _contenttree.default(modalBody.querySelector(this.selectors.CONTENTTREE),{SECTION:this.selectors.SECTIONNODE,TOGGLER:this.selectors.MODALTOGGLER,COLLAPSE:this.selectors.MODALTOGGLER},!0),modalBody.addEventListener("click",(event=>{const target=event.target;target.matches("a")&&"section"==target.dataset.for&&void 0!==target.dataset.id&&(target.getAttribute("aria-disabled")||(event.preventDefault(),this.reactive.dispatch("sectionMoveAfter",sectionIds,target.dataset.id),this._destroyModal(modal,editTools)))})),pendingModalReady.resolve()}async _requestMoveCm(target,event){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;event.preventDefault();const pendingModalReady=new _pending.default("courseformat/actions:prepareMoveCmModal"),editTools=this._getClosestActionMenuToogler(target),exporter=this.reactive.getExporter(),data=exporter.course(this.reactive.state);let titleText=null;if(1==cmIds.length){const cmInfo=this.reactive.get("cm",cmIds[0]);data.cmid=cmInfo.id,data.cmname=cmInfo.name,data.information=await this.reactive.getFormatString("cmmove_info",data.cmname),titleText=this.reactive.getFormatString("cmmove_title")}else data.information=await this.reactive.getFormatString("cmsmove_info",cmIds.length),titleText=this.reactive.getFormatString("cmsmove_title");const modalParams={title:titleText,body:_templates.default.render("core_courseformat/local/content/movecm",data)},modal=await this._modalBodyRenderedPromise(modalParams),modalBody=(0,_normalise.getFirst)(modal.getBody());cmIds.forEach((cmId=>{const currentElement=modalBody.querySelector("".concat(this.selectors.CMLINK,"[data-id='").concat(cmId,"']"));this._disableLink(currentElement)})),new _contenttree.default(modalBody.querySelector(this.selectors.CONTENTTREE),{SECTION:this.selectors.SECTIONNODE,TOGGLER:this.selectors.MODALTOGGLER,COLLAPSE:this.selectors.MODALTOGGLER,ENTER:this.selectors.SECTIONLINK}),cmIds.forEach((cmId=>{var _toggler$data;const sectionnode=modalBody.querySelector("".concat(this.selectors.CMLINK,"[data-id='").concat(cmId,"']")).closest(this.selectors.SECTIONNODE),toggler=(0,_jquery.default)(sectionnode).find(this.selectors.MODALTOGGLER);let collapsibleId=null!==(_toggler$data=toggler.data("target"))&&void 0!==_toggler$data?_toggler$data:toggler.attr("href");if(collapsibleId){collapsibleId=collapsibleId.replace("#","");const expandNode=modalBody.querySelector("#".concat(collapsibleId));(0,_jquery.default)(expandNode).collapse("show")}})),modalBody.addEventListener("click",(event=>{const target=event.target;if(!target.matches("a")||void 0===target.dataset.for||void 0===target.dataset.id)return;if(target.getAttribute("aria-disabled"))return;let targetSectionId,targetCmId;if(event.preventDefault(),"cm"==target.dataset.for){const dropData=exporter.cmDraggableData(this.reactive.state,target.dataset.id);targetSectionId=dropData.sectionid,targetCmId=dropData.nextcmid}else{const section=this.reactive.get("section",target.dataset.id);targetSectionId=target.dataset.id,targetCmId=null==section?void 0:section.cmlist[0]}this.reactive.dispatch("cmMove",cmIds,targetSectionId,targetCmId),this._destroyModal(modal,editTools)})),pendingModalReady.resolve()}async _requestAddSection(target,event){var _target$dataset$id;event.preventDefault(),this.reactive.dispatch("addSection",null!==(_target$dataset$id=target.dataset.id)&&void 0!==_target$dataset$id?_target$dataset$id:0)}async _requestDeleteSection(target,event){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;if(event.preventDefault(),!sectionIds.some((sectionId=>{var _sectionInfo$cmlist;const sectionInfo=this.reactive.get("section",sectionId);return(null!==(_sectionInfo$cmlist=sectionInfo.cmlist)&&void 0!==_sectionInfo$cmlist?_sectionInfo$cmlist:[]).length||sectionInfo.hassummary||sectionInfo.rawtitle})))return void this.reactive.dispatch("sectionDelete",sectionIds);let bodyText=null,titleText=null;if(1==sectionIds.length){titleText=this.reactive.getFormatString("sectiondelete_title");const sectionInfo=this.reactive.get("section",sectionIds[0]);bodyText=this.reactive.getFormatString("sectiondelete_info",{name:sectionInfo.title})}else titleText=this.reactive.getFormatString("sectionsdelete_title"),bodyText=this.reactive.getFormatString("sectionsdelete_info",{count:sectionIds.length});const modalParams={title:titleText,body:bodyText,type:_modal_factory.default.types.DELETE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);modal.getRoot().on(_modal_events.default.delete,(e=>{e.preventDefault(),modal.destroy(),this.reactive.dispatch("sectionDelete",sectionIds)}))}async _requestToggleSelectionCm(target,event){(0,_bulkselection.toggleBulkSelectionAction)(this.reactive,target,event,"cm")}async _requestToggleSelectionSection(target,event){(0,_bulkselection.toggleBulkSelectionAction)(this.reactive,target,event,"section")}async _requestMutationAction(target,event,mutationName){target.dataset.id&&(event.preventDefault(),this.reactive.dispatch(mutationName,[target.dataset.id]))}async _requestCmDuplicate(target,event){var _target$dataset$secti;const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;const sectionId=null!==(_target$dataset$secti=target.dataset.sectionid)&&void 0!==_target$dataset$secti?_target$dataset$secti:null;event.preventDefault(),this.reactive.dispatch("cmDuplicate",cmIds,sectionId)}async _requestCmDelete(target,event){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;event.preventDefault();let bodyText=null,titleText=null;if(1==cmIds.length){const cmInfo=this.reactive.get("cm",cmIds[0]);titleText=(0,_str.get_string)("cmdelete_title","core_courseformat"),bodyText=(0,_str.get_string)("cmdelete_info","core_courseformat",{type:cmInfo.modname,name:cmInfo.name})}else titleText=(0,_str.get_string)("cmsdelete_title","core_courseformat"),bodyText=(0,_str.get_string)("cmsdelete_info","core_courseformat",{count:cmIds.length});const modalParams={title:titleText,body:bodyText,type:_modal_factory.default.types.DELETE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);modal.getRoot().on(_modal_events.default.delete,(e=>{e.preventDefault(),modal.destroy(),this.reactive.dispatch("cmDelete",cmIds)}))}async _requestCmAvailability(target){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;const data={allowstealth:this.reactive.getExporter().canUseStealth(this.reactive.state,cmIds)},modalParams={title:(0,_str.get_string)("availability","core"),body:_templates.default.render("core_courseformat/local/content/cm/availabilitymodal",data),saveButtonText:(0,_str.get_string)("apply","core"),type:_modal_factory.default.types.SAVE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);this._setupMutationRadioButtonModal(modal,cmIds)}async _requestSectionAvailability(target){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;const title=1==sectionIds.length?"sectionavailability_title":"sectionsavailability_title",modalParams={title:this.reactive.getFormatString(title),body:_templates.default.render("core_courseformat/local/content/section/availabilitymodal",[]),saveButtonText:(0,_str.get_string)("apply","core"),type:_modal_factory.default.types.SAVE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);this._setupMutationRadioButtonModal(modal,sectionIds)}_setupMutationRadioButtonModal(modal,ids){modal.setButtonDisabled("save",!0);const submitFunction=radio=>{const mutation=null==radio?void 0:radio.value;return!!mutation&&(this.reactive.dispatch(mutation,ids),!0)},modalBody=(0,_normalise.getFirst)(modal.getBody());modalBody.querySelectorAll(this.selectors.OPTIONSRADIO).forEach((radio=>{radio.addEventListener("change",(()=>{modal.setButtonDisabled("save",!1)})),radio.parentNode.addEventListener("click",(()=>{radio.checked=!0,modal.setButtonDisabled("save",!1)})),radio.parentNode.addEventListener("dblclick",(dbClickEvent=>{submitFunction(radio)&&(dbClickEvent.preventDefault(),modal.destroy())}))})),modal.getRoot().on(_modal_events.default.save,(()=>{const radio=modalBody.querySelector("".concat(this.selectors.OPTIONSRADIO,":checked"));submitFunction(radio)}))}_setAddSectionLocked(locked){this.getElements(this.selectors.ADDSECTION).forEach((element=>{element.classList.toggle(this.classes.DISABLED,locked),this.setElementLocked(element,locked)}))}_disableLink(element){element&&(element.style.pointerEvents="none",element.style.userSelect="none",element.classList.add(this.classes.DISABLED),element.setAttribute("aria-disabled",!0),element.addEventListener("click",(event=>event.preventDefault())))}_modalBodyRenderedPromise(modalParams){return new Promise(((resolve,reject)=>{_modal_factory.default.create(modalParams).then((modal=>{modal.setRemoveOnClose(!0),modal.getRoot().on(_modal_events.default.bodyRendered,(()=>{resolve(modal)})),void 0!==modalParams.saveButtonText&&modal.setSaveButtonText(modalParams.saveButtonText),void 0!==modalParams.deleteButtonText&&modal.setDeleteButtonText(modalParams.saveButtonText),modal.show()})).catch((()=>{reject("Cannot load modal content")}))}))}_destroyModal(modal,element){modal.hide();const pendingDestroy=new _pending.default("courseformat/actions:destroyModal");element&&element.focus(),setTimeout((()=>{modal.destroy(),pendingDestroy.resolve()}),500)}_getClosestActionMenuToogler(element){const actionMenu=element.closest(this.selectors.ACTIONMENU);if(actionMenu)return actionMenu.querySelector(this.selectors.ACTIONMENUTOGGLER)}}return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_templates=_interopRequireDefault(_templates),CourseEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(CourseEvents),_pending=_interopRequireDefault(_pending),_contenttree=_interopRequireDefault(_contenttree),_jquery=_interopRequireDefault(_jquery),(0,_prefetch.prefetchStrings)("core",["movecoursesection","movecoursemodule","confirm","delete"]);const directMutations={sectionHide:"sectionHide",sectionShow:"sectionShow",cmHide:"cmHide",cmShow:"cmShow",cmStealth:"cmStealth",cmMoveRight:"cmMoveRight",cmMoveLeft:"cmMoveLeft",cmNoGroups:"cmNoGroups",cmSeparateGroups:"cmSeparateGroups",cmVisibleGroups:"cmVisibleGroups"};class _default extends _reactive.BaseComponent{create(){this.name="content_actions",this.selectors={ACTIONLINK:"[data-action]",SECTIONLINK:"[data-for='section']",CMLINK:"[data-for='cm']",SECTIONNODE:"[data-for='sectionnode']",MODALTOGGLER:"[data-toggle='collapse']",ADDSECTION:"[data-action='addSection']",CONTENTTREE:"#destination-selector",ACTIONMENU:".action-menu",ACTIONMENUTOGGLER:'[data-toggle="dropdown"]',OPTIONSRADIO:"[type='radio']"},this.classes={DISABLED:"disabled"}}static addActions(actions){for(const[action,mutationReference]of Object.entries(actions)){if("function"!=typeof mutationReference&&"string"!=typeof mutationReference)throw new Error("".concat(action," action must be a mutation name or a function"));directMutations[action]=mutationReference}}stateReady(state){this.addEventListener(this.element,"click",this._dispatchClick),this._checkSectionlist({state:state}),this.addEventListener(this.element,CourseEvents.sectionRefreshed,(()=>this._checkSectionlist({state:state})))}getWatchers(){return[{watch:"course.sectionlist:updated",handler:this._checkSectionlist}]}_dispatchClick(event){const target=event.target.closest(this.selectors.ACTIONLINK);if(!target)return;if(target.classList.contains(this.classes.DISABLED))return void event.preventDefault();const actionName=target.dataset.action,methodName=this._actionMethodName(actionName);if(void 0===this[methodName])return void 0!==directMutations[actionName]?"function"==typeof directMutations[actionName]?void directMutations[actionName](target,event):void this._requestMutationAction(target,event,directMutations[actionName]):void 0;this[methodName](target,event)}_actionMethodName(name){const requestName=name.charAt(0).toUpperCase()+name.slice(1);return"_request".concat(requestName)}_checkSectionlist(_ref){let{state:state}=_ref;this._setAddSectionLocked(state.course.sectionlist.length>state.course.maxsections)}_getTargetIds(target){var _target$dataset,_target$dataset2;let ids=[];null!=target&&null!==(_target$dataset=target.dataset)&&void 0!==_target$dataset&&_target$dataset.id&&ids.push(target.dataset.id);const bulkType=null==target||null===(_target$dataset2=target.dataset)||void 0===_target$dataset2?void 0:_target$dataset2.bulk;if(!bulkType)return ids;const bulk=this.reactive.get("bulk");return bulk.enabled&&bulk.selectedType===bulkType&&(ids=[...ids,...bulk.selection]),ids}async _requestMoveSection(target,event){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;event.preventDefault();const pendingModalReady=new _pending.default("courseformat/actions:prepareMoveSectionModal"),editTools=this._getClosestActionMenuToogler(target),data=this.reactive.getExporter().course(this.reactive.state);let titleText=null,sectionInfo=null;1==sectionIds.length?(sectionInfo=this.reactive.get("section",sectionIds[0]),data.sectionid=sectionInfo.id,data.sectiontitle=sectionInfo.title,data.information=await this.reactive.getFormatString("sectionmove_info",data.sectiontitle),titleText=this.reactive.getFormatString("sectionmove_title")):(data.information=await this.reactive.getFormatString("sectionsmove_info",sectionIds.length),titleText=this.reactive.getFormatString("sectionsmove_title"));const modalParams={title:titleText,body:_templates.default.render("core_courseformat/local/content/movesection",data)},modal=await this._modalBodyRenderedPromise(modalParams),modalBody=(0,_normalise.getFirst)(modal.getBody());sectionIds.forEach((sectionId=>{const currentElement=modalBody.querySelector("".concat(this.selectors.SECTIONLINK,"[data-id='").concat(sectionId,"']"));this._disableLink(currentElement)})),new _contenttree.default(modalBody.querySelector(this.selectors.CONTENTTREE),{SECTION:this.selectors.SECTIONNODE,TOGGLER:this.selectors.MODALTOGGLER,COLLAPSE:this.selectors.MODALTOGGLER},!0),modalBody.addEventListener("click",(event=>{const target=event.target;target.matches("a")&&"section"==target.dataset.for&&void 0!==target.dataset.id&&(target.getAttribute("aria-disabled")||(event.preventDefault(),this.reactive.dispatch("sectionMoveAfter",sectionIds,target.dataset.id),this._destroyModal(modal,editTools)))})),pendingModalReady.resolve()}async _requestMoveCm(target,event){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;event.preventDefault();const pendingModalReady=new _pending.default("courseformat/actions:prepareMoveCmModal"),editTools=this._getClosestActionMenuToogler(target),exporter=this.reactive.getExporter(),data=exporter.course(this.reactive.state);let titleText=null;if(1==cmIds.length){const cmInfo=this.reactive.get("cm",cmIds[0]);data.cmid=cmInfo.id,data.cmname=cmInfo.name,data.information=await this.reactive.getFormatString("cmmove_info",data.cmname),titleText=this.reactive.getFormatString("cmmove_title")}else data.information=await this.reactive.getFormatString("cmsmove_info",cmIds.length),titleText=this.reactive.getFormatString("cmsmove_title");const modalParams={title:titleText,body:_templates.default.render("core_courseformat/local/content/movecm",data)},modal=await this._modalBodyRenderedPromise(modalParams),modalBody=(0,_normalise.getFirst)(modal.getBody());cmIds.forEach((cmId=>{const currentElement=modalBody.querySelector("".concat(this.selectors.CMLINK,"[data-id='").concat(cmId,"']"));this._disableLink(currentElement)})),new _contenttree.default(modalBody.querySelector(this.selectors.CONTENTTREE),{SECTION:this.selectors.SECTIONNODE,TOGGLER:this.selectors.MODALTOGGLER,COLLAPSE:this.selectors.MODALTOGGLER,ENTER:this.selectors.SECTIONLINK}),cmIds.forEach((cmId=>{var _toggler$data;const sectionnode=modalBody.querySelector("".concat(this.selectors.CMLINK,"[data-id='").concat(cmId,"']")).closest(this.selectors.SECTIONNODE),toggler=(0,_jquery.default)(sectionnode).find(this.selectors.MODALTOGGLER);let collapsibleId=null!==(_toggler$data=toggler.data("target"))&&void 0!==_toggler$data?_toggler$data:toggler.attr("href");if(collapsibleId){collapsibleId=collapsibleId.replace("#","");const expandNode=modalBody.querySelector("#".concat(collapsibleId));(0,_jquery.default)(expandNode).collapse("show")}})),modalBody.addEventListener("click",(event=>{const target=event.target;if(!target.matches("a")||void 0===target.dataset.for||void 0===target.dataset.id)return;if(target.getAttribute("aria-disabled"))return;let targetSectionId,targetCmId;if(event.preventDefault(),"cm"==target.dataset.for){const dropData=exporter.cmDraggableData(this.reactive.state,target.dataset.id);targetSectionId=dropData.sectionid,targetCmId=dropData.nextcmid}else{const section=this.reactive.get("section",target.dataset.id);targetSectionId=target.dataset.id,targetCmId=null==section?void 0:section.cmlist[0]}this.reactive.dispatch("cmMove",cmIds,targetSectionId,targetCmId),this._destroyModal(modal,editTools)})),pendingModalReady.resolve()}async _requestAddSection(target,event){var _target$dataset$id;event.preventDefault(),this.reactive.dispatch("addSection",null!==(_target$dataset$id=target.dataset.id)&&void 0!==_target$dataset$id?_target$dataset$id:0)}async _requestDeleteSection(target,event){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;if(event.preventDefault(),!sectionIds.some((sectionId=>{var _sectionInfo$cmlist;const sectionInfo=this.reactive.get("section",sectionId);return(null!==(_sectionInfo$cmlist=sectionInfo.cmlist)&&void 0!==_sectionInfo$cmlist?_sectionInfo$cmlist:[]).length||sectionInfo.hassummary||sectionInfo.rawtitle})))return void this.reactive.dispatch("sectionDelete",sectionIds);let bodyText=null,titleText=null;if(1==sectionIds.length){titleText=this.reactive.getFormatString("sectiondelete_title");const sectionInfo=this.reactive.get("section",sectionIds[0]);bodyText=this.reactive.getFormatString("sectiondelete_info",{name:sectionInfo.title})}else titleText=this.reactive.getFormatString("sectionsdelete_title"),bodyText=this.reactive.getFormatString("sectionsdelete_info",{count:sectionIds.length});const modalParams={title:titleText,body:bodyText,type:_modal_factory.default.types.DELETE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);modal.getRoot().on(_modal_events.default.delete,(e=>{e.preventDefault(),modal.destroy(),this.reactive.dispatch("sectionDelete",sectionIds)}))}async _requestToggleSelectionCm(target,event){(0,_bulkselection.toggleBulkSelectionAction)(this.reactive,target,event,"cm")}async _requestToggleSelectionSection(target,event){(0,_bulkselection.toggleBulkSelectionAction)(this.reactive,target,event,"section")}async _requestMutationAction(target,event,mutationName){target.dataset.id&&(event.preventDefault(),this.reactive.dispatch(mutationName,[target.dataset.id]))}async _requestCmDuplicate(target,event){var _target$dataset$secti;const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;const sectionId=null!==(_target$dataset$secti=target.dataset.sectionid)&&void 0!==_target$dataset$secti?_target$dataset$secti:null;event.preventDefault(),this.reactive.dispatch("cmDuplicate",cmIds,sectionId)}async _requestCmDelete(target,event){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;event.preventDefault();let bodyText=null,titleText=null;if(1==cmIds.length){const cmInfo=this.reactive.get("cm",cmIds[0]);titleText=(0,_str.get_string)("cmdelete_title","core_courseformat"),bodyText=(0,_str.get_string)("cmdelete_info","core_courseformat",{type:cmInfo.modname,name:cmInfo.name})}else titleText=(0,_str.get_string)("cmsdelete_title","core_courseformat"),bodyText=(0,_str.get_string)("cmsdelete_info","core_courseformat",{count:cmIds.length});const modalParams={title:titleText,body:bodyText,type:_modal_factory.default.types.DELETE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);modal.getRoot().on(_modal_events.default.delete,(e=>{e.preventDefault(),modal.destroy(),this.reactive.dispatch("cmDelete",cmIds)}))}async _requestCmAvailability(target){const cmIds=this._getTargetIds(target);if(0==cmIds.length)return;const data={allowstealth:this.reactive.getExporter().canUseStealth(this.reactive.state,cmIds)},modalParams={title:(0,_str.get_string)("availability","core"),body:_templates.default.render("core_courseformat/local/content/cm/availabilitymodal",data),saveButtonText:(0,_str.get_string)("apply","core"),type:_modal_factory.default.types.SAVE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);this._setupMutationRadioButtonModal(modal,cmIds)}async _requestSectionAvailability(target){const sectionIds=this._getTargetIds(target);if(0==sectionIds.length)return;const title=1==sectionIds.length?"sectionavailability_title":"sectionsavailability_title",modalParams={title:this.reactive.getFormatString(title),body:_templates.default.render("core_courseformat/local/content/section/availabilitymodal",[]),saveButtonText:(0,_str.get_string)("apply","core"),type:_modal_factory.default.types.SAVE_CANCEL},modal=await this._modalBodyRenderedPromise(modalParams);this._setupMutationRadioButtonModal(modal,sectionIds)}_setupMutationRadioButtonModal(modal,ids){modal.setButtonDisabled("save",!0);const submitFunction=radio=>{const mutation=null==radio?void 0:radio.value;return!!mutation&&(this.reactive.dispatch(mutation,ids),!0)},modalBody=(0,_normalise.getFirst)(modal.getBody());modalBody.querySelectorAll(this.selectors.OPTIONSRADIO).forEach((radio=>{radio.addEventListener("change",(()=>{modal.setButtonDisabled("save",!1)})),radio.parentNode.addEventListener("click",(()=>{radio.checked=!0,modal.setButtonDisabled("save",!1)})),radio.parentNode.addEventListener("dblclick",(dbClickEvent=>{submitFunction(radio)&&(dbClickEvent.preventDefault(),modal.destroy())}))})),modal.getRoot().on(_modal_events.default.save,(()=>{const radio=modalBody.querySelector("".concat(this.selectors.OPTIONSRADIO,":checked"));submitFunction(radio)}))}_setAddSectionLocked(locked){this.getElements(this.selectors.ADDSECTION).forEach((element=>{element.classList.toggle(this.classes.DISABLED,locked),this.setElementLocked(element,locked)}))}_disableLink(element){element&&(element.style.pointerEvents="none",element.style.userSelect="none",element.classList.add(this.classes.DISABLED),element.setAttribute("aria-disabled",!0),element.addEventListener("click",(event=>event.preventDefault())))}_modalBodyRenderedPromise(modalParams){return new Promise(((resolve,reject)=>{_modal_factory.default.create(modalParams).then((modal=>{modal.setRemoveOnClose(!0),modal.getRoot().on(_modal_events.default.bodyRendered,(()=>{resolve(modal)})),void 0!==modalParams.saveButtonText&&modal.setSaveButtonText(modalParams.saveButtonText),void 0!==modalParams.deleteButtonText&&modal.setDeleteButtonText(modalParams.saveButtonText),modal.show()})).catch((()=>{reject("Cannot load modal content")}))}))}_destroyModal(modal,element){modal.hide();const pendingDestroy=new _pending.default("courseformat/actions:destroyModal");element&&element.focus(),setTimeout((()=>{modal.destroy(),pendingDestroy.resolve()}),500)}_getClosestActionMenuToogler(element){const actionMenu=element.closest(this.selectors.ACTIONMENU);if(actionMenu)return actionMenu.querySelector(this.selectors.ACTIONMENUTOGGLER)}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=actions.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/content/actions.min.js.map b/course/format/amd/build/local/content/actions.min.js.map index ebd35c9ff55..f70af8db6e5 100644 --- a/course/format/amd/build/local/content/actions.min.js.map +++ b/course/format/amd/build/local/content/actions.min.js.map @@ -1 +1 @@ -{"version":3,"file":"actions.min.js","sources":["../../../src/local/content/actions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Course state actions dispatcher.\n *\n * This module captures all data-dispatch links in the course content and dispatch the proper\n * state mutation, including any confirmation and modal required.\n *\n * @module core_courseformat/local/content/actions\n * @class core_courseformat/local/content/actions\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Templates from 'core/templates';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {getFirst} from 'core/normalise';\nimport {toggleBulkSelectionAction} from 'core_courseformat/local/content/actions/bulkselection';\nimport * as CourseEvents from 'core_course/events';\nimport Pending from 'core/pending';\nimport ContentTree from 'core_courseformat/local/courseeditor/contenttree';\n// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated.\nimport jQuery from 'jquery';\n\n// Load global strings.\nprefetchStrings('core', ['movecoursesection', 'movecoursemodule', 'confirm', 'delete']);\n\n// Mutations are dispatched by the course content actions.\n// Formats can use this module addActions static method to add custom actions.\n// Direct mutations can be simple strings (mutation) name or functions.\nconst directMutations = {\n sectionHide: 'sectionHide',\n sectionShow: 'sectionShow',\n cmHide: 'cmHide',\n cmShow: 'cmShow',\n cmStealth: 'cmStealth',\n cmMoveRight: 'cmMoveRight',\n cmMoveLeft: 'cmMoveLeft',\n};\n\nexport default class extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'content_actions';\n // Default query selectors.\n this.selectors = {\n ACTIONLINK: `[data-action]`,\n // Move modal selectors.\n SECTIONLINK: `[data-for='section']`,\n CMLINK: `[data-for='cm']`,\n SECTIONNODE: `[data-for='sectionnode']`,\n MODALTOGGLER: `[data-toggle='collapse']`,\n ADDSECTION: `[data-action='addSection']`,\n CONTENTTREE: `#destination-selector`,\n ACTIONMENU: `.action-menu`,\n ACTIONMENUTOGGLER: `[data-toggle=\"dropdown\"]`,\n // Availability modal selectors.\n OPTIONSRADIO: `[type='radio']`,\n };\n // Component css classes.\n this.classes = {\n DISABLED: `disabled`,\n };\n }\n\n /**\n * Add extra actions to the module.\n *\n * @param {array} actions array of methods to execute\n */\n static addActions(actions) {\n for (const [action, mutationReference] of Object.entries(actions)) {\n if (typeof mutationReference !== 'function' && typeof mutationReference !== 'string') {\n throw new Error(`${action} action must be a mutation name or a function`);\n }\n directMutations[action] = mutationReference;\n }\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data.\n *\n */\n stateReady(state) {\n // Delegate dispatch clicks.\n this.addEventListener(\n this.element,\n 'click',\n this._dispatchClick\n );\n // Check section limit.\n this._checkSectionlist({state});\n // Add an Event listener to recalculate limits it if a section HTML is altered.\n this.addEventListener(\n this.element,\n CourseEvents.sectionRefreshed,\n () => this._checkSectionlist({state})\n );\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n // Check section limit.\n {watch: `course.sectionlist:updated`, handler: this._checkSectionlist},\n ];\n }\n\n _dispatchClick(event) {\n const target = event.target.closest(this.selectors.ACTIONLINK);\n if (!target) {\n return;\n }\n if (target.classList.contains(this.classes.DISABLED)) {\n event.preventDefault();\n return;\n }\n\n // Invoke proper method.\n const actionName = target.dataset.action;\n const methodName = this._actionMethodName(actionName);\n\n if (this[methodName] !== undefined) {\n this[methodName](target, event);\n return;\n }\n\n // Check direct mutations or mutations handlers.\n if (directMutations[actionName] !== undefined) {\n if (typeof directMutations[actionName] === 'function') {\n directMutations[actionName](target, event);\n return;\n }\n this._requestMutationAction(target, event, directMutations[actionName]);\n return;\n }\n }\n\n _actionMethodName(name) {\n const requestName = name.charAt(0).toUpperCase() + name.slice(1);\n return `_request${requestName}`;\n }\n\n /**\n * Check the section list and disable some options if needed.\n *\n * @param {Object} detail the update details.\n * @param {Object} detail.state the state object.\n */\n _checkSectionlist({state}) {\n // Disable \"add section\" actions if the course max sections has been exceeded.\n this._setAddSectionLocked(state.course.sectionlist.length > state.course.maxsections);\n }\n\n /**\n * Return the ids represented by this element.\n *\n * Depending on the dataset attributes the action could represent a single id\n * or a bulk actions with all the current selected ids.\n *\n * @param {HTMLElement} target\n * @returns {Number[]} array of Ids\n */\n _getTargetIds(target) {\n let ids = [];\n if (target?.dataset?.id) {\n ids.push(target.dataset.id);\n }\n const bulkType = target?.dataset?.bulk;\n if (!bulkType) {\n return ids;\n }\n const bulk = this.reactive.get('bulk');\n if (bulk.enabled && bulk.selectedType === bulkType) {\n ids = [...ids, ...bulk.selection];\n }\n return ids;\n }\n\n /**\n * Handle a move section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestMoveSection(target, event) {\n // Check we have an id.\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n const pendingModalReady = new Pending(`courseformat/actions:prepareMoveSectionModal`);\n\n // The section edit menu to refocus on end.\n const editTools = this._getClosestActionMenuToogler(target);\n\n // Collect section information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.course(this.reactive.state);\n let titleText = null;\n\n // Add the target section id and title.\n let sectionInfo = null;\n if (sectionIds.length == 1) {\n sectionInfo = this.reactive.get('section', sectionIds[0]);\n data.sectionid = sectionInfo.id;\n data.sectiontitle = sectionInfo.title;\n data.information = await this.reactive.getFormatString('sectionmove_info', data.sectiontitle);\n titleText = this.reactive.getFormatString('sectionmove_title');\n } else {\n data.information = await this.reactive.getFormatString('sectionsmove_info', sectionIds.length);\n titleText = this.reactive.getFormatString('sectionsmove_title');\n }\n\n\n // Build the modal parameters from the event data.\n const modalParams = {\n title: titleText,\n body: Templates.render('core_courseformat/local/content/movesection', data),\n };\n\n // Create the modal.\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n const modalBody = getFirst(modal.getBody());\n\n // Disable current selected section ids.\n sectionIds.forEach(sectionId => {\n const currentElement = modalBody.querySelector(`${this.selectors.SECTIONLINK}[data-id='${sectionId}']`);\n this._disableLink(currentElement);\n });\n\n // Setup keyboard navigation.\n new ContentTree(\n modalBody.querySelector(this.selectors.CONTENTTREE),\n {\n SECTION: this.selectors.SECTIONNODE,\n TOGGLER: this.selectors.MODALTOGGLER,\n COLLAPSE: this.selectors.MODALTOGGLER,\n },\n true\n );\n\n // Capture click.\n modalBody.addEventListener('click', (event) => {\n const target = event.target;\n if (!target.matches('a') || target.dataset.for != 'section' || target.dataset.id === undefined) {\n return;\n }\n if (target.getAttribute('aria-disabled')) {\n return;\n }\n event.preventDefault();\n this.reactive.dispatch('sectionMoveAfter', sectionIds, target.dataset.id);\n this._destroyModal(modal, editTools);\n });\n\n pendingModalReady.resolve();\n }\n\n /**\n * Handle a move cm request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestMoveCm(target, event) {\n // Check we have an id.\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n const pendingModalReady = new Pending(`courseformat/actions:prepareMoveCmModal`);\n\n // The section edit menu to refocus on end.\n const editTools = this._getClosestActionMenuToogler(target);\n\n // Collect information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.course(this.reactive.state);\n\n let titleText = null;\n if (cmIds.length == 1) {\n const cmInfo = this.reactive.get('cm', cmIds[0]);\n data.cmid = cmInfo.id;\n data.cmname = cmInfo.name;\n data.information = await this.reactive.getFormatString('cmmove_info', data.cmname);\n titleText = this.reactive.getFormatString('cmmove_title');\n } else {\n data.information = await this.reactive.getFormatString('cmsmove_info', cmIds.length);\n titleText = this.reactive.getFormatString('cmsmove_title');\n }\n\n // Build the modal parameters from the event data.\n const modalParams = {\n title: titleText,\n body: Templates.render('core_courseformat/local/content/movecm', data),\n };\n\n // Create the modal.\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n const modalBody = getFirst(modal.getBody());\n\n // Disable current selected section ids.\n cmIds.forEach(cmId => {\n const currentElement = modalBody.querySelector(`${this.selectors.CMLINK}[data-id='${cmId}']`);\n this._disableLink(currentElement);\n });\n\n // Setup keyboard navigation.\n new ContentTree(\n modalBody.querySelector(this.selectors.CONTENTTREE),\n {\n SECTION: this.selectors.SECTIONNODE,\n TOGGLER: this.selectors.MODALTOGGLER,\n COLLAPSE: this.selectors.MODALTOGGLER,\n ENTER: this.selectors.SECTIONLINK,\n }\n );\n\n // Open the cm section node if possible (Bootstrap 4 uses jQuery to interact with collapsibles).\n // All jQuery in this code can be replaced when MDL-71979 is integrated.\n cmIds.forEach(cmId => {\n const currentElement = modalBody.querySelector(`${this.selectors.CMLINK}[data-id='${cmId}']`);\n const sectionnode = currentElement.closest(this.selectors.SECTIONNODE);\n const toggler = jQuery(sectionnode).find(this.selectors.MODALTOGGLER);\n let collapsibleId = toggler.data('target') ?? toggler.attr('href');\n if (collapsibleId) {\n // We cannot be sure we have # in the id element name.\n collapsibleId = collapsibleId.replace('#', '');\n const expandNode = modalBody.querySelector(`#${collapsibleId}`);\n jQuery(expandNode).collapse('show');\n }\n });\n\n modalBody.addEventListener('click', (event) => {\n const target = event.target;\n if (!target.matches('a') || target.dataset.for === undefined || target.dataset.id === undefined) {\n return;\n }\n if (target.getAttribute('aria-disabled')) {\n return;\n }\n event.preventDefault();\n\n let targetSectionId;\n let targetCmId;\n if (target.dataset.for == 'cm') {\n const dropData = exporter.cmDraggableData(this.reactive.state, target.dataset.id);\n targetSectionId = dropData.sectionid;\n targetCmId = dropData.nextcmid;\n } else {\n const section = this.reactive.get('section', target.dataset.id);\n targetSectionId = target.dataset.id;\n targetCmId = section?.cmlist[0];\n }\n this.reactive.dispatch('cmMove', cmIds, targetSectionId, targetCmId);\n this._destroyModal(modal, editTools);\n });\n\n pendingModalReady.resolve();\n }\n\n /**\n * Handle a create section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestAddSection(target, event) {\n event.preventDefault();\n this.reactive.dispatch('addSection', target.dataset.id ?? 0);\n }\n\n /**\n * Handle a delete section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestDeleteSection(target, event) {\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n // We don't need confirmation to delete empty sections.\n let needsConfirmation = sectionIds.some(sectionId => {\n const sectionInfo = this.reactive.get('section', sectionId);\n const cmList = sectionInfo.cmlist ?? [];\n return (cmList.length || sectionInfo.hassummary || sectionInfo.rawtitle);\n });\n if (!needsConfirmation) {\n this.reactive.dispatch('sectionDelete', sectionIds);\n return;\n }\n\n let bodyText = null;\n let titleText = null;\n if (sectionIds.length == 1) {\n titleText = this.reactive.getFormatString('sectiondelete_title');\n const sectionInfo = this.reactive.get('section', sectionIds[0]);\n bodyText = this.reactive.getFormatString('sectiondelete_info', {name: sectionInfo.title});\n } else {\n titleText = this.reactive.getFormatString('sectionsdelete_title');\n bodyText = this.reactive.getFormatString('sectionsdelete_info', {count: sectionIds.length});\n }\n\n const modalParams = {\n title: titleText,\n body: bodyText,\n type: ModalFactory.types.DELETE_CANCEL,\n };\n\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n modal.getRoot().on(\n ModalEvents.delete,\n e => {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n modal.destroy();\n this.reactive.dispatch('sectionDelete', sectionIds);\n }\n );\n }\n\n /**\n * Handle a toggle cm selection.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestToggleSelectionCm(target, event) {\n toggleBulkSelectionAction(this.reactive, target, event, 'cm');\n }\n\n /**\n * Handle a toggle section selection.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestToggleSelectionSection(target, event) {\n toggleBulkSelectionAction(this.reactive, target, event, 'section');\n }\n\n /**\n * Basic mutation action helper.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n * @param {string} mutationName the mutation name\n */\n async _requestMutationAction(target, event, mutationName) {\n if (!target.dataset.id) {\n return;\n }\n event.preventDefault();\n this.reactive.dispatch(mutationName, [target.dataset.id]);\n }\n\n /**\n * Handle a course module duplicate request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestCmDuplicate(target, event) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n const sectionId = target.dataset.sectionid ?? null;\n event.preventDefault();\n this.reactive.dispatch('cmDuplicate', cmIds, sectionId);\n }\n\n /**\n * Handle a delete cm request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestCmDelete(target, event) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n let bodyText = null;\n let titleText = null;\n if (cmIds.length == 1) {\n const cmInfo = this.reactive.get('cm', cmIds[0]);\n titleText = getString('cmdelete_title', 'core_courseformat');\n bodyText = getString(\n 'cmdelete_info',\n 'core_courseformat',\n {\n type: cmInfo.modname,\n name: cmInfo.name,\n }\n );\n } else {\n titleText = getString('cmsdelete_title', 'core_courseformat');\n bodyText = getString(\n 'cmsdelete_info',\n 'core_courseformat',\n {count: cmIds.length}\n );\n }\n\n const modalParams = {\n title: titleText,\n body: bodyText,\n type: ModalFactory.types.DELETE_CANCEL,\n };\n\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n modal.getRoot().on(\n ModalEvents.delete,\n e => {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n modal.destroy();\n this.reactive.dispatch('cmDelete', cmIds);\n }\n );\n }\n\n /**\n * Handle a cm availability change request.\n *\n * @param {Element} target the dispatch action element\n */\n async _requestCmAvailability(target) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n // Show the availability modal to decide which action to trigger.\n const exporter = this.reactive.getExporter();\n const data = {\n allowstealth: exporter.canUseStealth(this.reactive.state, cmIds),\n };\n const modalParams = {\n title: getString('availability', 'core'),\n body: Templates.render('core_courseformat/local/content/cm/availabilitymodal', data),\n saveButtonText: getString('apply', 'core'),\n type: ModalFactory.types.SAVE_CANCEL,\n };\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n this._setupMutationRadioButtonModal(modal, cmIds);\n }\n\n /**\n * Handle a section availability change request.\n *\n * @param {Element} target the dispatch action element\n */\n async _requestSectionAvailability(target) {\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n const title = (sectionIds.length == 1) ? 'sectionavailability_title' : 'sectionsavailability_title';\n // Show the availability modal to decide which action to trigger.\n const modalParams = {\n title: this.reactive.getFormatString(title),\n body: Templates.render('core_courseformat/local/content/section/availabilitymodal', []),\n saveButtonText: getString('apply', 'core'),\n type: ModalFactory.types.SAVE_CANCEL,\n };\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n this._setupMutationRadioButtonModal(modal, sectionIds);\n }\n\n /**\n * Add events to a mutation selector radio buttons modal.\n * @param {Modal} modal\n * @param {Number[]} ids the section or cm ids to apply the mutation\n */\n _setupMutationRadioButtonModal(modal, ids) {\n // The save button is not enabled until the user selects an option.\n modal.setButtonDisabled('save', true);\n\n const submitFunction = (radio) => {\n const mutation = radio?.value;\n if (!mutation) {\n return false;\n }\n this.reactive.dispatch(mutation, ids);\n return true;\n };\n\n const modalBody = getFirst(modal.getBody());\n const radioOptions = modalBody.querySelectorAll(this.selectors.OPTIONSRADIO);\n radioOptions.forEach(radio => {\n radio.addEventListener('change', () => {\n modal.setButtonDisabled('save', false);\n });\n radio.parentNode.addEventListener('click', () => {\n radio.checked = true;\n modal.setButtonDisabled('save', false);\n });\n radio.parentNode.addEventListener('dblclick', dbClickEvent => {\n if (submitFunction(radio)) {\n dbClickEvent.preventDefault();\n modal.destroy();\n }\n });\n });\n\n modal.getRoot().on(\n ModalEvents.save,\n () => {\n const radio = modalBody.querySelector(`${this.selectors.OPTIONSRADIO}:checked`);\n submitFunction(radio);\n }\n );\n }\n\n /**\n * Disable all add sections actions.\n *\n * @param {boolean} locked the new locked value.\n */\n _setAddSectionLocked(locked) {\n const targets = this.getElements(this.selectors.ADDSECTION);\n targets.forEach(element => {\n element.classList.toggle(this.classes.DISABLED, locked);\n this.setElementLocked(element, locked);\n });\n }\n\n /**\n * Replace an element with a copy with a different tag name.\n *\n * @param {Element} element the original element\n */\n _disableLink(element) {\n if (element) {\n element.style.pointerEvents = 'none';\n element.style.userSelect = 'none';\n element.classList.add(this.classes.DISABLED);\n element.setAttribute('aria-disabled', true);\n element.addEventListener('click', event => event.preventDefault());\n }\n }\n\n /**\n * Render a modal and return a body ready promise.\n *\n * @param {object} modalParams the modal params\n * @return {Promise} the modal body ready promise\n */\n _modalBodyRenderedPromise(modalParams) {\n return new Promise((resolve, reject) => {\n ModalFactory.create(modalParams).then((modal) => {\n modal.setRemoveOnClose(true);\n // Handle body loading event.\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n resolve(modal);\n });\n // Configure some extra modal params.\n if (modalParams.saveButtonText !== undefined) {\n modal.setSaveButtonText(modalParams.saveButtonText);\n }\n if (modalParams.deleteButtonText !== undefined) {\n modal.setDeleteButtonText(modalParams.saveButtonText);\n }\n modal.show();\n return;\n }).catch(() => {\n reject(`Cannot load modal content`);\n });\n });\n }\n\n /**\n * Hide and later destroy a modal.\n *\n * Behat will fail if we remove the modal while some boostrap collapse is executing.\n *\n * @param {Modal} modal\n * @param {HTMLElement} element the dom element to focus on.\n */\n _destroyModal(modal, element) {\n modal.hide();\n const pendingDestroy = new Pending(`courseformat/actions:destroyModal`);\n if (element) {\n element.focus();\n }\n setTimeout(() =>{\n modal.destroy();\n pendingDestroy.resolve();\n }, 500);\n }\n\n /**\n * Get the closest actions menu toggler to an action element.\n *\n * @param {HTMLElement} element the action link element\n * @returns {HTMLElement|undefined}\n */\n _getClosestActionMenuToogler(element) {\n const actionMenu = element.closest(this.selectors.ACTIONMENU);\n if (!actionMenu) {\n return undefined;\n }\n return actionMenu.querySelector(this.selectors.ACTIONMENUTOGGLER);\n }\n}\n"],"names":["directMutations","sectionHide","sectionShow","cmHide","cmShow","cmStealth","cmMoveRight","cmMoveLeft","BaseComponent","create","name","selectors","ACTIONLINK","SECTIONLINK","CMLINK","SECTIONNODE","MODALTOGGLER","ADDSECTION","CONTENTTREE","ACTIONMENU","ACTIONMENUTOGGLER","OPTIONSRADIO","classes","DISABLED","actions","action","mutationReference","Object","entries","Error","stateReady","state","addEventListener","this","element","_dispatchClick","_checkSectionlist","CourseEvents","sectionRefreshed","getWatchers","watch","handler","event","target","closest","classList","contains","preventDefault","actionName","dataset","methodName","_actionMethodName","undefined","_requestMutationAction","requestName","charAt","toUpperCase","slice","_setAddSectionLocked","course","sectionlist","length","maxsections","_getTargetIds","ids","_target$dataset","id","push","bulkType","_target$dataset2","bulk","reactive","get","enabled","selectedType","selection","sectionIds","pendingModalReady","Pending","editTools","_getClosestActionMenuToogler","data","getExporter","titleText","sectionInfo","sectionid","sectiontitle","title","information","getFormatString","modalParams","body","Templates","render","modal","_modalBodyRenderedPromise","modalBody","getBody","forEach","sectionId","currentElement","querySelector","_disableLink","ContentTree","SECTION","TOGGLER","COLLAPSE","matches","for","getAttribute","dispatch","_destroyModal","resolve","cmIds","exporter","cmInfo","cmid","cmname","cmId","ENTER","sectionnode","toggler","find","collapsibleId","attr","replace","expandNode","collapse","targetSectionId","targetCmId","dropData","cmDraggableData","nextcmid","section","cmlist","some","hassummary","rawtitle","bodyText","count","type","ModalFactory","types","DELETE_CANCEL","getRoot","on","ModalEvents","delete","e","destroy","mutationName","modname","allowstealth","canUseStealth","saveButtonText","SAVE_CANCEL","_setupMutationRadioButtonModal","setButtonDisabled","submitFunction","radio","mutation","value","querySelectorAll","parentNode","checked","dbClickEvent","save","locked","getElements","toggle","setElementLocked","style","pointerEvents","userSelect","add","setAttribute","Promise","reject","then","setRemoveOnClose","bodyRendered","setSaveButtonText","deleteButtonText","setDeleteButtonText","show","catch","hide","pendingDestroy","focus","setTimeout","actionMenu"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"actions.min.js","sources":["../../../src/local/content/actions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Course state actions dispatcher.\n *\n * This module captures all data-dispatch links in the course content and dispatch the proper\n * state mutation, including any confirmation and modal required.\n *\n * @module core_courseformat/local/content/actions\n * @class core_courseformat/local/content/actions\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Templates from 'core/templates';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {getFirst} from 'core/normalise';\nimport {toggleBulkSelectionAction} from 'core_courseformat/local/content/actions/bulkselection';\nimport * as CourseEvents from 'core_course/events';\nimport Pending from 'core/pending';\nimport ContentTree from 'core_courseformat/local/courseeditor/contenttree';\n// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated.\nimport jQuery from 'jquery';\n\n// Load global strings.\nprefetchStrings('core', ['movecoursesection', 'movecoursemodule', 'confirm', 'delete']);\n\n// Mutations are dispatched by the course content actions.\n// Formats can use this module addActions static method to add custom actions.\n// Direct mutations can be simple strings (mutation) name or functions.\nconst directMutations = {\n sectionHide: 'sectionHide',\n sectionShow: 'sectionShow',\n cmHide: 'cmHide',\n cmShow: 'cmShow',\n cmStealth: 'cmStealth',\n cmMoveRight: 'cmMoveRight',\n cmMoveLeft: 'cmMoveLeft',\n cmNoGroups: 'cmNoGroups',\n cmSeparateGroups: 'cmSeparateGroups',\n cmVisibleGroups: 'cmVisibleGroups',\n};\n\nexport default class extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'content_actions';\n // Default query selectors.\n this.selectors = {\n ACTIONLINK: `[data-action]`,\n // Move modal selectors.\n SECTIONLINK: `[data-for='section']`,\n CMLINK: `[data-for='cm']`,\n SECTIONNODE: `[data-for='sectionnode']`,\n MODALTOGGLER: `[data-toggle='collapse']`,\n ADDSECTION: `[data-action='addSection']`,\n CONTENTTREE: `#destination-selector`,\n ACTIONMENU: `.action-menu`,\n ACTIONMENUTOGGLER: `[data-toggle=\"dropdown\"]`,\n // Availability modal selectors.\n OPTIONSRADIO: `[type='radio']`,\n };\n // Component css classes.\n this.classes = {\n DISABLED: `disabled`,\n };\n }\n\n /**\n * Add extra actions to the module.\n *\n * @param {array} actions array of methods to execute\n */\n static addActions(actions) {\n for (const [action, mutationReference] of Object.entries(actions)) {\n if (typeof mutationReference !== 'function' && typeof mutationReference !== 'string') {\n throw new Error(`${action} action must be a mutation name or a function`);\n }\n directMutations[action] = mutationReference;\n }\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the state data.\n *\n */\n stateReady(state) {\n // Delegate dispatch clicks.\n this.addEventListener(\n this.element,\n 'click',\n this._dispatchClick\n );\n // Check section limit.\n this._checkSectionlist({state});\n // Add an Event listener to recalculate limits it if a section HTML is altered.\n this.addEventListener(\n this.element,\n CourseEvents.sectionRefreshed,\n () => this._checkSectionlist({state})\n );\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n // Check section limit.\n {watch: `course.sectionlist:updated`, handler: this._checkSectionlist},\n ];\n }\n\n _dispatchClick(event) {\n const target = event.target.closest(this.selectors.ACTIONLINK);\n if (!target) {\n return;\n }\n if (target.classList.contains(this.classes.DISABLED)) {\n event.preventDefault();\n return;\n }\n\n // Invoke proper method.\n const actionName = target.dataset.action;\n const methodName = this._actionMethodName(actionName);\n\n if (this[methodName] !== undefined) {\n this[methodName](target, event);\n return;\n }\n\n // Check direct mutations or mutations handlers.\n if (directMutations[actionName] !== undefined) {\n if (typeof directMutations[actionName] === 'function') {\n directMutations[actionName](target, event);\n return;\n }\n this._requestMutationAction(target, event, directMutations[actionName]);\n return;\n }\n }\n\n _actionMethodName(name) {\n const requestName = name.charAt(0).toUpperCase() + name.slice(1);\n return `_request${requestName}`;\n }\n\n /**\n * Check the section list and disable some options if needed.\n *\n * @param {Object} detail the update details.\n * @param {Object} detail.state the state object.\n */\n _checkSectionlist({state}) {\n // Disable \"add section\" actions if the course max sections has been exceeded.\n this._setAddSectionLocked(state.course.sectionlist.length > state.course.maxsections);\n }\n\n /**\n * Return the ids represented by this element.\n *\n * Depending on the dataset attributes the action could represent a single id\n * or a bulk actions with all the current selected ids.\n *\n * @param {HTMLElement} target\n * @returns {Number[]} array of Ids\n */\n _getTargetIds(target) {\n let ids = [];\n if (target?.dataset?.id) {\n ids.push(target.dataset.id);\n }\n const bulkType = target?.dataset?.bulk;\n if (!bulkType) {\n return ids;\n }\n const bulk = this.reactive.get('bulk');\n if (bulk.enabled && bulk.selectedType === bulkType) {\n ids = [...ids, ...bulk.selection];\n }\n return ids;\n }\n\n /**\n * Handle a move section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestMoveSection(target, event) {\n // Check we have an id.\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n const pendingModalReady = new Pending(`courseformat/actions:prepareMoveSectionModal`);\n\n // The section edit menu to refocus on end.\n const editTools = this._getClosestActionMenuToogler(target);\n\n // Collect section information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.course(this.reactive.state);\n let titleText = null;\n\n // Add the target section id and title.\n let sectionInfo = null;\n if (sectionIds.length == 1) {\n sectionInfo = this.reactive.get('section', sectionIds[0]);\n data.sectionid = sectionInfo.id;\n data.sectiontitle = sectionInfo.title;\n data.information = await this.reactive.getFormatString('sectionmove_info', data.sectiontitle);\n titleText = this.reactive.getFormatString('sectionmove_title');\n } else {\n data.information = await this.reactive.getFormatString('sectionsmove_info', sectionIds.length);\n titleText = this.reactive.getFormatString('sectionsmove_title');\n }\n\n\n // Build the modal parameters from the event data.\n const modalParams = {\n title: titleText,\n body: Templates.render('core_courseformat/local/content/movesection', data),\n };\n\n // Create the modal.\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n const modalBody = getFirst(modal.getBody());\n\n // Disable current selected section ids.\n sectionIds.forEach(sectionId => {\n const currentElement = modalBody.querySelector(`${this.selectors.SECTIONLINK}[data-id='${sectionId}']`);\n this._disableLink(currentElement);\n });\n\n // Setup keyboard navigation.\n new ContentTree(\n modalBody.querySelector(this.selectors.CONTENTTREE),\n {\n SECTION: this.selectors.SECTIONNODE,\n TOGGLER: this.selectors.MODALTOGGLER,\n COLLAPSE: this.selectors.MODALTOGGLER,\n },\n true\n );\n\n // Capture click.\n modalBody.addEventListener('click', (event) => {\n const target = event.target;\n if (!target.matches('a') || target.dataset.for != 'section' || target.dataset.id === undefined) {\n return;\n }\n if (target.getAttribute('aria-disabled')) {\n return;\n }\n event.preventDefault();\n this.reactive.dispatch('sectionMoveAfter', sectionIds, target.dataset.id);\n this._destroyModal(modal, editTools);\n });\n\n pendingModalReady.resolve();\n }\n\n /**\n * Handle a move cm request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestMoveCm(target, event) {\n // Check we have an id.\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n const pendingModalReady = new Pending(`courseformat/actions:prepareMoveCmModal`);\n\n // The section edit menu to refocus on end.\n const editTools = this._getClosestActionMenuToogler(target);\n\n // Collect information from the state.\n const exporter = this.reactive.getExporter();\n const data = exporter.course(this.reactive.state);\n\n let titleText = null;\n if (cmIds.length == 1) {\n const cmInfo = this.reactive.get('cm', cmIds[0]);\n data.cmid = cmInfo.id;\n data.cmname = cmInfo.name;\n data.information = await this.reactive.getFormatString('cmmove_info', data.cmname);\n titleText = this.reactive.getFormatString('cmmove_title');\n } else {\n data.information = await this.reactive.getFormatString('cmsmove_info', cmIds.length);\n titleText = this.reactive.getFormatString('cmsmove_title');\n }\n\n // Build the modal parameters from the event data.\n const modalParams = {\n title: titleText,\n body: Templates.render('core_courseformat/local/content/movecm', data),\n };\n\n // Create the modal.\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n const modalBody = getFirst(modal.getBody());\n\n // Disable current selected section ids.\n cmIds.forEach(cmId => {\n const currentElement = modalBody.querySelector(`${this.selectors.CMLINK}[data-id='${cmId}']`);\n this._disableLink(currentElement);\n });\n\n // Setup keyboard navigation.\n new ContentTree(\n modalBody.querySelector(this.selectors.CONTENTTREE),\n {\n SECTION: this.selectors.SECTIONNODE,\n TOGGLER: this.selectors.MODALTOGGLER,\n COLLAPSE: this.selectors.MODALTOGGLER,\n ENTER: this.selectors.SECTIONLINK,\n }\n );\n\n // Open the cm section node if possible (Bootstrap 4 uses jQuery to interact with collapsibles).\n // All jQuery in this code can be replaced when MDL-71979 is integrated.\n cmIds.forEach(cmId => {\n const currentElement = modalBody.querySelector(`${this.selectors.CMLINK}[data-id='${cmId}']`);\n const sectionnode = currentElement.closest(this.selectors.SECTIONNODE);\n const toggler = jQuery(sectionnode).find(this.selectors.MODALTOGGLER);\n let collapsibleId = toggler.data('target') ?? toggler.attr('href');\n if (collapsibleId) {\n // We cannot be sure we have # in the id element name.\n collapsibleId = collapsibleId.replace('#', '');\n const expandNode = modalBody.querySelector(`#${collapsibleId}`);\n jQuery(expandNode).collapse('show');\n }\n });\n\n modalBody.addEventListener('click', (event) => {\n const target = event.target;\n if (!target.matches('a') || target.dataset.for === undefined || target.dataset.id === undefined) {\n return;\n }\n if (target.getAttribute('aria-disabled')) {\n return;\n }\n event.preventDefault();\n\n let targetSectionId;\n let targetCmId;\n if (target.dataset.for == 'cm') {\n const dropData = exporter.cmDraggableData(this.reactive.state, target.dataset.id);\n targetSectionId = dropData.sectionid;\n targetCmId = dropData.nextcmid;\n } else {\n const section = this.reactive.get('section', target.dataset.id);\n targetSectionId = target.dataset.id;\n targetCmId = section?.cmlist[0];\n }\n this.reactive.dispatch('cmMove', cmIds, targetSectionId, targetCmId);\n this._destroyModal(modal, editTools);\n });\n\n pendingModalReady.resolve();\n }\n\n /**\n * Handle a create section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestAddSection(target, event) {\n event.preventDefault();\n this.reactive.dispatch('addSection', target.dataset.id ?? 0);\n }\n\n /**\n * Handle a delete section request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestDeleteSection(target, event) {\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n // We don't need confirmation to delete empty sections.\n let needsConfirmation = sectionIds.some(sectionId => {\n const sectionInfo = this.reactive.get('section', sectionId);\n const cmList = sectionInfo.cmlist ?? [];\n return (cmList.length || sectionInfo.hassummary || sectionInfo.rawtitle);\n });\n if (!needsConfirmation) {\n this.reactive.dispatch('sectionDelete', sectionIds);\n return;\n }\n\n let bodyText = null;\n let titleText = null;\n if (sectionIds.length == 1) {\n titleText = this.reactive.getFormatString('sectiondelete_title');\n const sectionInfo = this.reactive.get('section', sectionIds[0]);\n bodyText = this.reactive.getFormatString('sectiondelete_info', {name: sectionInfo.title});\n } else {\n titleText = this.reactive.getFormatString('sectionsdelete_title');\n bodyText = this.reactive.getFormatString('sectionsdelete_info', {count: sectionIds.length});\n }\n\n const modalParams = {\n title: titleText,\n body: bodyText,\n type: ModalFactory.types.DELETE_CANCEL,\n };\n\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n modal.getRoot().on(\n ModalEvents.delete,\n e => {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n modal.destroy();\n this.reactive.dispatch('sectionDelete', sectionIds);\n }\n );\n }\n\n /**\n * Handle a toggle cm selection.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestToggleSelectionCm(target, event) {\n toggleBulkSelectionAction(this.reactive, target, event, 'cm');\n }\n\n /**\n * Handle a toggle section selection.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestToggleSelectionSection(target, event) {\n toggleBulkSelectionAction(this.reactive, target, event, 'section');\n }\n\n /**\n * Basic mutation action helper.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n * @param {string} mutationName the mutation name\n */\n async _requestMutationAction(target, event, mutationName) {\n if (!target.dataset.id) {\n return;\n }\n event.preventDefault();\n this.reactive.dispatch(mutationName, [target.dataset.id]);\n }\n\n /**\n * Handle a course module duplicate request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestCmDuplicate(target, event) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n const sectionId = target.dataset.sectionid ?? null;\n event.preventDefault();\n this.reactive.dispatch('cmDuplicate', cmIds, sectionId);\n }\n\n /**\n * Handle a delete cm request.\n *\n * @param {Element} target the dispatch action element\n * @param {Event} event the triggered event\n */\n async _requestCmDelete(target, event) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n\n event.preventDefault();\n\n let bodyText = null;\n let titleText = null;\n if (cmIds.length == 1) {\n const cmInfo = this.reactive.get('cm', cmIds[0]);\n titleText = getString('cmdelete_title', 'core_courseformat');\n bodyText = getString(\n 'cmdelete_info',\n 'core_courseformat',\n {\n type: cmInfo.modname,\n name: cmInfo.name,\n }\n );\n } else {\n titleText = getString('cmsdelete_title', 'core_courseformat');\n bodyText = getString(\n 'cmsdelete_info',\n 'core_courseformat',\n {count: cmIds.length}\n );\n }\n\n const modalParams = {\n title: titleText,\n body: bodyText,\n type: ModalFactory.types.DELETE_CANCEL,\n };\n\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n modal.getRoot().on(\n ModalEvents.delete,\n e => {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n modal.destroy();\n this.reactive.dispatch('cmDelete', cmIds);\n }\n );\n }\n\n /**\n * Handle a cm availability change request.\n *\n * @param {Element} target the dispatch action element\n */\n async _requestCmAvailability(target) {\n const cmIds = this._getTargetIds(target);\n if (cmIds.length == 0) {\n return;\n }\n // Show the availability modal to decide which action to trigger.\n const exporter = this.reactive.getExporter();\n const data = {\n allowstealth: exporter.canUseStealth(this.reactive.state, cmIds),\n };\n const modalParams = {\n title: getString('availability', 'core'),\n body: Templates.render('core_courseformat/local/content/cm/availabilitymodal', data),\n saveButtonText: getString('apply', 'core'),\n type: ModalFactory.types.SAVE_CANCEL,\n };\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n this._setupMutationRadioButtonModal(modal, cmIds);\n }\n\n /**\n * Handle a section availability change request.\n *\n * @param {Element} target the dispatch action element\n */\n async _requestSectionAvailability(target) {\n const sectionIds = this._getTargetIds(target);\n if (sectionIds.length == 0) {\n return;\n }\n const title = (sectionIds.length == 1) ? 'sectionavailability_title' : 'sectionsavailability_title';\n // Show the availability modal to decide which action to trigger.\n const modalParams = {\n title: this.reactive.getFormatString(title),\n body: Templates.render('core_courseformat/local/content/section/availabilitymodal', []),\n saveButtonText: getString('apply', 'core'),\n type: ModalFactory.types.SAVE_CANCEL,\n };\n const modal = await this._modalBodyRenderedPromise(modalParams);\n\n this._setupMutationRadioButtonModal(modal, sectionIds);\n }\n\n /**\n * Add events to a mutation selector radio buttons modal.\n * @param {Modal} modal\n * @param {Number[]} ids the section or cm ids to apply the mutation\n */\n _setupMutationRadioButtonModal(modal, ids) {\n // The save button is not enabled until the user selects an option.\n modal.setButtonDisabled('save', true);\n\n const submitFunction = (radio) => {\n const mutation = radio?.value;\n if (!mutation) {\n return false;\n }\n this.reactive.dispatch(mutation, ids);\n return true;\n };\n\n const modalBody = getFirst(modal.getBody());\n const radioOptions = modalBody.querySelectorAll(this.selectors.OPTIONSRADIO);\n radioOptions.forEach(radio => {\n radio.addEventListener('change', () => {\n modal.setButtonDisabled('save', false);\n });\n radio.parentNode.addEventListener('click', () => {\n radio.checked = true;\n modal.setButtonDisabled('save', false);\n });\n radio.parentNode.addEventListener('dblclick', dbClickEvent => {\n if (submitFunction(radio)) {\n dbClickEvent.preventDefault();\n modal.destroy();\n }\n });\n });\n\n modal.getRoot().on(\n ModalEvents.save,\n () => {\n const radio = modalBody.querySelector(`${this.selectors.OPTIONSRADIO}:checked`);\n submitFunction(radio);\n }\n );\n }\n\n /**\n * Disable all add sections actions.\n *\n * @param {boolean} locked the new locked value.\n */\n _setAddSectionLocked(locked) {\n const targets = this.getElements(this.selectors.ADDSECTION);\n targets.forEach(element => {\n element.classList.toggle(this.classes.DISABLED, locked);\n this.setElementLocked(element, locked);\n });\n }\n\n /**\n * Replace an element with a copy with a different tag name.\n *\n * @param {Element} element the original element\n */\n _disableLink(element) {\n if (element) {\n element.style.pointerEvents = 'none';\n element.style.userSelect = 'none';\n element.classList.add(this.classes.DISABLED);\n element.setAttribute('aria-disabled', true);\n element.addEventListener('click', event => event.preventDefault());\n }\n }\n\n /**\n * Render a modal and return a body ready promise.\n *\n * @param {object} modalParams the modal params\n * @return {Promise} the modal body ready promise\n */\n _modalBodyRenderedPromise(modalParams) {\n return new Promise((resolve, reject) => {\n ModalFactory.create(modalParams).then((modal) => {\n modal.setRemoveOnClose(true);\n // Handle body loading event.\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n resolve(modal);\n });\n // Configure some extra modal params.\n if (modalParams.saveButtonText !== undefined) {\n modal.setSaveButtonText(modalParams.saveButtonText);\n }\n if (modalParams.deleteButtonText !== undefined) {\n modal.setDeleteButtonText(modalParams.saveButtonText);\n }\n modal.show();\n return;\n }).catch(() => {\n reject(`Cannot load modal content`);\n });\n });\n }\n\n /**\n * Hide and later destroy a modal.\n *\n * Behat will fail if we remove the modal while some boostrap collapse is executing.\n *\n * @param {Modal} modal\n * @param {HTMLElement} element the dom element to focus on.\n */\n _destroyModal(modal, element) {\n modal.hide();\n const pendingDestroy = new Pending(`courseformat/actions:destroyModal`);\n if (element) {\n element.focus();\n }\n setTimeout(() =>{\n modal.destroy();\n pendingDestroy.resolve();\n }, 500);\n }\n\n /**\n * Get the closest actions menu toggler to an action element.\n *\n * @param {HTMLElement} element the action link element\n * @returns {HTMLElement|undefined}\n */\n _getClosestActionMenuToogler(element) {\n const actionMenu = element.closest(this.selectors.ACTIONMENU);\n if (!actionMenu) {\n return undefined;\n }\n return actionMenu.querySelector(this.selectors.ACTIONMENUTOGGLER);\n }\n}\n"],"names":["directMutations","sectionHide","sectionShow","cmHide","cmShow","cmStealth","cmMoveRight","cmMoveLeft","cmNoGroups","cmSeparateGroups","cmVisibleGroups","BaseComponent","create","name","selectors","ACTIONLINK","SECTIONLINK","CMLINK","SECTIONNODE","MODALTOGGLER","ADDSECTION","CONTENTTREE","ACTIONMENU","ACTIONMENUTOGGLER","OPTIONSRADIO","classes","DISABLED","actions","action","mutationReference","Object","entries","Error","stateReady","state","addEventListener","this","element","_dispatchClick","_checkSectionlist","CourseEvents","sectionRefreshed","getWatchers","watch","handler","event","target","closest","classList","contains","preventDefault","actionName","dataset","methodName","_actionMethodName","undefined","_requestMutationAction","requestName","charAt","toUpperCase","slice","_setAddSectionLocked","course","sectionlist","length","maxsections","_getTargetIds","ids","_target$dataset","id","push","bulkType","_target$dataset2","bulk","reactive","get","enabled","selectedType","selection","sectionIds","pendingModalReady","Pending","editTools","_getClosestActionMenuToogler","data","getExporter","titleText","sectionInfo","sectionid","sectiontitle","title","information","getFormatString","modalParams","body","Templates","render","modal","_modalBodyRenderedPromise","modalBody","getBody","forEach","sectionId","currentElement","querySelector","_disableLink","ContentTree","SECTION","TOGGLER","COLLAPSE","matches","for","getAttribute","dispatch","_destroyModal","resolve","cmIds","exporter","cmInfo","cmid","cmname","cmId","ENTER","sectionnode","toggler","find","collapsibleId","attr","replace","expandNode","collapse","targetSectionId","targetCmId","dropData","cmDraggableData","nextcmid","section","cmlist","some","hassummary","rawtitle","bodyText","count","type","ModalFactory","types","DELETE_CANCEL","getRoot","on","ModalEvents","delete","e","destroy","mutationName","modname","allowstealth","canUseStealth","saveButtonText","SAVE_CANCEL","_setupMutationRadioButtonModal","setButtonDisabled","submitFunction","radio","mutation","value","querySelectorAll","parentNode","checked","dbClickEvent","save","locked","getElements","toggle","setElementLocked","style","pointerEvents","userSelect","add","setAttribute","Promise","reject","then","setRemoveOnClose","bodyRendered","setSaveButtonText","deleteButtonText","setDeleteButtonText","show","catch","hide","pendingDestroy","focus","setTimeout","actionMenu"],"mappings":"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"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/mutations.min.js b/course/format/amd/build/local/courseeditor/mutations.min.js index 65d4a18ccf9..bce3a2c2be9 100644 --- a/course/format/amd/build/local/courseeditor/mutations.min.js +++ b/course/format/amd/build/local/courseeditor/mutations.min.js @@ -6,6 +6,6 @@ define("core_courseformat/local/courseeditor/mutations",["exports","core/ajax"], * @class core_courseformat/local/courseeditor/mutations * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};return _exports.default=class{async _callEditWebservice(action,courseId,ids,targetSectionId,targetCmId){const args={action:action,courseid:courseId,ids:ids};targetSectionId&&(args.targetsectionid=targetSectionId),targetCmId&&(args.targetcmid=targetCmId);let ajaxresult=await _ajax.default.call([{methodname:"core_courseformat_update_course",args:args}])[0];return JSON.parse(ajaxresult)}async _sectionBasicAction(stateManager,action,sectionIds,targetSectionId,targetCmId){const course=stateManager.get("course");this.sectionLock(stateManager,sectionIds,!0);const updates=await this._callEditWebservice(action,course.id,sectionIds,targetSectionId,targetCmId);this.bulkReset(stateManager),stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async _cmBasicAction(stateManager,action,cmIds,targetSectionId,targetCmId){const course=stateManager.get("course");this.cmLock(stateManager,cmIds,!0);const updates=await this._callEditWebservice(action,course.id,cmIds,targetSectionId,targetCmId);this.bulkReset(stateManager),stateManager.processUpdates(updates),this.cmLock(stateManager,cmIds,!1)}init(stateManager){stateManager.addUpdateTypes({prepareFields:this._prepareFields})}_prepareFields(stateManager,updateName,fields){return fields.locked=!1,fields}async sectionHide(stateManager,sectionIds){await this._sectionBasicAction(stateManager,"section_hide",sectionIds)}async sectionShow(stateManager,sectionIds){await this._sectionBasicAction(stateManager,"section_show",sectionIds)}async cmShow(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_show",cmIds)}async cmHide(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_hide",cmIds)}async cmStealth(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_stealth",cmIds)}async cmDuplicate(stateManager,cmIds,targetSectionId,targetCmId){const course=stateManager.get("course"),sectionIds=new Set;targetSectionId?sectionIds.add(targetSectionId):cmIds.forEach((cmId=>{const cm=stateManager.get("cm",cmId);sectionIds.add(cm.sectionid)})),this.sectionLock(stateManager,Array.from(sectionIds),!0);const updates=await this._callEditWebservice("cm_duplicate",course.id,cmIds,targetSectionId,targetCmId);this.bulkReset(stateManager),stateManager.processUpdates(updates),this.sectionLock(stateManager,Array.from(sectionIds),!1)}async cmMove(stateManager,cmids,targetSectionId,targetCmId){if(!targetSectionId&&!targetCmId)throw new Error("Mutation cmMove requires targetSectionId or targetCmId");const course=stateManager.get("course");this.cmLock(stateManager,cmids,!0);const updates=await this._callEditWebservice("cm_move",course.id,cmids,targetSectionId,targetCmId);this.bulkReset(stateManager),stateManager.processUpdates(updates),this.cmLock(stateManager,cmids,!1)}async sectionMove(stateManager,sectionIds,targetSectionId){if(!targetSectionId)throw new Error("Mutation sectionMove requires targetSectionId");const course=stateManager.get("course");this.sectionLock(stateManager,sectionIds,!0);const updates=await this._callEditWebservice("section_move",course.id,sectionIds,targetSectionId);this.bulkReset(stateManager),stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async sectionMoveAfter(stateManager,sectionIds,targetSectionId){if(!targetSectionId)throw new Error("Mutation sectionMoveAfter requires targetSectionId");const course=stateManager.get("course");this.sectionLock(stateManager,sectionIds,!0);const updates=await this._callEditWebservice("section_move_after",course.id,sectionIds,targetSectionId);this.bulkReset(stateManager),stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async addSection(stateManager,targetSectionId){targetSectionId||(targetSectionId=0);const course=stateManager.get("course"),updates=await this._callEditWebservice("section_add",course.id,[],targetSectionId);stateManager.processUpdates(updates)}async sectionDelete(stateManager,sectionIds){const course=stateManager.get("course"),updates=await this._callEditWebservice("section_delete",course.id,sectionIds);this.bulkReset(stateManager),stateManager.processUpdates(updates)}async cmDelete(stateManager,cmIds){const course=stateManager.get("course");this.cmLock(stateManager,cmIds,!0);const updates=await this._callEditWebservice("cm_delete",course.id,cmIds);this.bulkReset(stateManager),this.cmLock(stateManager,cmIds,!1),stateManager.processUpdates(updates)}cmDrag(stateManager,cmIds,dragValue){this.setPageItem(stateManager),this._setElementsValue(stateManager,"cm",cmIds,"dragging",dragValue)}sectionDrag(stateManager,sectionIds,dragValue){this.setPageItem(stateManager),this._setElementsValue(stateManager,"section",sectionIds,"dragging",dragValue)}cmCompletion(stateManager,cmIds,complete){const newValue=complete?1:0;this._setElementsValue(stateManager,"cm",cmIds,"completionstate",newValue)}async cmMoveRight(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_moveright",cmIds)}async cmMoveLeft(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_moveleft",cmIds)}cmLock(stateManager,cmIds,lockValue){this._setElementsValue(stateManager,"cm",cmIds,"locked",lockValue)}sectionLock(stateManager,sectionIds,lockValue){this._setElementsValue(stateManager,"section",sectionIds,"locked",lockValue)}_setElementsValue(stateManager,name,ids,fieldName,newValue){stateManager.setReadOnly(!1),ids.forEach((id=>{const element=stateManager.get(name,id);element&&(element[fieldName]=newValue)})),stateManager.setReadOnly(!0)}setPageItem(stateManager,type,id,isStatic){let newPageItem;if(void 0!==type&&(newPageItem=stateManager.get(type,id),!newPageItem))return;stateManager.setReadOnly(!1);const course=stateManager.get("course");course.pageItem=null,newPageItem&&(course.pageItem={id:id,type:type,sectionId:"section"==type?newPageItem.id:newPageItem.sectionid,isStatic:isStatic}),stateManager.setReadOnly(!0)}unlockAll(stateManager){const state=stateManager.state;stateManager.setReadOnly(!1),state.section.forEach((section=>{section.locked=!1})),state.cm.forEach((cm=>{cm.locked=!1})),stateManager.setReadOnly(!0)}async sectionIndexCollapsed(stateManager,sectionIds,collapsed){const collapsedIds=this._updateStateSectionPreference(stateManager,"indexcollapsed",sectionIds,collapsed);if(!collapsedIds)return;const course=stateManager.get("course");await this._callEditWebservice("section_index_collapsed",course.id,collapsedIds)}async sectionContentCollapsed(stateManager,sectionIds,collapsed){const collapsedIds=this._updateStateSectionPreference(stateManager,"contentcollapsed",sectionIds,collapsed);if(!collapsedIds)return;const course=stateManager.get("course");await this._callEditWebservice("section_content_collapsed",course.id,collapsedIds)}_updateStateSectionPreference(stateManager,preferenceName,sectionIds,preferenceValue){stateManager.setReadOnly(!1);const affectedSections=new Set;if(sectionIds.forEach((sectionId=>{const section=stateManager.get("section",sectionId);if(void 0===section)return null;const newValue=null!=preferenceValue?preferenceValue:section[preferenceName];section[preferenceName]!=newValue&&(section[preferenceName]=newValue,affectedSections.add(section.id))})),stateManager.setReadOnly(!0),0==affectedSections.size)return null;const collapsedSectionIds=[];return stateManager.state.section.forEach((section=>{section[preferenceName]&&collapsedSectionIds.push(section.id)})),collapsedSectionIds}bulkEnable(stateManager,enabled){const state=stateManager.state;stateManager.setReadOnly(!1),state.bulk.enabled=enabled,state.bulk.selectedType="",state.bulk.selection=[],stateManager.setReadOnly(!0)}bulkReset(stateManager){const state=stateManager.state;stateManager.setReadOnly(!1),state.bulk.selectedType="",state.bulk.selection=[],stateManager.setReadOnly(!0)}cmSelect(stateManager,cmIds){this._addIdsToSelection(stateManager,"cm",cmIds)}cmUnselect(stateManager,cmIds){this._removeIdsFromSelection(stateManager,"cm",cmIds)}sectionSelect(stateManager,sectionIds){this._addIdsToSelection(stateManager,"section",sectionIds)}sectionUnselect(stateManager,sectionIds){this._removeIdsFromSelection(stateManager,"section",sectionIds)}_addIdsToSelection(stateManager,typeName,ids){const bulk=stateManager.state.bulk;if(null==bulk||!bulk.enabled)throw new Error("Bulk is not enabled");if(""!==(null==bulk?void 0:bulk.selectedType)&&(null==bulk?void 0:bulk.selectedType)!==typeName)throw new Error("Cannot add ".concat(typeName," to the current selection"));ids=ids.map((value=>value.toString())),stateManager.setReadOnly(!1),bulk.selectedType=typeName;const newSelection=new Set([...bulk.selection,...ids]);bulk.selection=[...newSelection],stateManager.setReadOnly(!0)}_removeIdsFromSelection(stateManager,typeName,ids){const bulk=stateManager.state.bulk;if(null==bulk||!bulk.enabled)throw new Error("Bulk is not enabled");if(""!==(null==bulk?void 0:bulk.selectedType)&&(null==bulk?void 0:bulk.selectedType)!==typeName)throw new Error("Cannot remove ".concat(typeName," from the current selection"));ids=ids.map((value=>value.toString())),stateManager.setReadOnly(!1);const IdsToFilter=new Set(ids);bulk.selection=bulk.selection.filter((current=>!IdsToFilter.has(current))),0===bulk.selection.length&&(bulk.selectedType=""),stateManager.setReadOnly(!0)}async cmState(stateManager,cmids){this.cmLock(stateManager,cmids,!0);const course=stateManager.get("course"),updates=await this._callEditWebservice("cm_state",course.id,cmids);stateManager.processUpdates(updates),this.cmLock(stateManager,cmids,!1)}async sectionState(stateManager,sectionIds){this.sectionLock(stateManager,sectionIds,!0);const course=stateManager.get("course"),updates=await this._callEditWebservice("section_state",course.id,sectionIds);stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async courseState(stateManager){const course=stateManager.get("course"),updates=await this._callEditWebservice("course_state",course.id);stateManager.processUpdates(updates)}},_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};return _exports.default=class{async _callEditWebservice(action,courseId,ids,targetSectionId,targetCmId){const args={action:action,courseid:courseId,ids:ids};targetSectionId&&(args.targetsectionid=targetSectionId),targetCmId&&(args.targetcmid=targetCmId);let ajaxresult=await _ajax.default.call([{methodname:"core_courseformat_update_course",args:args}])[0];return JSON.parse(ajaxresult)}async _sectionBasicAction(stateManager,action,sectionIds,targetSectionId,targetCmId){const course=stateManager.get("course");this.sectionLock(stateManager,sectionIds,!0);const updates=await this._callEditWebservice(action,course.id,sectionIds,targetSectionId,targetCmId);this.bulkReset(stateManager),stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async _cmBasicAction(stateManager,action,cmIds,targetSectionId,targetCmId){const course=stateManager.get("course");this.cmLock(stateManager,cmIds,!0);const updates=await this._callEditWebservice(action,course.id,cmIds,targetSectionId,targetCmId);this.bulkReset(stateManager),stateManager.processUpdates(updates),this.cmLock(stateManager,cmIds,!1)}init(stateManager){stateManager.addUpdateTypes({prepareFields:this._prepareFields})}_prepareFields(stateManager,updateName,fields){return fields.locked=!1,fields}async sectionHide(stateManager,sectionIds){await this._sectionBasicAction(stateManager,"section_hide",sectionIds)}async sectionShow(stateManager,sectionIds){await this._sectionBasicAction(stateManager,"section_show",sectionIds)}async cmShow(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_show",cmIds)}async cmHide(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_hide",cmIds)}async cmStealth(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_stealth",cmIds)}async cmDuplicate(stateManager,cmIds,targetSectionId,targetCmId){const course=stateManager.get("course"),sectionIds=new Set;targetSectionId?sectionIds.add(targetSectionId):cmIds.forEach((cmId=>{const cm=stateManager.get("cm",cmId);sectionIds.add(cm.sectionid)})),this.sectionLock(stateManager,Array.from(sectionIds),!0);const updates=await this._callEditWebservice("cm_duplicate",course.id,cmIds,targetSectionId,targetCmId);this.bulkReset(stateManager),stateManager.processUpdates(updates),this.sectionLock(stateManager,Array.from(sectionIds),!1)}async cmMove(stateManager,cmids,targetSectionId,targetCmId){if(!targetSectionId&&!targetCmId)throw new Error("Mutation cmMove requires targetSectionId or targetCmId");const course=stateManager.get("course");this.cmLock(stateManager,cmids,!0);const updates=await this._callEditWebservice("cm_move",course.id,cmids,targetSectionId,targetCmId);this.bulkReset(stateManager),stateManager.processUpdates(updates),this.cmLock(stateManager,cmids,!1)}async sectionMove(stateManager,sectionIds,targetSectionId){if(!targetSectionId)throw new Error("Mutation sectionMove requires targetSectionId");const course=stateManager.get("course");this.sectionLock(stateManager,sectionIds,!0);const updates=await this._callEditWebservice("section_move",course.id,sectionIds,targetSectionId);this.bulkReset(stateManager),stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async sectionMoveAfter(stateManager,sectionIds,targetSectionId){if(!targetSectionId)throw new Error("Mutation sectionMoveAfter requires targetSectionId");const course=stateManager.get("course");this.sectionLock(stateManager,sectionIds,!0);const updates=await this._callEditWebservice("section_move_after",course.id,sectionIds,targetSectionId);this.bulkReset(stateManager),stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async addSection(stateManager,targetSectionId){targetSectionId||(targetSectionId=0);const course=stateManager.get("course"),updates=await this._callEditWebservice("section_add",course.id,[],targetSectionId);stateManager.processUpdates(updates)}async sectionDelete(stateManager,sectionIds){const course=stateManager.get("course"),updates=await this._callEditWebservice("section_delete",course.id,sectionIds);this.bulkReset(stateManager),stateManager.processUpdates(updates)}async cmDelete(stateManager,cmIds){const course=stateManager.get("course");this.cmLock(stateManager,cmIds,!0);const updates=await this._callEditWebservice("cm_delete",course.id,cmIds);this.bulkReset(stateManager),this.cmLock(stateManager,cmIds,!1),stateManager.processUpdates(updates)}cmDrag(stateManager,cmIds,dragValue){this.setPageItem(stateManager),this._setElementsValue(stateManager,"cm",cmIds,"dragging",dragValue)}sectionDrag(stateManager,sectionIds,dragValue){this.setPageItem(stateManager),this._setElementsValue(stateManager,"section",sectionIds,"dragging",dragValue)}cmCompletion(stateManager,cmIds,complete){const newValue=complete?1:0;this._setElementsValue(stateManager,"cm",cmIds,"completionstate",newValue)}async cmMoveRight(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_moveright",cmIds)}async cmMoveLeft(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_moveleft",cmIds)}async cmNoGroups(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_nogroups",cmIds)}async cmVisibleGroups(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_visiblegroups",cmIds)}async cmSeparateGroups(stateManager,cmIds){await this._cmBasicAction(stateManager,"cm_separategroups",cmIds)}cmLock(stateManager,cmIds,lockValue){this._setElementsValue(stateManager,"cm",cmIds,"locked",lockValue)}sectionLock(stateManager,sectionIds,lockValue){this._setElementsValue(stateManager,"section",sectionIds,"locked",lockValue)}_setElementsValue(stateManager,name,ids,fieldName,newValue){stateManager.setReadOnly(!1),ids.forEach((id=>{const element=stateManager.get(name,id);element&&(element[fieldName]=newValue)})),stateManager.setReadOnly(!0)}setPageItem(stateManager,type,id,isStatic){let newPageItem;if(void 0!==type&&(newPageItem=stateManager.get(type,id),!newPageItem))return;stateManager.setReadOnly(!1);const course=stateManager.get("course");course.pageItem=null,newPageItem&&(course.pageItem={id:id,type:type,sectionId:"section"==type?newPageItem.id:newPageItem.sectionid,isStatic:isStatic}),stateManager.setReadOnly(!0)}unlockAll(stateManager){const state=stateManager.state;stateManager.setReadOnly(!1),state.section.forEach((section=>{section.locked=!1})),state.cm.forEach((cm=>{cm.locked=!1})),stateManager.setReadOnly(!0)}async sectionIndexCollapsed(stateManager,sectionIds,collapsed){const collapsedIds=this._updateStateSectionPreference(stateManager,"indexcollapsed",sectionIds,collapsed);if(!collapsedIds)return;const course=stateManager.get("course");await this._callEditWebservice("section_index_collapsed",course.id,collapsedIds)}async sectionContentCollapsed(stateManager,sectionIds,collapsed){const collapsedIds=this._updateStateSectionPreference(stateManager,"contentcollapsed",sectionIds,collapsed);if(!collapsedIds)return;const course=stateManager.get("course");await this._callEditWebservice("section_content_collapsed",course.id,collapsedIds)}_updateStateSectionPreference(stateManager,preferenceName,sectionIds,preferenceValue){stateManager.setReadOnly(!1);const affectedSections=new Set;if(sectionIds.forEach((sectionId=>{const section=stateManager.get("section",sectionId);if(void 0===section)return null;const newValue=null!=preferenceValue?preferenceValue:section[preferenceName];section[preferenceName]!=newValue&&(section[preferenceName]=newValue,affectedSections.add(section.id))})),stateManager.setReadOnly(!0),0==affectedSections.size)return null;const collapsedSectionIds=[];return stateManager.state.section.forEach((section=>{section[preferenceName]&&collapsedSectionIds.push(section.id)})),collapsedSectionIds}bulkEnable(stateManager,enabled){const state=stateManager.state;stateManager.setReadOnly(!1),state.bulk.enabled=enabled,state.bulk.selectedType="",state.bulk.selection=[],stateManager.setReadOnly(!0)}bulkReset(stateManager){const state=stateManager.state;stateManager.setReadOnly(!1),state.bulk.selectedType="",state.bulk.selection=[],stateManager.setReadOnly(!0)}cmSelect(stateManager,cmIds){this._addIdsToSelection(stateManager,"cm",cmIds)}cmUnselect(stateManager,cmIds){this._removeIdsFromSelection(stateManager,"cm",cmIds)}sectionSelect(stateManager,sectionIds){this._addIdsToSelection(stateManager,"section",sectionIds)}sectionUnselect(stateManager,sectionIds){this._removeIdsFromSelection(stateManager,"section",sectionIds)}_addIdsToSelection(stateManager,typeName,ids){const bulk=stateManager.state.bulk;if(null==bulk||!bulk.enabled)throw new Error("Bulk is not enabled");if(""!==(null==bulk?void 0:bulk.selectedType)&&(null==bulk?void 0:bulk.selectedType)!==typeName)throw new Error("Cannot add ".concat(typeName," to the current selection"));ids=ids.map((value=>value.toString())),stateManager.setReadOnly(!1),bulk.selectedType=typeName;const newSelection=new Set([...bulk.selection,...ids]);bulk.selection=[...newSelection],stateManager.setReadOnly(!0)}_removeIdsFromSelection(stateManager,typeName,ids){const bulk=stateManager.state.bulk;if(null==bulk||!bulk.enabled)throw new Error("Bulk is not enabled");if(""!==(null==bulk?void 0:bulk.selectedType)&&(null==bulk?void 0:bulk.selectedType)!==typeName)throw new Error("Cannot remove ".concat(typeName," from the current selection"));ids=ids.map((value=>value.toString())),stateManager.setReadOnly(!1);const IdsToFilter=new Set(ids);bulk.selection=bulk.selection.filter((current=>!IdsToFilter.has(current))),0===bulk.selection.length&&(bulk.selectedType=""),stateManager.setReadOnly(!0)}async cmState(stateManager,cmids){this.cmLock(stateManager,cmids,!0);const course=stateManager.get("course"),updates=await this._callEditWebservice("cm_state",course.id,cmids);stateManager.processUpdates(updates),this.cmLock(stateManager,cmids,!1)}async sectionState(stateManager,sectionIds){this.sectionLock(stateManager,sectionIds,!0);const course=stateManager.get("course"),updates=await this._callEditWebservice("section_state",course.id,sectionIds);stateManager.processUpdates(updates),this.sectionLock(stateManager,sectionIds,!1)}async courseState(stateManager){const course=stateManager.get("course"),updates=await this._callEditWebservice("course_state",course.id);stateManager.processUpdates(updates)}},_exports.default})); //# sourceMappingURL=mutations.min.js.map \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/mutations.min.js.map b/course/format/amd/build/local/courseeditor/mutations.min.js.map index d292396fb3e..2dccbf42579 100644 --- a/course/format/amd/build/local/courseeditor/mutations.min.js.map +++ b/course/format/amd/build/local/courseeditor/mutations.min.js.map @@ -1 +1 @@ -{"version":3,"file":"mutations.min.js","sources":["../../../src/local/courseeditor/mutations.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\nimport ajax from 'core/ajax';\n\n/**\n * Default mutation manager\n *\n * @module core_courseformat/local/courseeditor/mutations\n * @class core_courseformat/local/courseeditor/mutations\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class {\n\n // All course editor mutations for Moodle 4.0 will be located in this file.\n\n /**\n * Private method to call core_courseformat_update_course webservice.\n *\n * @method _callEditWebservice\n * @param {string} action\n * @param {number} courseId\n * @param {array} ids\n * @param {number} targetSectionId optional target section id (for moving actions)\n * @param {number} targetCmId optional target cm id (for moving actions)\n */\n async _callEditWebservice(action, courseId, ids, targetSectionId, targetCmId) {\n const args = {\n action,\n courseid: courseId,\n ids,\n };\n if (targetSectionId) {\n args.targetsectionid = targetSectionId;\n }\n if (targetCmId) {\n args.targetcmid = targetCmId;\n }\n let ajaxresult = await ajax.call([{\n methodname: 'core_courseformat_update_course',\n args,\n }])[0];\n return JSON.parse(ajaxresult);\n }\n\n /**\n * Execute a basic section state action.\n * @param {StateManager} stateManager the current state manager\n * @param {string} action the action name\n * @param {array} sectionIds the section ids\n * @param {number} targetSectionId optional target section id (for moving actions)\n * @param {number} targetCmId optional target cm id (for moving actions)\n */\n async _sectionBasicAction(stateManager, action, sectionIds, targetSectionId, targetCmId) {\n const course = stateManager.get('course');\n this.sectionLock(stateManager, sectionIds, true);\n const updates = await this._callEditWebservice(\n action,\n course.id,\n sectionIds,\n targetSectionId,\n targetCmId\n );\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Execute a basic course module state action.\n * @param {StateManager} stateManager the current state manager\n * @param {string} action the action name\n * @param {array} cmIds the cm ids\n * @param {number} targetSectionId optional target section id (for moving actions)\n * @param {number} targetCmId optional target cm id (for moving actions)\n */\n async _cmBasicAction(stateManager, action, cmIds, targetSectionId, targetCmId) {\n const course = stateManager.get('course');\n this.cmLock(stateManager, cmIds, true);\n const updates = await this._callEditWebservice(\n action,\n course.id,\n cmIds,\n targetSectionId,\n targetCmId\n );\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n this.cmLock(stateManager, cmIds, false);\n }\n\n /**\n * Mutation module initialize.\n *\n * The reactive instance will execute this method when addMutations or setMutation is invoked.\n *\n * @param {StateManager} stateManager the state manager\n */\n init(stateManager) {\n // Add a method to prepare the fields when some update is comming from the server.\n stateManager.addUpdateTypes({\n prepareFields: this._prepareFields,\n });\n }\n\n /**\n * Add default values to state elements.\n *\n * This method is called every time a webservice returns a update state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n * @returns {Object} final fields data\n */\n _prepareFields(stateManager, updateName, fields) {\n // Any update should unlock the element.\n fields.locked = false;\n return fields;\n }\n\n /**\n * Hides sections.\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n */\n async sectionHide(stateManager, sectionIds) {\n await this._sectionBasicAction(stateManager, 'section_hide', sectionIds);\n }\n\n /**\n * Show sections.\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n */\n async sectionShow(stateManager, sectionIds) {\n await this._sectionBasicAction(stateManager, 'section_show', sectionIds);\n }\n\n /**\n * Show cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmShow(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_show', cmIds);\n }\n\n /**\n * Hide cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmHide(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_hide', cmIds);\n }\n\n /**\n * Stealth cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmStealth(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_stealth', cmIds);\n }\n\n /**\n * Duplicate course modules\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {number|undefined} targetSectionId the optional target sectionId\n * @param {number|undefined} targetCmId the target course module id\n */\n async cmDuplicate(stateManager, cmIds, targetSectionId, targetCmId) {\n const course = stateManager.get('course');\n // Lock all target sections.\n const sectionIds = new Set();\n if (targetSectionId) {\n sectionIds.add(targetSectionId);\n } else {\n cmIds.forEach((cmId) => {\n const cm = stateManager.get('cm', cmId);\n sectionIds.add(cm.sectionid);\n });\n }\n this.sectionLock(stateManager, Array.from(sectionIds), true);\n\n const updates = await this._callEditWebservice('cm_duplicate', course.id, cmIds, targetSectionId, targetCmId);\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n\n this.sectionLock(stateManager, Array.from(sectionIds), false);\n }\n\n /**\n * Move course modules to specific course location.\n *\n * Note that one of targetSectionId or targetCmId should be provided in order to identify the\n * new location:\n * - targetCmId: the activities will be located avobe the target cm. The targetSectionId\n * value will be ignored in this case.\n * - targetSectionId: the activities will be appended to the section. In this case\n * targetSectionId should not be present.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmids the list of cm ids to move\n * @param {number} targetSectionId the target section id\n * @param {number} targetCmId the target course module id\n */\n async cmMove(stateManager, cmids, targetSectionId, targetCmId) {\n if (!targetSectionId && !targetCmId) {\n throw new Error(`Mutation cmMove requires targetSectionId or targetCmId`);\n }\n const course = stateManager.get('course');\n this.cmLock(stateManager, cmids, true);\n const updates = await this._callEditWebservice('cm_move', course.id, cmids, targetSectionId, targetCmId);\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n this.cmLock(stateManager, cmids, false);\n }\n\n /**\n * Move course modules to specific course location.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids to move\n * @param {number} targetSectionId the target section id\n */\n async sectionMove(stateManager, sectionIds, targetSectionId) {\n if (!targetSectionId) {\n throw new Error(`Mutation sectionMove requires targetSectionId`);\n }\n const course = stateManager.get('course');\n this.sectionLock(stateManager, sectionIds, true);\n const updates = await this._callEditWebservice('section_move', course.id, sectionIds, targetSectionId);\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Move course modules after a specific course location.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids to move\n * @param {number} targetSectionId the target section id\n */\n async sectionMoveAfter(stateManager, sectionIds, targetSectionId) {\n if (!targetSectionId) {\n throw new Error(`Mutation sectionMoveAfter requires targetSectionId`);\n }\n const course = stateManager.get('course');\n this.sectionLock(stateManager, sectionIds, true);\n const updates = await this._callEditWebservice('section_move_after', course.id, sectionIds, targetSectionId);\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Add a new section to a specific course location.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {number} targetSectionId optional the target section id\n */\n async addSection(stateManager, targetSectionId) {\n if (!targetSectionId) {\n targetSectionId = 0;\n }\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_add', course.id, [], targetSectionId);\n stateManager.processUpdates(updates);\n }\n\n /**\n * Delete sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of course modules ids\n */\n async sectionDelete(stateManager, sectionIds) {\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_delete', course.id, sectionIds);\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n }\n\n /**\n * Delete cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of section ids\n */\n async cmDelete(stateManager, cmIds) {\n const course = stateManager.get('course');\n this.cmLock(stateManager, cmIds, true);\n const updates = await this._callEditWebservice('cm_delete', course.id, cmIds);\n this.bulkReset(stateManager);\n this.cmLock(stateManager, cmIds, false);\n stateManager.processUpdates(updates);\n }\n\n /**\n * Mark or unmark course modules as dragging.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {bool} dragValue the new dragging value\n */\n cmDrag(stateManager, cmIds, dragValue) {\n this.setPageItem(stateManager);\n this._setElementsValue(stateManager, 'cm', cmIds, 'dragging', dragValue);\n }\n\n /**\n * Mark or unmark course sections as dragging.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n * @param {bool} dragValue the new dragging value\n */\n sectionDrag(stateManager, sectionIds, dragValue) {\n this.setPageItem(stateManager);\n this._setElementsValue(stateManager, 'section', sectionIds, 'dragging', dragValue);\n }\n\n /**\n * Mark or unmark course modules as complete.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {bool} complete the new completion value\n */\n cmCompletion(stateManager, cmIds, complete) {\n const newValue = (complete) ? 1 : 0;\n this._setElementsValue(stateManager, 'cm', cmIds, 'completionstate', newValue);\n }\n\n /**\n * Move cms to the right: indent = 1.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmMoveRight(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_moveright', cmIds);\n }\n\n /**\n * Move cms to the left: indent = 0.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmMoveLeft(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_moveleft', cmIds);\n }\n\n /**\n * Lock or unlock course modules.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {bool} lockValue the new locked value\n */\n cmLock(stateManager, cmIds, lockValue) {\n this._setElementsValue(stateManager, 'cm', cmIds, 'locked', lockValue);\n }\n\n /**\n * Lock or unlock course sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n * @param {bool} lockValue the new locked value\n */\n sectionLock(stateManager, sectionIds, lockValue) {\n this._setElementsValue(stateManager, 'section', sectionIds, 'locked', lockValue);\n }\n\n _setElementsValue(stateManager, name, ids, fieldName, newValue) {\n stateManager.setReadOnly(false);\n ids.forEach((id) => {\n const element = stateManager.get(name, id);\n if (element) {\n element[fieldName] = newValue;\n }\n });\n stateManager.setReadOnly(true);\n }\n\n /**\n * Set the page current item.\n *\n * Only one element of the course state can be the page item at a time.\n *\n * There are several actions that can alter the page current item. For example, when the user is in an activity\n * page, the page item is always the activity one. However, in a course page, when the user scrolls to an element,\n * this element get the page item.\n *\n * If the page item is static means that it is not meant to change. This is important because\n * static page items has some special logic. For example, if a cm is the static page item\n * and it is inside a collapsed section, the course index will expand the section to make it visible.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {String|undefined} type the element type (section or cm). Undefined will remove the current page item.\n * @param {Number|undefined} id the element id\n * @param {boolean|undefined} isStatic if the page item is static\n */\n setPageItem(stateManager, type, id, isStatic) {\n let newPageItem;\n if (type !== undefined) {\n newPageItem = stateManager.get(type, id);\n if (!newPageItem) {\n return;\n }\n }\n stateManager.setReadOnly(false);\n // Remove the current page item.\n const course = stateManager.get('course');\n course.pageItem = null;\n // Save the new page item.\n if (newPageItem) {\n course.pageItem = {\n id,\n type,\n sectionId: (type == 'section') ? newPageItem.id : newPageItem.sectionid,\n isStatic,\n };\n }\n stateManager.setReadOnly(true);\n }\n\n /**\n * Unlock all course elements.\n *\n * @param {StateManager} stateManager the current state manager\n */\n unlockAll(stateManager) {\n const state = stateManager.state;\n stateManager.setReadOnly(false);\n state.section.forEach((section) => {\n section.locked = false;\n });\n state.cm.forEach((cm) => {\n cm.locked = false;\n });\n stateManager.setReadOnly(true);\n }\n\n /**\n * Update the course index collapsed attribute of some sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the affected section ids\n * @param {boolean} collapsed the new collapsed value\n */\n async sectionIndexCollapsed(stateManager, sectionIds, collapsed) {\n const collapsedIds = this._updateStateSectionPreference(stateManager, 'indexcollapsed', sectionIds, collapsed);\n if (!collapsedIds) {\n return;\n }\n const course = stateManager.get('course');\n await this._callEditWebservice('section_index_collapsed', course.id, collapsedIds);\n }\n\n /**\n * Update the course content collapsed attribute of some sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the affected section ids\n * @param {boolean} collapsed the new collapsed value\n */\n async sectionContentCollapsed(stateManager, sectionIds, collapsed) {\n const collapsedIds = this._updateStateSectionPreference(stateManager, 'contentcollapsed', sectionIds, collapsed);\n if (!collapsedIds) {\n return;\n }\n const course = stateManager.get('course');\n await this._callEditWebservice('section_content_collapsed', course.id, collapsedIds);\n }\n\n /**\n * Private batch update for a section preference attribute.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {string} preferenceName the preference name\n * @param {array} sectionIds the affected section ids\n * @param {boolean} preferenceValue the new preferenceValue value\n * @return {Number[]|null} sections ids with the preference value true or null if no update is required\n */\n _updateStateSectionPreference(stateManager, preferenceName, sectionIds, preferenceValue) {\n stateManager.setReadOnly(false);\n const affectedSections = new Set();\n // Check if we need to update preferences.\n sectionIds.forEach(sectionId => {\n const section = stateManager.get('section', sectionId);\n if (section === undefined) {\n return null;\n }\n const newValue = preferenceValue ?? section[preferenceName];\n if (section[preferenceName] != newValue) {\n section[preferenceName] = newValue;\n affectedSections.add(section.id);\n }\n });\n stateManager.setReadOnly(true);\n if (affectedSections.size == 0) {\n return null;\n }\n // Get all collapsed section ids.\n const collapsedSectionIds = [];\n const state = stateManager.state;\n state.section.forEach(section => {\n if (section[preferenceName]) {\n collapsedSectionIds.push(section.id);\n }\n });\n return collapsedSectionIds;\n }\n\n /**\n * Enable/disable bulk editing.\n *\n * Note: reenabling the bulk will clean the current selection.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {Boolean} enabled the new bulk state.\n */\n bulkEnable(stateManager, enabled) {\n const state = stateManager.state;\n stateManager.setReadOnly(false);\n state.bulk.enabled = enabled;\n state.bulk.selectedType = '';\n state.bulk.selection = [];\n stateManager.setReadOnly(true);\n }\n\n /**\n * Reset the current selection.\n * @param {StateManager} stateManager the current state manager\n */\n bulkReset(stateManager) {\n const state = stateManager.state;\n stateManager.setReadOnly(false);\n state.bulk.selectedType = '';\n state.bulk.selection = [];\n stateManager.setReadOnly(true);\n }\n\n /**\n * Select a list of cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n cmSelect(stateManager, cmIds) {\n this._addIdsToSelection(stateManager, 'cm', cmIds);\n }\n\n /**\n * Unselect a list of cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n cmUnselect(stateManager, cmIds) {\n this._removeIdsFromSelection(stateManager, 'cm', cmIds);\n }\n\n /**\n * Select a list of sections.\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of cm ids\n */\n sectionSelect(stateManager, sectionIds) {\n this._addIdsToSelection(stateManager, 'section', sectionIds);\n }\n\n /**\n * Unselect a list of sections.\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of cm ids\n */\n sectionUnselect(stateManager, sectionIds) {\n this._removeIdsFromSelection(stateManager, 'section', sectionIds);\n }\n\n /**\n * Add some ids to the current bulk selection.\n * @param {StateManager} stateManager the current state manager\n * @param {String} typeName the type name (section/cm)\n * @param {array} ids the list of ids\n */\n _addIdsToSelection(stateManager, typeName, ids) {\n const bulk = stateManager.state.bulk;\n if (!bulk?.enabled) {\n throw new Error(`Bulk is not enabled`);\n }\n if (bulk?.selectedType !== \"\" && bulk?.selectedType !== typeName) {\n throw new Error(`Cannot add ${typeName} to the current selection`);\n }\n\n // Stored ids are strings for compatability with HTML data attributes.\n ids = ids.map(value => value.toString());\n\n stateManager.setReadOnly(false);\n bulk.selectedType = typeName;\n const newSelection = new Set([...bulk.selection, ...ids]);\n bulk.selection = [...newSelection];\n stateManager.setReadOnly(true);\n }\n\n /**\n * Remove some ids to the current bulk selection.\n *\n * The method resets the selection type if the current selection is empty.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {String} typeName the type name (section/cm)\n * @param {array} ids the list of ids\n */\n _removeIdsFromSelection(stateManager, typeName, ids) {\n const bulk = stateManager.state.bulk;\n if (!bulk?.enabled) {\n throw new Error(`Bulk is not enabled`);\n }\n if (bulk?.selectedType !== \"\" && bulk?.selectedType !== typeName) {\n throw new Error(`Cannot remove ${typeName} from the current selection`);\n }\n\n // Stored ids are strings for compatability with HTML data attributes.\n ids = ids.map(value => value.toString());\n\n stateManager.setReadOnly(false);\n const IdsToFilter = new Set(ids);\n bulk.selection = bulk.selection.filter(current => !IdsToFilter.has(current));\n if (bulk.selection.length === 0) {\n bulk.selectedType = '';\n }\n stateManager.setReadOnly(true);\n }\n\n /**\n * Get updated state data related to some cm ids.\n *\n * @method cmState\n * @param {StateManager} stateManager the current state\n * @param {array} cmids the list of cm ids to update\n */\n async cmState(stateManager, cmids) {\n this.cmLock(stateManager, cmids, true);\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('cm_state', course.id, cmids);\n stateManager.processUpdates(updates);\n this.cmLock(stateManager, cmids, false);\n }\n\n /**\n * Get updated state data related to some section ids.\n *\n * @method sectionState\n * @param {StateManager} stateManager the current state\n * @param {array} sectionIds the list of section ids to update\n */\n async sectionState(stateManager, sectionIds) {\n this.sectionLock(stateManager, sectionIds, true);\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_state', course.id, sectionIds);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Get the full updated state data of the course.\n *\n * @param {StateManager} stateManager the current state\n */\n async courseState(stateManager) {\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('course_state', course.id);\n stateManager.processUpdates(updates);\n }\n\n}\n"],"names":["action","courseId","ids","targetSectionId","targetCmId","args","courseid","targetsectionid","targetcmid","ajaxresult","ajax","call","methodname","JSON","parse","stateManager","sectionIds","course","get","sectionLock","updates","this","_callEditWebservice","id","bulkReset","processUpdates","cmIds","cmLock","init","addUpdateTypes","prepareFields","_prepareFields","updateName","fields","locked","_sectionBasicAction","_cmBasicAction","Set","add","forEach","cmId","cm","sectionid","Array","from","cmids","Error","cmDrag","dragValue","setPageItem","_setElementsValue","sectionDrag","cmCompletion","complete","newValue","lockValue","name","fieldName","setReadOnly","element","type","isStatic","newPageItem","undefined","pageItem","sectionId","unlockAll","state","section","collapsed","collapsedIds","_updateStateSectionPreference","preferenceName","preferenceValue","affectedSections","size","collapsedSectionIds","push","bulkEnable","enabled","bulk","selectedType","selection","cmSelect","_addIdsToSelection","cmUnselect","_removeIdsFromSelection","sectionSelect","sectionUnselect","typeName","map","value","toString","newSelection","IdsToFilter","filter","current","has","length"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"mutations.min.js","sources":["../../../src/local/courseeditor/mutations.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\nimport ajax from 'core/ajax';\n\n/**\n * Default mutation manager\n *\n * @module core_courseformat/local/courseeditor/mutations\n * @class core_courseformat/local/courseeditor/mutations\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class {\n\n // All course editor mutations for Moodle 4.0 will be located in this file.\n\n /**\n * Private method to call core_courseformat_update_course webservice.\n *\n * @method _callEditWebservice\n * @param {string} action\n * @param {number} courseId\n * @param {array} ids\n * @param {number} targetSectionId optional target section id (for moving actions)\n * @param {number} targetCmId optional target cm id (for moving actions)\n */\n async _callEditWebservice(action, courseId, ids, targetSectionId, targetCmId) {\n const args = {\n action,\n courseid: courseId,\n ids,\n };\n if (targetSectionId) {\n args.targetsectionid = targetSectionId;\n }\n if (targetCmId) {\n args.targetcmid = targetCmId;\n }\n let ajaxresult = await ajax.call([{\n methodname: 'core_courseformat_update_course',\n args,\n }])[0];\n return JSON.parse(ajaxresult);\n }\n\n /**\n * Execute a basic section state action.\n * @param {StateManager} stateManager the current state manager\n * @param {string} action the action name\n * @param {array} sectionIds the section ids\n * @param {number} targetSectionId optional target section id (for moving actions)\n * @param {number} targetCmId optional target cm id (for moving actions)\n */\n async _sectionBasicAction(stateManager, action, sectionIds, targetSectionId, targetCmId) {\n const course = stateManager.get('course');\n this.sectionLock(stateManager, sectionIds, true);\n const updates = await this._callEditWebservice(\n action,\n course.id,\n sectionIds,\n targetSectionId,\n targetCmId\n );\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Execute a basic course module state action.\n * @param {StateManager} stateManager the current state manager\n * @param {string} action the action name\n * @param {array} cmIds the cm ids\n * @param {number} targetSectionId optional target section id (for moving actions)\n * @param {number} targetCmId optional target cm id (for moving actions)\n */\n async _cmBasicAction(stateManager, action, cmIds, targetSectionId, targetCmId) {\n const course = stateManager.get('course');\n this.cmLock(stateManager, cmIds, true);\n const updates = await this._callEditWebservice(\n action,\n course.id,\n cmIds,\n targetSectionId,\n targetCmId\n );\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n this.cmLock(stateManager, cmIds, false);\n }\n\n /**\n * Mutation module initialize.\n *\n * The reactive instance will execute this method when addMutations or setMutation is invoked.\n *\n * @param {StateManager} stateManager the state manager\n */\n init(stateManager) {\n // Add a method to prepare the fields when some update is comming from the server.\n stateManager.addUpdateTypes({\n prepareFields: this._prepareFields,\n });\n }\n\n /**\n * Add default values to state elements.\n *\n * This method is called every time a webservice returns a update state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n * @returns {Object} final fields data\n */\n _prepareFields(stateManager, updateName, fields) {\n // Any update should unlock the element.\n fields.locked = false;\n return fields;\n }\n\n /**\n * Hides sections.\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n */\n async sectionHide(stateManager, sectionIds) {\n await this._sectionBasicAction(stateManager, 'section_hide', sectionIds);\n }\n\n /**\n * Show sections.\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n */\n async sectionShow(stateManager, sectionIds) {\n await this._sectionBasicAction(stateManager, 'section_show', sectionIds);\n }\n\n /**\n * Show cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmShow(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_show', cmIds);\n }\n\n /**\n * Hide cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmHide(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_hide', cmIds);\n }\n\n /**\n * Stealth cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmStealth(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_stealth', cmIds);\n }\n\n /**\n * Duplicate course modules\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {number|undefined} targetSectionId the optional target sectionId\n * @param {number|undefined} targetCmId the target course module id\n */\n async cmDuplicate(stateManager, cmIds, targetSectionId, targetCmId) {\n const course = stateManager.get('course');\n // Lock all target sections.\n const sectionIds = new Set();\n if (targetSectionId) {\n sectionIds.add(targetSectionId);\n } else {\n cmIds.forEach((cmId) => {\n const cm = stateManager.get('cm', cmId);\n sectionIds.add(cm.sectionid);\n });\n }\n this.sectionLock(stateManager, Array.from(sectionIds), true);\n\n const updates = await this._callEditWebservice('cm_duplicate', course.id, cmIds, targetSectionId, targetCmId);\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n\n this.sectionLock(stateManager, Array.from(sectionIds), false);\n }\n\n /**\n * Move course modules to specific course location.\n *\n * Note that one of targetSectionId or targetCmId should be provided in order to identify the\n * new location:\n * - targetCmId: the activities will be located avobe the target cm. The targetSectionId\n * value will be ignored in this case.\n * - targetSectionId: the activities will be appended to the section. In this case\n * targetSectionId should not be present.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmids the list of cm ids to move\n * @param {number} targetSectionId the target section id\n * @param {number} targetCmId the target course module id\n */\n async cmMove(stateManager, cmids, targetSectionId, targetCmId) {\n if (!targetSectionId && !targetCmId) {\n throw new Error(`Mutation cmMove requires targetSectionId or targetCmId`);\n }\n const course = stateManager.get('course');\n this.cmLock(stateManager, cmids, true);\n const updates = await this._callEditWebservice('cm_move', course.id, cmids, targetSectionId, targetCmId);\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n this.cmLock(stateManager, cmids, false);\n }\n\n /**\n * Move course modules to specific course location.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids to move\n * @param {number} targetSectionId the target section id\n */\n async sectionMove(stateManager, sectionIds, targetSectionId) {\n if (!targetSectionId) {\n throw new Error(`Mutation sectionMove requires targetSectionId`);\n }\n const course = stateManager.get('course');\n this.sectionLock(stateManager, sectionIds, true);\n const updates = await this._callEditWebservice('section_move', course.id, sectionIds, targetSectionId);\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Move course modules after a specific course location.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids to move\n * @param {number} targetSectionId the target section id\n */\n async sectionMoveAfter(stateManager, sectionIds, targetSectionId) {\n if (!targetSectionId) {\n throw new Error(`Mutation sectionMoveAfter requires targetSectionId`);\n }\n const course = stateManager.get('course');\n this.sectionLock(stateManager, sectionIds, true);\n const updates = await this._callEditWebservice('section_move_after', course.id, sectionIds, targetSectionId);\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Add a new section to a specific course location.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {number} targetSectionId optional the target section id\n */\n async addSection(stateManager, targetSectionId) {\n if (!targetSectionId) {\n targetSectionId = 0;\n }\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_add', course.id, [], targetSectionId);\n stateManager.processUpdates(updates);\n }\n\n /**\n * Delete sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of course modules ids\n */\n async sectionDelete(stateManager, sectionIds) {\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_delete', course.id, sectionIds);\n this.bulkReset(stateManager);\n stateManager.processUpdates(updates);\n }\n\n /**\n * Delete cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of section ids\n */\n async cmDelete(stateManager, cmIds) {\n const course = stateManager.get('course');\n this.cmLock(stateManager, cmIds, true);\n const updates = await this._callEditWebservice('cm_delete', course.id, cmIds);\n this.bulkReset(stateManager);\n this.cmLock(stateManager, cmIds, false);\n stateManager.processUpdates(updates);\n }\n\n /**\n * Mark or unmark course modules as dragging.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {bool} dragValue the new dragging value\n */\n cmDrag(stateManager, cmIds, dragValue) {\n this.setPageItem(stateManager);\n this._setElementsValue(stateManager, 'cm', cmIds, 'dragging', dragValue);\n }\n\n /**\n * Mark or unmark course sections as dragging.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n * @param {bool} dragValue the new dragging value\n */\n sectionDrag(stateManager, sectionIds, dragValue) {\n this.setPageItem(stateManager);\n this._setElementsValue(stateManager, 'section', sectionIds, 'dragging', dragValue);\n }\n\n /**\n * Mark or unmark course modules as complete.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {bool} complete the new completion value\n */\n cmCompletion(stateManager, cmIds, complete) {\n const newValue = (complete) ? 1 : 0;\n this._setElementsValue(stateManager, 'cm', cmIds, 'completionstate', newValue);\n }\n\n /**\n * Move cms to the right: indent = 1.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmMoveRight(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_moveright', cmIds);\n }\n\n /**\n * Move cms to the left: indent = 0.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmMoveLeft(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_moveleft', cmIds);\n }\n\n /**\n * Set cms group mode to NOGROUPS.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmNoGroups(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_nogroups', cmIds);\n }\n\n /**\n * Set cms group mode to VISIBLEGROUPS.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmVisibleGroups(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_visiblegroups', cmIds);\n }\n\n /**\n * Set cms group mode to SEPARATEGROUPS.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n async cmSeparateGroups(stateManager, cmIds) {\n await this._cmBasicAction(stateManager, 'cm_separategroups', cmIds);\n }\n\n /**\n * Lock or unlock course modules.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of course modules ids\n * @param {bool} lockValue the new locked value\n */\n cmLock(stateManager, cmIds, lockValue) {\n this._setElementsValue(stateManager, 'cm', cmIds, 'locked', lockValue);\n }\n\n /**\n * Lock or unlock course sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of section ids\n * @param {bool} lockValue the new locked value\n */\n sectionLock(stateManager, sectionIds, lockValue) {\n this._setElementsValue(stateManager, 'section', sectionIds, 'locked', lockValue);\n }\n\n _setElementsValue(stateManager, name, ids, fieldName, newValue) {\n stateManager.setReadOnly(false);\n ids.forEach((id) => {\n const element = stateManager.get(name, id);\n if (element) {\n element[fieldName] = newValue;\n }\n });\n stateManager.setReadOnly(true);\n }\n\n /**\n * Set the page current item.\n *\n * Only one element of the course state can be the page item at a time.\n *\n * There are several actions that can alter the page current item. For example, when the user is in an activity\n * page, the page item is always the activity one. However, in a course page, when the user scrolls to an element,\n * this element get the page item.\n *\n * If the page item is static means that it is not meant to change. This is important because\n * static page items has some special logic. For example, if a cm is the static page item\n * and it is inside a collapsed section, the course index will expand the section to make it visible.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {String|undefined} type the element type (section or cm). Undefined will remove the current page item.\n * @param {Number|undefined} id the element id\n * @param {boolean|undefined} isStatic if the page item is static\n */\n setPageItem(stateManager, type, id, isStatic) {\n let newPageItem;\n if (type !== undefined) {\n newPageItem = stateManager.get(type, id);\n if (!newPageItem) {\n return;\n }\n }\n stateManager.setReadOnly(false);\n // Remove the current page item.\n const course = stateManager.get('course');\n course.pageItem = null;\n // Save the new page item.\n if (newPageItem) {\n course.pageItem = {\n id,\n type,\n sectionId: (type == 'section') ? newPageItem.id : newPageItem.sectionid,\n isStatic,\n };\n }\n stateManager.setReadOnly(true);\n }\n\n /**\n * Unlock all course elements.\n *\n * @param {StateManager} stateManager the current state manager\n */\n unlockAll(stateManager) {\n const state = stateManager.state;\n stateManager.setReadOnly(false);\n state.section.forEach((section) => {\n section.locked = false;\n });\n state.cm.forEach((cm) => {\n cm.locked = false;\n });\n stateManager.setReadOnly(true);\n }\n\n /**\n * Update the course index collapsed attribute of some sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the affected section ids\n * @param {boolean} collapsed the new collapsed value\n */\n async sectionIndexCollapsed(stateManager, sectionIds, collapsed) {\n const collapsedIds = this._updateStateSectionPreference(stateManager, 'indexcollapsed', sectionIds, collapsed);\n if (!collapsedIds) {\n return;\n }\n const course = stateManager.get('course');\n await this._callEditWebservice('section_index_collapsed', course.id, collapsedIds);\n }\n\n /**\n * Update the course content collapsed attribute of some sections.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the affected section ids\n * @param {boolean} collapsed the new collapsed value\n */\n async sectionContentCollapsed(stateManager, sectionIds, collapsed) {\n const collapsedIds = this._updateStateSectionPreference(stateManager, 'contentcollapsed', sectionIds, collapsed);\n if (!collapsedIds) {\n return;\n }\n const course = stateManager.get('course');\n await this._callEditWebservice('section_content_collapsed', course.id, collapsedIds);\n }\n\n /**\n * Private batch update for a section preference attribute.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {string} preferenceName the preference name\n * @param {array} sectionIds the affected section ids\n * @param {boolean} preferenceValue the new preferenceValue value\n * @return {Number[]|null} sections ids with the preference value true or null if no update is required\n */\n _updateStateSectionPreference(stateManager, preferenceName, sectionIds, preferenceValue) {\n stateManager.setReadOnly(false);\n const affectedSections = new Set();\n // Check if we need to update preferences.\n sectionIds.forEach(sectionId => {\n const section = stateManager.get('section', sectionId);\n if (section === undefined) {\n return null;\n }\n const newValue = preferenceValue ?? section[preferenceName];\n if (section[preferenceName] != newValue) {\n section[preferenceName] = newValue;\n affectedSections.add(section.id);\n }\n });\n stateManager.setReadOnly(true);\n if (affectedSections.size == 0) {\n return null;\n }\n // Get all collapsed section ids.\n const collapsedSectionIds = [];\n const state = stateManager.state;\n state.section.forEach(section => {\n if (section[preferenceName]) {\n collapsedSectionIds.push(section.id);\n }\n });\n return collapsedSectionIds;\n }\n\n /**\n * Enable/disable bulk editing.\n *\n * Note: reenabling the bulk will clean the current selection.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {Boolean} enabled the new bulk state.\n */\n bulkEnable(stateManager, enabled) {\n const state = stateManager.state;\n stateManager.setReadOnly(false);\n state.bulk.enabled = enabled;\n state.bulk.selectedType = '';\n state.bulk.selection = [];\n stateManager.setReadOnly(true);\n }\n\n /**\n * Reset the current selection.\n * @param {StateManager} stateManager the current state manager\n */\n bulkReset(stateManager) {\n const state = stateManager.state;\n stateManager.setReadOnly(false);\n state.bulk.selectedType = '';\n state.bulk.selection = [];\n stateManager.setReadOnly(true);\n }\n\n /**\n * Select a list of cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n cmSelect(stateManager, cmIds) {\n this._addIdsToSelection(stateManager, 'cm', cmIds);\n }\n\n /**\n * Unselect a list of cms.\n * @param {StateManager} stateManager the current state manager\n * @param {array} cmIds the list of cm ids\n */\n cmUnselect(stateManager, cmIds) {\n this._removeIdsFromSelection(stateManager, 'cm', cmIds);\n }\n\n /**\n * Select a list of sections.\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of cm ids\n */\n sectionSelect(stateManager, sectionIds) {\n this._addIdsToSelection(stateManager, 'section', sectionIds);\n }\n\n /**\n * Unselect a list of sections.\n * @param {StateManager} stateManager the current state manager\n * @param {array} sectionIds the list of cm ids\n */\n sectionUnselect(stateManager, sectionIds) {\n this._removeIdsFromSelection(stateManager, 'section', sectionIds);\n }\n\n /**\n * Add some ids to the current bulk selection.\n * @param {StateManager} stateManager the current state manager\n * @param {String} typeName the type name (section/cm)\n * @param {array} ids the list of ids\n */\n _addIdsToSelection(stateManager, typeName, ids) {\n const bulk = stateManager.state.bulk;\n if (!bulk?.enabled) {\n throw new Error(`Bulk is not enabled`);\n }\n if (bulk?.selectedType !== \"\" && bulk?.selectedType !== typeName) {\n throw new Error(`Cannot add ${typeName} to the current selection`);\n }\n\n // Stored ids are strings for compatability with HTML data attributes.\n ids = ids.map(value => value.toString());\n\n stateManager.setReadOnly(false);\n bulk.selectedType = typeName;\n const newSelection = new Set([...bulk.selection, ...ids]);\n bulk.selection = [...newSelection];\n stateManager.setReadOnly(true);\n }\n\n /**\n * Remove some ids to the current bulk selection.\n *\n * The method resets the selection type if the current selection is empty.\n *\n * @param {StateManager} stateManager the current state manager\n * @param {String} typeName the type name (section/cm)\n * @param {array} ids the list of ids\n */\n _removeIdsFromSelection(stateManager, typeName, ids) {\n const bulk = stateManager.state.bulk;\n if (!bulk?.enabled) {\n throw new Error(`Bulk is not enabled`);\n }\n if (bulk?.selectedType !== \"\" && bulk?.selectedType !== typeName) {\n throw new Error(`Cannot remove ${typeName} from the current selection`);\n }\n\n // Stored ids are strings for compatability with HTML data attributes.\n ids = ids.map(value => value.toString());\n\n stateManager.setReadOnly(false);\n const IdsToFilter = new Set(ids);\n bulk.selection = bulk.selection.filter(current => !IdsToFilter.has(current));\n if (bulk.selection.length === 0) {\n bulk.selectedType = '';\n }\n stateManager.setReadOnly(true);\n }\n\n /**\n * Get updated state data related to some cm ids.\n *\n * @method cmState\n * @param {StateManager} stateManager the current state\n * @param {array} cmids the list of cm ids to update\n */\n async cmState(stateManager, cmids) {\n this.cmLock(stateManager, cmids, true);\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('cm_state', course.id, cmids);\n stateManager.processUpdates(updates);\n this.cmLock(stateManager, cmids, false);\n }\n\n /**\n * Get updated state data related to some section ids.\n *\n * @method sectionState\n * @param {StateManager} stateManager the current state\n * @param {array} sectionIds the list of section ids to update\n */\n async sectionState(stateManager, sectionIds) {\n this.sectionLock(stateManager, sectionIds, true);\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_state', course.id, sectionIds);\n stateManager.processUpdates(updates);\n this.sectionLock(stateManager, sectionIds, false);\n }\n\n /**\n * Get the full updated state data of the course.\n *\n * @param {StateManager} stateManager the current state\n */\n async courseState(stateManager) {\n const course = stateManager.get('course');\n const updates = await this._callEditWebservice('course_state', course.id);\n stateManager.processUpdates(updates);\n }\n\n}\n"],"names":["action","courseId","ids","targetSectionId","targetCmId","args","courseid","targetsectionid","targetcmid","ajaxresult","ajax","call","methodname","JSON","parse","stateManager","sectionIds","course","get","sectionLock","updates","this","_callEditWebservice","id","bulkReset","processUpdates","cmIds","cmLock","init","addUpdateTypes","prepareFields","_prepareFields","updateName","fields","locked","_sectionBasicAction","_cmBasicAction","Set","add","forEach","cmId","cm","sectionid","Array","from","cmids","Error","cmDrag","dragValue","setPageItem","_setElementsValue","sectionDrag","cmCompletion","complete","newValue","lockValue","name","fieldName","setReadOnly","element","type","isStatic","newPageItem","undefined","pageItem","sectionId","unlockAll","state","section","collapsed","collapsedIds","_updateStateSectionPreference","preferenceName","preferenceValue","affectedSections","size","collapsedSectionIds","push","bulkEnable","enabled","bulk","selectedType","selection","cmSelect","_addIdsToSelection","cmUnselect","_removeIdsFromSelection","sectionSelect","sectionUnselect","typeName","map","value","toString","newSelection","IdsToFilter","filter","current","has","length"],"mappings":"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"} \ No newline at end of file diff --git a/course/format/amd/src/local/content.js b/course/format/amd/src/local/content.js index bce9f4d2a85..8fb21e9ccf8 100644 --- a/course/format/amd/src/local/content.js +++ b/course/format/amd/src/local/content.js @@ -227,6 +227,7 @@ export default class Component extends BaseComponent { {watch: `cm.stealth:updated`, handler: this._reloadCm}, {watch: `cm.sectionid:updated`, handler: this._reloadCm}, {watch: `cm.indent:updated`, handler: this._reloadCm}, + {watch: `cm.groupmode:updated`, handler: this._reloadCm}, // Update section number and title. {watch: `section.number:updated`, handler: this._refreshSectionNumber}, // Collapse and expand sections. diff --git a/course/format/amd/src/local/content/actions.js b/course/format/amd/src/local/content/actions.js index 583a04e38ab..6bf38661db7 100644 --- a/course/format/amd/src/local/content/actions.js +++ b/course/format/amd/src/local/content/actions.js @@ -53,6 +53,9 @@ const directMutations = { cmStealth: 'cmStealth', cmMoveRight: 'cmMoveRight', cmMoveLeft: 'cmMoveLeft', + cmNoGroups: 'cmNoGroups', + cmSeparateGroups: 'cmSeparateGroups', + cmVisibleGroups: 'cmVisibleGroups', }; export default class extends BaseComponent { diff --git a/course/format/amd/src/local/courseeditor/mutations.js b/course/format/amd/src/local/courseeditor/mutations.js index c2dc9df224d..712b14c283c 100644 --- a/course/format/amd/src/local/courseeditor/mutations.js +++ b/course/format/amd/src/local/courseeditor/mutations.js @@ -366,6 +366,33 @@ export default class { await this._cmBasicAction(stateManager, 'cm_moveleft', cmIds); } + /** + * Set cms group mode to NOGROUPS. + * @param {StateManager} stateManager the current state manager + * @param {array} cmIds the list of cm ids + */ + async cmNoGroups(stateManager, cmIds) { + await this._cmBasicAction(stateManager, 'cm_nogroups', cmIds); + } + + /** + * Set cms group mode to VISIBLEGROUPS. + * @param {StateManager} stateManager the current state manager + * @param {array} cmIds the list of cm ids + */ + async cmVisibleGroups(stateManager, cmIds) { + await this._cmBasicAction(stateManager, 'cm_visiblegroups', cmIds); + } + + /** + * Set cms group mode to SEPARATEGROUPS. + * @param {StateManager} stateManager the current state manager + * @param {array} cmIds the list of cm ids + */ + async cmSeparateGroups(stateManager, cmIds) { + await this._cmBasicAction(stateManager, 'cm_separategroups', cmIds); + } + /** * Lock or unlock course modules. * diff --git a/course/format/classes/output/local/content/section/cmitem.php b/course/format/classes/output/local/content/section/cmitem.php index eebfda8bd22..5475bad9ce7 100644 --- a/course/format/classes/output/local/content/section/cmitem.php +++ b/course/format/classes/output/local/content/section/cmitem.php @@ -107,6 +107,7 @@ class cmitem implements named_templatable, renderable { 'cmformat' => $item->export_for_template($output), 'hasinfo' => $hasinfo, 'indent' => ($format->uses_indentation()) ? $mod->indent : 0, + 'groupmode' => $mod->groupmode, ]; } } diff --git a/course/format/classes/output/local/state/cm.php b/course/format/classes/output/local/state/cm.php index 11452a7cdec..a3acbabc250 100644 --- a/course/format/classes/output/local/state/cm.php +++ b/course/format/classes/output/local/state/cm.php @@ -91,6 +91,7 @@ class cm implements renderable { 'hascmrestrictions' => $this->get_has_restrictions(), 'modname' => get_string('pluginname', 'mod_' . $cm->modname), 'indent' => ($format->uses_indentation()) ? $cm->indent : 0, + 'groupmode' => $cm->groupmode, 'module' => $cm->modname, 'plugin' => 'mod_' . $cm->modname, ]; diff --git a/course/format/classes/stateactions.php b/course/format/classes/stateactions.php index 2110cfd96af..372559bb9c4 100644 --- a/course/format/classes/stateactions.php +++ b/course/format/classes/stateactions.php @@ -16,7 +16,6 @@ namespace core_courseformat; -use core_courseformat\stateupdates; use core\event\course_module_updated; use cm_info; use section_info; @@ -705,6 +704,92 @@ class stateactions { } } + /** + * Set NOGROUPS const value to cms groupmode. + * + * @param stateupdates $updates the affected course elements track + * @param stdClass $course the course object + * @param int[] $ids cm ids + * @param int $targetsectionid not used + * @param int $targetcmid not used + */ + public function cm_nogroups( + stateupdates $updates, + stdClass $course, + array $ids = [], + ?int $targetsectionid = null, + ?int $targetcmid = null + ): void { + $this->set_cm_groupmode($updates, $course, $ids, NOGROUPS); + } + + /** + * Set VISIBLEGROUPS const value to cms groupmode. + * + * @param stateupdates $updates the affected course elements track + * @param stdClass $course the course object + * @param int[] $ids cm ids + * @param int $targetsectionid not used + * @param int $targetcmid not used + */ + public function cm_visiblegroups( + stateupdates $updates, + stdClass $course, + array $ids = [], + ?int $targetsectionid = null, + ?int $targetcmid = null + ): void { + $this->set_cm_groupmode($updates, $course, $ids, VISIBLEGROUPS); + } + + /** + * Set SEPARATEGROUPS const value to cms groupmode. + * + * @param stateupdates $updates the affected course elements track + * @param stdClass $course the course object + * @param int[] $ids cm ids + * @param int $targetsectionid not used + * @param int $targetcmid not used + */ + public function cm_separategroups( + stateupdates $updates, + stdClass $course, + array $ids = [], + ?int $targetsectionid = null, + ?int $targetcmid = null + ): void { + $this->set_cm_groupmode($updates, $course, $ids, SEPARATEGROUPS); + } + + /** + * Internal method to define the cm groupmode value. + * + * @param stateupdates $updates the affected course elements track + * @param stdClass $course the course object + * @param int[] $ids cm ids + * @param int $groupmode new value for groupmode: NOGROUPS, SEPARATEGROUPS, VISIBLEGROUPS + */ + protected function set_cm_groupmode( + stateupdates $updates, + stdClass $course, + array $ids, + int $groupmode + ): void { + global $DB; + + $this->validate_cms($course, $ids, __FUNCTION__, ['moodle/course:manageactivities']); + $modinfo = get_fast_modinfo($course); + $cms = $this->get_cm_info($modinfo, $ids); + list($insql, $inparams) = $DB->get_in_or_equal(array_keys($cms), SQL_PARAMS_NAMED); + $DB->set_field_select('course_modules', 'groupmode', $groupmode, "id $insql", $inparams); + rebuild_course_cache($course->id, false, true); + foreach ($cms as $cm) { + $modcontext = context_module::instance($cm->id); + course_module_updated::create_from_cm($cm, $modcontext)->trigger(); + $updates->add_cm_put($cm->id); + } + } + /** * Extract several cm_info from the course_modinfo. * diff --git a/course/format/tests/stateactions_test.php b/course/format/tests/stateactions_test.php index a8ccb300dd9..bf79fb09a41 100644 --- a/course/format/tests/stateactions_test.php +++ b/course/format/tests/stateactions_test.php @@ -1137,6 +1137,87 @@ class stateactions_test extends \advanced_testcase { ); } + /** + * Test for cm_nogroups + * + * @covers ::cm_nogroups + * @dataProvider basic_role_provider + * @param string $role the user role + * @param bool $expectedexception if it will expect an exception. + */ + public function test_cm_nogroups( + string $role = 'editingteacher', + bool $expectedexception = false + ): void { + $this->basic_state_text( + 'cm_nogroups', + $role, + ['cm0', 'cm1', 'cm2', 'cm3'], + $expectedexception, + ['put' => 4], + null, + null, + null, + null, + 'groupmode', + NOGROUPS + ); + } + + /** + * Test for cm_visiblegroups + * + * @covers ::cm_visiblegroups + * @dataProvider basic_role_provider + * @param string $role the user role + * @param bool $expectedexception if it will expect an exception. + */ + public function test_cm_visiblegroups( + string $role = 'editingteacher', + bool $expectedexception = false + ): void { + $this->basic_state_text( + 'cm_visiblegroups', + $role, + ['cm0', 'cm1', 'cm2', 'cm3'], + $expectedexception, + ['put' => 4], + null, + null, + null, + null, + 'groupmode', + VISIBLEGROUPS + ); + } + + /** + * Test for cm_separategroups + * + * @covers ::cm_separategroups + * @dataProvider basic_role_provider + * @param string $role the user role + * @param bool $expectedexception if it will expect an exception. + */ + public function test_cm_separategroups( + string $role = 'editingteacher', + bool $expectedexception = false + ): void { + $this->basic_state_text( + 'cm_separategroups', + $role, + ['cm0', 'cm1', 'cm2', 'cm3'], + $expectedexception, + ['put' => 4], + null, + null, + null, + null, + 'groupmode', + SEPARATEGROUPS + ); + } + /** * Test for section_move_after *