diff --git a/course/amd/build/actions.min.js b/course/amd/build/actions.min.js index d64d4fe62d3..1fca150f6b8 100644 --- a/course/amd/build/actions.min.js +++ b/course/amd/build/actions.min.js @@ -1,2 +1,2 @@ -define ("core_course/actions",["jquery","core/ajax","core/templates","core/notification","core/str","core/url","core/yui","core/modal_factory","core/modal_events","core/key_codes","core/log","core_courseformat/courseeditor"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m=l.getCurrentCourseEditor(),n={EDITINPROGRESS:"editinprogress",SECTIONDRAGGABLE:"sectiondraggable",EDITINGMOVE:"editing_move"},o={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",ADDSECTIONS:"#changenumsections [data-add-sections]"};g.use("moodle-course-coursebase",function(){var a=M.course.format.get_section_selector();if(a){o.SECTIONLI=a}});var p=function(a){var b;g.use("moodle-course-util",function(c){b=c.Moodle.core_course.util.cm.getId(c.Node(a.get(0)))});return b},q=function(a){var b;g.use("moodle-course-util",function(c){b=c.Moodle.core_course.util.cm.getName(c.Node(a.get(0)))});return b},r=function(a){a.addClass(n.EDITINPROGRESS);var b=a.find(o.ACTIONAREA).get(0);if(b){var c=M.util.add_spinner(g,g.Node(b));c.show();return c}return null},s=function(a){a.addClass(n.EDITINPROGRESS);var b=a.find(o.SECTIONACTIONMENU).get(0);if(b){var c=M.util.add_spinner(g,g.Node(b));c.show();return c}return null},t=function(a){var b=M.util.add_lightbox(g,g.Node(a.get(0)));b.show();return b},u=function(a,b,c){window.setTimeout(function(){a.removeClass(n.EDITINPROGRESS);if(b){b.hide()}},c)},v=function(a,b){if(a){window.setTimeout(function(){a.hide()},b)}},w=function(a){g.use("moodle-course-coursebase",function(){M.course.coursebase.invoke_function("setup_for_resource","#"+a)});if(M.core.actionmenu&&M.core.actionmenu.newDOMNode){M.core.actionmenu.newDOMNode(g.one("#"+a))}},x=function(b,c){var d=a("#"+b),e="[data-action="+c+"]";if("groupsseparate"===c||"groupsvisible"===c||"groupsnone"===c){e="[data-action=groupsseparate],[data-action=groupsvisible],[data-action=groupsnone]"}if(d.find(e).is(":visible")){d.find(e).focus()}else{d.find(o.MENU).find(o.TOGGLE).focus()}},y=function(b){var c=a("a:visible"),d=!1,e=null;c.each(function(){if(a.contains(b[0],this)){d=!0}else if(d){e=this;return!1}});return e},z=function(c,e,f){var g=f.attr("data-action"),h=r(c),i=b.call([{methodname:"core_course_edit_module",args:{id:e,action:g,sectionreturn:f.attr("data-sectionreturn")?f.attr("data-sectionreturn"):0}}],!0),j;if("duplicate"===g){j=t(f.closest(o.SECTIONLI))}a.when.apply(a,i).done(function(b){var d=y(c);c.replaceWith(b);var f=[];a("
"+b+"
").find(o.ACTIVITYLI).each(function(b){w(a(this).attr("id"));if(0===b){x(a(this).attr("id"),g);d=null}f.push(p(a(this)))});if(d){d.focus()}u(c,h,400);v(j,400);c.trigger(a.Event("coursemoduleedited",{ajaxreturn:b,action:g}));m.dispatch("legacyActivityAction",g,e,f)}).fail(function(b){u(c,h);v(j);var f=a.Event("coursemoduleeditfailed",{exception:b,action:g});c.trigger(f);if(!f.isDefaultPrevented()){d.exception(b)}})},A=function(c,d,e){var f=r(c),g=b.call([{methodname:"core_course_get_module",args:{id:d,sectionreturn:e}}],!0);a.when.apply(a,g).done(function(a){u(c,f,400);F(a)}).fail(function(){u(c,f)})},B=function(a,b){var c=a.attr("class").match(/modtype_([^\s]*)/)[1],f=q(a);e.get_string("pluginname",c).done(function(a){e.get_strings([{key:"confirm"},{key:null===f?"deletechecktype":"deletechecktypename",param:{type:a,name:f}},{key:"yes"},{key:"no"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],b)})})},C=function(a,b){e.get_strings([{key:"confirm"},{key:"yes"},{key:"no"}]).done(function(c){d.confirm(c[0],a,c[1],c[2],b)})},D=function(a,b,f,g,h){return e.get_strings([{key:f,component:g}]).then(function(d){a.find("span.menu-action-text").html(d[0]);return c.renderPix(b,"core")}).then(function(b){a.find(".icon").replaceWith(b);a.attr("data-action",h)}).catch(d.exception)},E=function(b,c,d,e,f){var g=c.attr("data-action");if("hide"===g||"show"===g){if("hide"===g){b.addClass("hidden");D(c,"i/show","showfromothers","format_"+e,"show")}else{b.removeClass("hidden");D(c,"i/hide","hidefromothers","format_"+e,"hide")}if(d.modules!==void 0){for(var h in d.modules){F(d.modules[h])}}if(d.section_availability!==void 0){b.find(".section_availability").first().replaceWith(d.section_availability)}var k=m.state.section.get(f);if(k!==void 0){m.dispatch("sectionState",[f])}}else if("setmarker"===g){var i=a(o.SECTIONLI+".current"),j=i.find(o.SECTIONACTIONMENU+" a[data-action=removemarker]");i.removeClass("current");D(j,"i/marker","highlight","core","setmarker");b.addClass("current");D(c,"i/marked","highlightoff","core","removemarker");m.dispatch("legacySectionAction",g,f)}else if("removemarker"===g){b.removeClass("current");D(c,"i/marker","highlight","core","setmarker");m.dispatch("legacySectionAction",g,f)}},F=function(b){a("
"+b+"
").find(o.ACTIVITYLI).each(function(){var c=a(this).attr("id");a(o.ACTIVITYLI+"#"+c).replaceWith(b);w(c)})},G=function(c,e,f,g){var h=f.attr("data-action"),i=f.attr("data-sectionreturn")?f.attr("data-sectionreturn"):0,j=s(c),k=b.call([{methodname:"core_course_edit_section",args:{id:e,action:h,sectionreturn:i}}],!0),l=t(c);a.when.apply(a,k).done(function(b){var d=a.parseJSON(b);u(c,j);v(l);c.find(o.SECTIONACTIONMENU).find(o.TOGGLE).focus();var i=a.Event("coursesectionedited",{ajaxreturn:d,action:h});c.trigger(i);if(!i.isDefaultPrevented()){E(c,f,d,g,e)}}).fail(function(b){u(c,j);v(l);var f=a.Event("coursesectioneditfailed",{exception:b,action:h});c.trigger(f);if(!f.isDefaultPrevented()){d.exception(b)}})};g.use("moodle-course-coursebase",function(){M.course.coursebase.register_module({set_visibility_resource_ui:function set_visibility_resource_ui(b){var c=a(b.element.getDOMNode()),d=p(c);if(d){var e=c.find("."+n.EDITINGMOVE).attr("data-sectionreturn");A(c,d,e)}},updateMovedCmState:function updateMovedCmState(a){var b=m.state,c=b.cm.get(a.cmid);if(c!==void 0){m.dispatch("sectionState",[c.sectionid])}m.dispatch("cmState",[a.cmid])},updateMovedSectionState:function updateMovedSectionState(){m.dispatch("courseState")}})});m.addMutations({legacyActivityAction:function legacyActivityAction(a,b,c,d){var e=a.state,f=e.cm.get(c);if(f===void 0){return}var g=e.section.get(f.sectionid);if(g===void 0){return}a.setReadOnly(!1);switch(b){case"delete":g.cmlist=g.cmlist.reduce(function(a,b){if(b!=c){a.push(b)}return a},[]);e.cm.delete(c);break;case"hide":case"show":f.visible="show"===b?!0:!1;break;case"duplicate":m.dispatch("cmState",d);break;}a.setReadOnly(!0)},legacySectionAction:function legacySectionAction(a,b,c){var d=a.state,e=d.section.get(c);if(e===void 0){return}a.setReadOnly(!1);switch(b){case"setmarker":d.section.forEach(function(a){if(a.id!=c){a.current=!1}});e.current=!0;break;case"removemarker":e.current=!1;break;}a.setReadOnly(!0)}});return{initCoursePage:function initCoursePage(b){a("body").on("click keypress",o.ACTIVITYLI+" "+o.ACTIVITYACTION+"[data-action]",function(b){if("keypress"===b.type&&13!==b.keyCode){return}var c=a(this),d=c.closest(o.ACTIVITYLI),e=c.attr("data-action"),f=p(d);switch(e){case"moveleft":case"moveright":case"delete":case"duplicate":case"hide":case"stealth":case"show":case"groupsseparate":case"groupsvisible":case"groupsnone":break;default:return;}if(!f){return}b.preventDefault();if("delete"===e){B(d,function(){z(d,f,c)})}else{z(d,f,c)}});a("body").on("click keypress",o.SECTIONLI+" "+o.SECTIONACTIONMENU+"[data-sectionid] a[data-action]",function(c){if("keypress"===c.type&&13!==c.keyCode){return}var d=a(this),e=d.closest(o.SECTIONLI),f=d.closest(o.SECTIONACTIONMENU).attr("data-sectionid");c.preventDefault();if(d.attr("data-confirm")){C(d.attr("data-confirm"),function(){G(e,f,d,b)})}else{G(e,f,d,b)}});a("body").on("updated","".concat(o.SECTIONLI," [data-inplaceeditable]"),function(a){if(a.ajaxreturn&&a.ajaxreturn.itemid){var b=m.state,c=b.section.get(a.ajaxreturn.itemid);if(c!==void 0){m.dispatch("sectionState",[a.ajaxreturn.itemid])}}});a("body").on("updated","".concat(o.ACTIVITYLI," [data-inplaceeditable]"),function(a){if(a.ajaxreturn&&a.ajaxreturn.itemid){m.dispatch("cmState",[a.ajaxreturn.itemid])}});e.get_string("numberweeks").done(function(b){var c=a(o.ADDSECTIONS),d=c.attr("data-add-sections"),e=c.attr("data-new-sections"),f=a("
");f.find("label").html(b);h.create({title:d,type:h.types.SAVE_CANCEL,body:f.html()},c).done(function(b){var e=a(b.getBody()).find("#add_section_numsections"),f=function(){if(""+parseInt(e.val())===e.val()&&1<=parseInt(e.val())){document.location=c.attr("href")+"&numsections="+parseInt(e.val())}};b.setSaveButtonText(d);b.getRoot().on(i.shown,function(){e.focus().select().on("keydown",function(a){if(a.keyCode===j.enter){f()}})});b.getRoot().on(i.save,function(a){a.preventDefault();f()})})})},replaceSectionActionItem:function replaceSectionActionItem(a,b,c,d,e,f){k.debug("replaceSectionActionItem() is deprecated and will be removed.");var g=a.find(o.SECTIONACTIONMENU+" "+b);D(g,c,d,e,f)}}}); +define ("core_course/actions",["jquery","core/ajax","core/templates","core/notification","core/str","core/url","core/yui","core/modal_factory","core/modal_events","core/key_codes","core/log","core_courseformat/courseeditor"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m=l.getCurrentCourseEditor(),n={EDITINPROGRESS:"editinprogress",SECTIONDRAGGABLE:"sectiondraggable",EDITINGMOVE:"editing_move"},o={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",ADDSECTIONS:"#changenumsections [data-add-sections]"};g.use("moodle-course-coursebase",function(){var a=M.course.format.get_section_selector();if(a){o.SECTIONLI=a}});var p=function(a){var b;g.use("moodle-course-util",function(c){b=c.Moodle.core_course.util.cm.getId(c.Node(a.get(0)))});return b},q=function(a){var b;g.use("moodle-course-util",function(c){b=c.Moodle.core_course.util.cm.getName(c.Node(a.get(0)))});return b},r=function(a){a.addClass(n.EDITINPROGRESS);var b=a.find(o.ACTIONAREA).get(0);if(b){var c=M.util.add_spinner(g,g.Node(b));c.show();return c}return null},s=function(a){a.addClass(n.EDITINPROGRESS);var b=a.find(o.SECTIONACTIONMENU).get(0);if(b){var c=M.util.add_spinner(g,g.Node(b));c.show();return c}return null},t=function(a){var b=M.util.add_lightbox(g,g.Node(a.get(0)));b.show();return b},u=function(a,b,c){window.setTimeout(function(){a.removeClass(n.EDITINPROGRESS);if(b){b.hide()}},c)},v=function(a,b){if(a){window.setTimeout(function(){a.hide()},b)}},w=function(a){g.use("moodle-course-coursebase",function(){M.course.coursebase.invoke_function("setup_for_resource","#"+a)});if(M.core.actionmenu&&M.core.actionmenu.newDOMNode){M.core.actionmenu.newDOMNode(g.one("#"+a))}},x=function(b,c){var d=a("#"+b),e="[data-action="+c+"]";if("groupsseparate"===c||"groupsvisible"===c||"groupsnone"===c){e="[data-action=groupsseparate],[data-action=groupsvisible],[data-action=groupsnone]"}if(d.find(e).is(":visible")){d.find(e).focus()}else{d.find(o.MENU).find(o.TOGGLE).focus()}},y=function(b){var c=a("a:visible"),d=!1,e=null;c.each(function(){if(a.contains(b[0],this)){d=!0}else if(d){e=this;return!1}});return e},z=function(c,e,f){var g=f.attr("data-action"),h=r(c),i=b.call([{methodname:"core_course_edit_module",args:{id:e,action:g,sectionreturn:f.attr("data-sectionreturn")?f.attr("data-sectionreturn"):0}}],!0),j;if("duplicate"===g){j=t(f.closest(o.SECTIONLI))}a.when.apply(a,i).done(function(b){var d=y(c);c.replaceWith(b);var f=[];a("
"+b+"
").find(o.ACTIVITYLI).each(function(b){w(a(this).attr("id"));if(0===b){x(a(this).attr("id"),g);d=null}f.push(p(a(this)))});if(d){d.focus()}u(c,h,400);v(j,400);c.trigger(a.Event("coursemoduleedited",{ajaxreturn:b,action:g}));m.dispatch("legacyActivityAction",g,e,f)}).fail(function(b){u(c,h);v(j);var f=a.Event("coursemoduleeditfailed",{exception:b,action:g});c.trigger(f);if(!f.isDefaultPrevented()){d.exception(b)}})},A=function(c,d,e){var f=a(c),g=r(f),h=b.call([{methodname:"core_course_get_module",args:{id:d,sectionreturn:e}}],!0);a.when.apply(a,h).done(function(a){u(f,g,400);F(a)}).fail(function(){u(f,g)})},B=function(a,b){var c=a.attr("class").match(/modtype_([^\s]*)/)[1],f=q(a);e.get_string("pluginname",c).done(function(a){e.get_strings([{key:"confirm"},{key:null===f?"deletechecktype":"deletechecktypename",param:{type:a,name:f}},{key:"yes"},{key:"no"}]).done(function(a){d.confirm(a[0],a[1],a[2],a[3],b)})})},C=function(a,b){e.get_strings([{key:"confirm"},{key:"yes"},{key:"no"}]).done(function(c){d.confirm(c[0],a,c[1],c[2],b)})},D=function(a,b,f,g,h){return e.get_strings([{key:f,component:g}]).then(function(d){a.find("span.menu-action-text").html(d[0]);return c.renderPix(b,"core")}).then(function(b){a.find(".icon").replaceWith(b);a.attr("data-action",h)}).catch(d.exception)},E=function(b,c,d,e,f){var g=c.attr("data-action");if("hide"===g||"show"===g){if("hide"===g){b.addClass("hidden");D(c,"i/show","showfromothers","format_"+e,"show")}else{b.removeClass("hidden");D(c,"i/hide","hidefromothers","format_"+e,"hide")}if(d.modules!==void 0){for(var h in d.modules){F(d.modules[h])}}if(d.section_availability!==void 0){b.find(".section_availability").first().replaceWith(d.section_availability)}var k=m.state.section.get(f);if(k!==void 0){m.dispatch("sectionState",[f])}}else if("setmarker"===g){var i=a(o.SECTIONLI+".current"),j=i.find(o.SECTIONACTIONMENU+" a[data-action=removemarker]");i.removeClass("current");D(j,"i/marker","highlight","core","setmarker");b.addClass("current");D(c,"i/marked","highlightoff","core","removemarker");m.dispatch("legacySectionAction",g,f)}else if("removemarker"===g){b.removeClass("current");D(c,"i/marker","highlight","core","setmarker");m.dispatch("legacySectionAction",g,f)}},F=function(b){a("
"+b+"
").find(o.ACTIVITYLI).each(function(){var c=a(this).attr("id");a(o.ACTIVITYLI+"#"+c).replaceWith(b);w(c)})},G=function(c,e,f,g){var h=f.attr("data-action"),i=f.attr("data-sectionreturn")?f.attr("data-sectionreturn"):0,j=s(c),k=b.call([{methodname:"core_course_edit_section",args:{id:e,action:h,sectionreturn:i}}],!0),l=t(c);a.when.apply(a,k).done(function(b){var d=a.parseJSON(b);u(c,j);v(l);c.find(o.SECTIONACTIONMENU).find(o.TOGGLE).focus();var i=a.Event("coursesectionedited",{ajaxreturn:d,action:h});c.trigger(i);if(!i.isDefaultPrevented()){E(c,f,d,g,e)}}).fail(function(b){u(c,j);v(l);var f=a.Event("coursesectioneditfailed",{exception:b,action:h});c.trigger(f);if(!f.isDefaultPrevented()){d.exception(b)}})};g.use("moodle-course-coursebase",function(){M.course.coursebase.register_module({set_visibility_resource_ui:function set_visibility_resource_ui(b){var c=a(b.element.getDOMNode()),d=p(c);if(d){var e=c.find("."+n.EDITINGMOVE).attr("data-sectionreturn");A(c,d,e)}},updateMovedCmState:function updateMovedCmState(a){var b=m.state,c=b.cm.get(a.cmid);if(c!==void 0){m.dispatch("sectionState",[c.sectionid])}m.dispatch("cmState",[a.cmid])},updateMovedSectionState:function updateMovedSectionState(){m.dispatch("courseState")}})});m.addMutations({legacyActivityAction:function legacyActivityAction(a,b,c,d){var e=a.state,f=e.cm.get(c);if(f===void 0){return}var g=e.section.get(f.sectionid);if(g===void 0){return}a.setReadOnly(!1);switch(b){case"delete":g.cmlist=g.cmlist.reduce(function(a,b){if(b!=c){a.push(b)}return a},[]);e.cm.delete(c);break;case"hide":case"show":f.visible="show"===b?!0:!1;break;case"duplicate":m.dispatch("cmState",d);break;}a.setReadOnly(!0)},legacySectionAction:function legacySectionAction(a,b,c){var d=a.state,e=d.section.get(c);if(e===void 0){return}a.setReadOnly(!1);switch(b){case"setmarker":d.section.forEach(function(a){if(a.id!=c){a.current=!1}});e.current=!0;break;case"removemarker":e.current=!1;break;}a.setReadOnly(!0)}});return{initCoursePage:function initCoursePage(b){a("body").on("click keypress",o.ACTIVITYLI+" "+o.ACTIVITYACTION+"[data-action]",function(b){if("keypress"===b.type&&13!==b.keyCode){return}var c=a(this),d=c.closest(o.ACTIVITYLI),e=c.attr("data-action"),f=p(d);switch(e){case"moveleft":case"moveright":case"delete":case"duplicate":case"hide":case"stealth":case"show":case"groupsseparate":case"groupsvisible":case"groupsnone":break;default:return;}if(!f){return}b.preventDefault();if("delete"===e){B(d,function(){z(d,f,c)})}else{z(d,f,c)}});a("body").on("click keypress",o.SECTIONLI+" "+o.SECTIONACTIONMENU+"[data-sectionid] a[data-action]",function(c){if("keypress"===c.type&&13!==c.keyCode){return}var d=a(this),e=d.closest(o.SECTIONLI),f=d.closest(o.SECTIONACTIONMENU).attr("data-sectionid");c.preventDefault();if(d.attr("data-confirm")){C(d.attr("data-confirm"),function(){G(e,f,d,b)})}else{G(e,f,d,b)}});a("body").on("updated","".concat(o.SECTIONLI," [data-inplaceeditable]"),function(a){if(a.ajaxreturn&&a.ajaxreturn.itemid){var b=m.state,c=b.section.get(a.ajaxreturn.itemid);if(c!==void 0){m.dispatch("sectionState",[a.ajaxreturn.itemid])}}});a("body").on("updated","".concat(o.ACTIVITYLI," [data-inplaceeditable]"),function(a){if(a.ajaxreturn&&a.ajaxreturn.itemid){m.dispatch("cmState",[a.ajaxreturn.itemid])}});e.get_string("numberweeks").done(function(b){var c=a(o.ADDSECTIONS),d=c.attr("data-add-sections"),e=c.attr("data-new-sections"),f=a("
");f.find("label").html(b);h.create({title:d,type:h.types.SAVE_CANCEL,body:f.html()},c).done(function(b){var e=a(b.getBody()).find("#add_section_numsections"),f=function(){if(""+parseInt(e.val())===e.val()&&1<=parseInt(e.val())){document.location=c.attr("href")+"&numsections="+parseInt(e.val())}};b.setSaveButtonText(d);b.getRoot().on(i.shown,function(){e.focus().select().on("keydown",function(a){if(a.keyCode===j.enter){f()}})});b.getRoot().on(i.save,function(a){a.preventDefault();f()})})})},replaceSectionActionItem:function replaceSectionActionItem(a,b,c,d,e,f){k.debug("replaceSectionActionItem() is deprecated and will be removed.");var g=a.find(o.SECTIONACTIONMENU+" "+b);D(g,c,d,e,f)},refreshModule:A}}); //# sourceMappingURL=actions.min.js.map diff --git a/course/amd/build/actions.min.js.map b/course/amd/build/actions.min.js.map index 4227116b6fc..d435aed2922 100644 --- a/course/amd/build/actions.min.js.map +++ b/course/amd/build/actions.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/actions.js"],"names":["define","$","ajax","templates","notification","str","url","Y","ModalFactory","ModalEvents","KeyCodes","log","editor","courseeditor","getCurrentCourseEditor","CSS","EDITINPROGRESS","SECTIONDRAGGABLE","EDITINGMOVE","SELECTOR","ACTIVITYLI","ACTIONAREA","ACTIVITYACTION","MENU","TOGGLE","SECTIONLI","SECTIONACTIONMENU","ADDSECTIONS","use","courseformatselector","M","course","format","get_section_selector","getModuleId","element","id","Moodle","core_course","util","cm","getId","Node","get","getModuleName","name","getName","addActivitySpinner","activity","addClass","actionarea","find","spinner","add_spinner","show","addSectionSpinner","sectionelement","addSectionLightbox","lightbox","add_lightbox","removeSpinner","delay","window","setTimeout","removeClass","hide","removeLightbox","initActionMenu","elementid","coursebase","invoke_function","core","actionmenu","newDOMNode","one","focusActionItem","elementId","action","mainelement","selector","is","focus","findNextFocusable","mainElement","tabables","isInside","foundElement","each","contains","editModule","moduleElement","cmid","target","attr","promises","call","methodname","args","sectionreturn","closest","when","apply","done","data","elementToFocus","replaceWith","affectedids","index","push","trigger","Event","ajaxreturn","dispatch","fail","ex","e","exception","isDefaultPrevented","refreshModule","activityElement","replaceActivityHtmlWith","confirmDeleteModule","onconfirm","modtypename","match","modulename","get_string","pluginname","get_strings","key","param","type","s","confirm","confirmEditSection","message","replaceActionItem","actionitem","image","stringname","stringcomponent","newaction","component","then","strings","html","renderPix","pixhtml","catch","defaultEditSectionHandler","sectionElement","actionItem","courseformat","sectionid","modules","i","section_availability","first","section","state","oldmarker","oldActionItem","activityHTML","editSection","dataencoded","parseJSON","register_module","set_visibility_resource_ui","getDOMNode","updateMovedCmState","params","updateMovedSectionState","addMutations","legacyActivityAction","statemanager","setReadOnly","cmlist","reduce","current","delete","visible","legacySectionAction","forEach","initCoursePage","on","keyCode","moduleId","preventDefault","sectionId","itemid","strNumberSections","modalTitle","newSections","modalBody","create","title","types","SAVE_CANCEL","body","modal","numSections","getBody","addSections","parseInt","val","document","location","setSaveButtonText","getRoot","shown","select","enter","save","replaceSectionActionItem","debug"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * 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(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str', 'core/url', 'core/yui',\n 'core/modal_factory', 'core/modal_events', 'core/key_codes', 'core/log', 'core_courseformat/courseeditor'],\n function($, ajax, templates, notification, str, url, Y, ModalFactory, ModalEvents, KeyCodes, log, editor) {\n\n const courseeditor = editor.getCurrentCourseEditor();\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 ADDSECTIONS: '#changenumsections [data-add-sections]'\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 * Wrapper for Y.Moodle.core_course.util.cm.getId\n *\n * @param {JQuery} element\n * @returns {Integer}\n */\n var getModuleId = function(element) {\n var id;\n Y.use('moodle-course-util', function(Y) {\n id = Y.Moodle.core_course.util.cm.getId(Y.Node(element.get(0)));\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 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 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 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 var lightbox = M.util.add_lightbox(Y, Y.Node(sectionelement.get(0)));\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 }, 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 }, 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 });\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} activityElement\n * @param {Number} cmid\n * @param {Number} sectionreturn\n */\n var refreshModule = function(activityElement, cmid, sectionreturn) {\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 $.when.apply($, promises)\n .done(function(data) {\n removeSpinner(activityElement, spinner, 400);\n replaceActivityHtmlWith(data);\n }).fail(function() {\n removeSpinner(activityElement, spinner);\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'},\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 replaceActionItem(actionItem, 'i/show',\n 'showfromothers', 'format_' + courseformat, 'show');\n } else {\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 } else if (action === 'removemarker') {\n sectionElement.removeClass('current');\n replaceActionItem(actionItem, 'i/marker',\n 'highlight', 'core', 'setmarker');\n courseeditor.dispatch('legacySectionAction', action, sectionid);\n }\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 // 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 });\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 */\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 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 };\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 statemanager.setReadOnly(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 cm.visible = (action === 'show') ? true : false;\n break;\n\n case 'duplicate':\n // Duplicate requires to get extra data from the server.\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 statemanager.setReadOnly(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 // 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 e.preventDefault();\n if (actionItem.attr('data-confirm')) {\n // Action requires confirmation.\n confirmEditSection(actionItem.attr('data-confirm'), function() {\n editSection(sectionElement, sectionId, actionItem, courseformat);\n });\n } else {\n editSection(sectionElement, sectionId, actionItem, courseformat);\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} [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 // 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 };\n });\n"],"file":"actions.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/actions.js"],"names":["define","$","ajax","templates","notification","str","url","Y","ModalFactory","ModalEvents","KeyCodes","log","editor","courseeditor","getCurrentCourseEditor","CSS","EDITINPROGRESS","SECTIONDRAGGABLE","EDITINGMOVE","SELECTOR","ACTIVITYLI","ACTIONAREA","ACTIVITYACTION","MENU","TOGGLE","SECTIONLI","SECTIONACTIONMENU","ADDSECTIONS","use","courseformatselector","M","course","format","get_section_selector","getModuleId","element","id","Moodle","core_course","util","cm","getId","Node","get","getModuleName","name","getName","addActivitySpinner","activity","addClass","actionarea","find","spinner","add_spinner","show","addSectionSpinner","sectionelement","addSectionLightbox","lightbox","add_lightbox","removeSpinner","delay","window","setTimeout","removeClass","hide","removeLightbox","initActionMenu","elementid","coursebase","invoke_function","core","actionmenu","newDOMNode","one","focusActionItem","elementId","action","mainelement","selector","is","focus","findNextFocusable","mainElement","tabables","isInside","foundElement","each","contains","editModule","moduleElement","cmid","target","attr","promises","call","methodname","args","sectionreturn","closest","when","apply","done","data","elementToFocus","replaceWith","affectedids","index","push","trigger","Event","ajaxreturn","dispatch","fail","ex","e","exception","isDefaultPrevented","refreshModule","activityElement","replaceActivityHtmlWith","confirmDeleteModule","onconfirm","modtypename","match","modulename","get_string","pluginname","get_strings","key","param","type","s","confirm","confirmEditSection","message","replaceActionItem","actionitem","image","stringname","stringcomponent","newaction","component","then","strings","html","renderPix","pixhtml","catch","defaultEditSectionHandler","sectionElement","actionItem","courseformat","sectionid","modules","i","section_availability","first","section","state","oldmarker","oldActionItem","activityHTML","editSection","dataencoded","parseJSON","register_module","set_visibility_resource_ui","getDOMNode","updateMovedCmState","params","updateMovedSectionState","addMutations","legacyActivityAction","statemanager","setReadOnly","cmlist","reduce","current","delete","visible","legacySectionAction","forEach","initCoursePage","on","keyCode","moduleId","preventDefault","sectionId","itemid","strNumberSections","modalTitle","newSections","modalBody","create","title","types","SAVE_CANCEL","body","modal","numSections","getBody","addSections","parseInt","val","document","location","setSaveButtonText","getRoot","shown","select","enter","save","replaceSectionActionItem","debug"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * 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(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str', 'core/url', 'core/yui',\n 'core/modal_factory', 'core/modal_events', 'core/key_codes', 'core/log', 'core_courseformat/courseeditor'],\n function($, ajax, templates, notification, str, url, Y, ModalFactory, ModalEvents, KeyCodes, log, editor) {\n\n const courseeditor = editor.getCurrentCourseEditor();\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 ADDSECTIONS: '#changenumsections [data-add-sections]'\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 * Wrapper for Y.Moodle.core_course.util.cm.getId\n *\n * @param {JQuery} element\n * @returns {Integer}\n */\n var getModuleId = function(element) {\n var id;\n Y.use('moodle-course-util', function(Y) {\n id = Y.Moodle.core_course.util.cm.getId(Y.Node(element.get(0)));\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 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 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 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 var lightbox = M.util.add_lightbox(Y, Y.Node(sectionelement.get(0)));\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 }, 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 }, 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 });\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 */\n var refreshModule = function(element, cmid, sectionreturn) {\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 $.when.apply($, promises)\n .done(function(data) {\n removeSpinner(activityElement, spinner, 400);\n replaceActivityHtmlWith(data);\n }).fail(function() {\n removeSpinner(activityElement, spinner);\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'},\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 replaceActionItem(actionItem, 'i/show',\n 'showfromothers', 'format_' + courseformat, 'show');\n } else {\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 } else if (action === 'removemarker') {\n sectionElement.removeClass('current');\n replaceActionItem(actionItem, 'i/marker',\n 'highlight', 'core', 'setmarker');\n courseeditor.dispatch('legacySectionAction', action, sectionid);\n }\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 // 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 });\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 */\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 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 };\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 statemanager.setReadOnly(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 cm.visible = (action === 'show') ? true : false;\n break;\n\n case 'duplicate':\n // Duplicate requires to get extra data from the server.\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 statemanager.setReadOnly(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 // 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 e.preventDefault();\n if (actionItem.attr('data-confirm')) {\n // Action requires confirmation.\n confirmEditSection(actionItem.attr('data-confirm'), function() {\n editSection(sectionElement, sectionId, actionItem, courseformat);\n });\n } else {\n editSection(sectionElement, sectionId, actionItem, courseformat);\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} [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 // 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 };\n });\n"],"file":"actions.min.js"} \ No newline at end of file diff --git a/course/amd/src/actions.js b/course/amd/src/actions.js index 54420c47f52..f7c62fa359d 100644 --- a/course/amd/src/actions.js +++ b/course/amd/src/actions.js @@ -279,11 +279,12 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str' * * Used after d&d of the module to another section * - * @param {JQuery} activityElement + * @param {JQuery|Element} element * @param {Number} cmid * @param {Number} sectionreturn */ - var refreshModule = function(activityElement, cmid, sectionreturn) { + var refreshModule = function(element, cmid, sectionreturn) { + const activityElement = $(element); var spinner = addActivitySpinner(activityElement); var promises = ajax.call([{ methodname: 'core_course_get_module', @@ -766,6 +767,8 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str' log.debug('replaceSectionActionItem() is deprecated and will be removed.'); var actionitem = sectionelement.find(SELECTOR.SECTIONACTIONMENU + ' ' + selector); replaceActionItem(actionitem, image, stringname, stringcomponent, newaction); - } + }, + // Method to refresh a module. + refreshModule, }; }); diff --git a/course/format/amd/build/courseeditor.min.js.map b/course/format/amd/build/courseeditor.min.js.map index fd66829a1d4..8ad88346fcd 100644 --- a/course/format/amd/build/courseeditor.min.js.map +++ b/course/format/amd/build/courseeditor.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/courseeditor.js"],"names":["courseEditorMap","Map","dispatchStateChangedEvent","detail","target","document","dispatchEvent","CustomEvent","events","stateChanged","bubbles","setViewFormat","courseId","setup","editor","getCourseEditor","parseInt","has","set","CourseEditor","name","eventName","eventDispatch","mutations","DefaultMutations","get","loadCourse","getCurrentCourseEditor","M","cfg"],"mappings":"iUAuBA,OACA,OACA,O,mDAGA,GAAMA,CAAAA,CAAe,CAAG,GAAIC,CAAAA,GAA5B,CAYA,QAASC,CAAAA,CAAT,CAAmCC,CAAnC,CAA2CC,CAA3C,CAAmD,CAC/C,GAAIA,CAAM,SAAV,CAA0B,CACtBA,CAAM,CAAGC,QACZ,CACDD,CAAM,CAACE,aAAP,CAAqB,GAAIC,CAAAA,WAAJ,CAAgBC,UAAOC,YAAvB,CAAqC,CACtDC,OAAO,GAD+C,CAEtDP,MAAM,CAAEA,CAF8C,CAArC,CAArB,CAIH,C,gBAS4B,QAAhBQ,CAAAA,aAAgB,CAACC,CAAD,CAAWC,CAAX,CAAqB,CAC9C,GAAMC,CAAAA,CAAM,CAAGC,CAAe,CAACH,CAAD,CAA9B,CACAE,CAAM,CAACH,aAAP,CAAqBE,CAArB,CACH,C,CAQM,GAAME,CAAAA,CAAe,CAAG,SAACH,CAAD,CAAc,CACzCA,CAAQ,CAAGI,QAAQ,CAACJ,CAAD,CAAnB,CAEA,GAAI,CAACZ,CAAe,CAACiB,GAAhB,CAAoBL,CAApB,CAAL,CAAoC,CAChCZ,CAAe,CAACkB,GAAhB,CACIN,CADJ,CAEI,GAAIO,UAAJ,CAAiB,CACbC,IAAI,uBAAiBR,CAAjB,CADS,CAEbS,SAAS,CAAEb,UAAOC,YAFL,CAGba,aAAa,CAAEpB,CAHF,CAMbqB,SAAS,CAAE,GAAIC,UANF,CAAjB,CAFJ,EAWAxB,CAAe,CAACyB,GAAhB,CAAoBb,CAApB,EAA8Bc,UAA9B,CAAyCd,CAAzC,CACH,CACD,MAAOZ,CAAAA,CAAe,CAACyB,GAAhB,CAAoBb,CAApB,CACV,CAlBM,C,6CAyB+B,QAAzBe,CAAAA,sBAAyB,SAAMZ,CAAAA,CAAe,CAACa,CAAC,CAACC,GAAF,CAAMjB,QAAP,CAArB,C","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 * Generic reactive module used in the course editor.\n *\n * @module core_courseformat/courseeditor\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DefaultMutations from 'core_courseformat/local/courseeditor/mutations';\nimport CourseEditor from 'core_courseformat/local/courseeditor/courseeditor';\nimport events from 'core_course/events';\n\n// A map with all the course editor instances.\nconst courseEditorMap = new Map();\n\n/**\n * Trigger a state changed event.\n *\n * This function will be moved to core_course/events module\n * when the file is migrated to the new JS events structure proposed in MDL-70990.\n *\n * @method dispatchStateChangedEvent\n * @param {object} detail the full state\n * @param {object} target the custom event target (document if none provided)\n */\nfunction dispatchStateChangedEvent(detail, target) {\n if (target === undefined) {\n target = document;\n }\n target.dispatchEvent(new CustomEvent(events.stateChanged, {\n bubbles: true,\n detail: detail,\n }));\n}\n\n/**\n * Setup the current view settings\n *\n * @param {number} courseId the course id\n * @param {setup} setup format, page and course settings\n * @property {boolean} setup.editing if the page is in edit mode\n */\nexport const setViewFormat = (courseId, setup) => {\n const editor = getCourseEditor(courseId);\n editor.setViewFormat(setup);\n};\n\n/**\n * Get a specific course editor reactive instance.\n *\n * @param {number} courseId the course id\n * @returns {CourseEditor}\n */\nexport const getCourseEditor = (courseId) => {\n courseId = parseInt(courseId);\n\n if (!courseEditorMap.has(courseId)) {\n courseEditorMap.set(\n courseId,\n new CourseEditor({\n name: `CourseEditor${courseId}`,\n eventName: events.stateChanged,\n eventDispatch: dispatchStateChangedEvent,\n // Mutations can be overridden by the format plugin using setMutations\n // but we need the default one at least.\n mutations: new DefaultMutations(),\n })\n );\n courseEditorMap.get(courseId).loadCourse(courseId);\n }\n return courseEditorMap.get(courseId);\n};\n\n/**\n * Get the current course reactive instance.\n *\n * @returns {CourseEditor}\n */\nexport const getCurrentCourseEditor = () => getCourseEditor(M.cfg.courseId);\n"],"file":"courseeditor.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/courseeditor.js"],"names":["courseEditorMap","Map","dispatchStateChangedEvent","detail","target","document","dispatchEvent","CustomEvent","events","stateChanged","bubbles","setViewFormat","courseId","setup","editor","getCourseEditor","parseInt","has","set","CourseEditor","name","eventName","eventDispatch","mutations","DefaultMutations","get","loadCourse","getCurrentCourseEditor","M","cfg"],"mappings":"iUAuBA,OACA,OACA,O,mDAGA,GAAMA,CAAAA,CAAe,CAAG,GAAIC,CAAAA,GAA5B,CAYA,QAASC,CAAAA,CAAT,CAAmCC,CAAnC,CAA2CC,CAA3C,CAAmD,CAC/C,GAAIA,CAAM,SAAV,CAA0B,CACtBA,CAAM,CAAGC,QACZ,CACDD,CAAM,CAACE,aAAP,CAAqB,GAAIC,CAAAA,WAAJ,CAAgBC,UAAOC,YAAvB,CAAqC,CACtDC,OAAO,GAD+C,CAEtDP,MAAM,CAAEA,CAF8C,CAArC,CAArB,CAIH,C,gBAU4B,QAAhBQ,CAAAA,aAAgB,CAACC,CAAD,CAAWC,CAAX,CAAqB,CAC9C,GAAMC,CAAAA,CAAM,CAAGC,CAAe,CAACH,CAAD,CAA9B,CACAE,CAAM,CAACH,aAAP,CAAqBE,CAArB,CACH,C,CAQM,GAAME,CAAAA,CAAe,CAAG,SAACH,CAAD,CAAc,CACzCA,CAAQ,CAAGI,QAAQ,CAACJ,CAAD,CAAnB,CAEA,GAAI,CAACZ,CAAe,CAACiB,GAAhB,CAAoBL,CAApB,CAAL,CAAoC,CAChCZ,CAAe,CAACkB,GAAhB,CACIN,CADJ,CAEI,GAAIO,UAAJ,CAAiB,CACbC,IAAI,uBAAiBR,CAAjB,CADS,CAEbS,SAAS,CAAEb,UAAOC,YAFL,CAGba,aAAa,CAAEpB,CAHF,CAMbqB,SAAS,CAAE,GAAIC,UANF,CAAjB,CAFJ,EAWAxB,CAAe,CAACyB,GAAhB,CAAoBb,CAApB,EAA8Bc,UAA9B,CAAyCd,CAAzC,CACH,CACD,MAAOZ,CAAAA,CAAe,CAACyB,GAAhB,CAAoBb,CAApB,CACV,CAlBM,C,6CAyB+B,QAAzBe,CAAAA,sBAAyB,SAAMZ,CAAAA,CAAe,CAACa,CAAC,CAACC,GAAF,CAAMjB,QAAP,CAArB,C","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 * Generic reactive module used in the course editor.\n *\n * @module core_courseformat/courseeditor\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DefaultMutations from 'core_courseformat/local/courseeditor/mutations';\nimport CourseEditor from 'core_courseformat/local/courseeditor/courseeditor';\nimport events from 'core_course/events';\n\n// A map with all the course editor instances.\nconst courseEditorMap = new Map();\n\n/**\n * Trigger a state changed event.\n *\n * This function will be moved to core_course/events module\n * when the file is migrated to the new JS events structure proposed in MDL-70990.\n *\n * @method dispatchStateChangedEvent\n * @param {object} detail the full state\n * @param {object} target the custom event target (document if none provided)\n */\nfunction dispatchStateChangedEvent(detail, target) {\n if (target === undefined) {\n target = document;\n }\n target.dispatchEvent(new CustomEvent(events.stateChanged, {\n bubbles: true,\n detail: detail,\n }));\n}\n\n/**\n * Setup the current view settings\n *\n * @param {number} courseId the course id\n * @param {setup} setup format, page and course settings\n * @property {boolean} setup.editing if the page is in edit mode\n * @property {boolean} setup.supportscomponents if the format supports components for content\n */\nexport const setViewFormat = (courseId, setup) => {\n const editor = getCourseEditor(courseId);\n editor.setViewFormat(setup);\n};\n\n/**\n * Get a specific course editor reactive instance.\n *\n * @param {number} courseId the course id\n * @returns {CourseEditor}\n */\nexport const getCourseEditor = (courseId) => {\n courseId = parseInt(courseId);\n\n if (!courseEditorMap.has(courseId)) {\n courseEditorMap.set(\n courseId,\n new CourseEditor({\n name: `CourseEditor${courseId}`,\n eventName: events.stateChanged,\n eventDispatch: dispatchStateChangedEvent,\n // Mutations can be overridden by the format plugin using setMutations\n // but we need the default one at least.\n mutations: new DefaultMutations(),\n })\n );\n courseEditorMap.get(courseId).loadCourse(courseId);\n }\n return courseEditorMap.get(courseId);\n};\n\n/**\n * Get the current course reactive instance.\n *\n * @returns {CourseEditor}\n */\nexport const getCurrentCourseEditor = () => getCourseEditor(M.cfg.courseId);\n"],"file":"courseeditor.min.js"} \ No newline at end of file diff --git a/course/format/amd/build/local/content.min.js b/course/format/amd/build/local/content.min.js new file mode 100644 index 00000000000..0433933f3c9 --- /dev/null +++ b/course/format/amd/build/local/content.min.js @@ -0,0 +1,2 @@ +define ("core_courseformat/local/content",["exports","core/reactive","core_courseformat/courseeditor","core/inplace_editable","core_course/actions"],function(a,b,c,d,e){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;d=f(d);e=f(e);function f(a){return a&&a.__esModule?a:{default:a}}function g(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){g=function(a){return typeof a}}else{g=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return g(a)}function h(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function i(a,b){for(var c=0,d;cb.length){var f,g,h=a.lastChild;d[null!==(f=null===h||void 0===h?void 0:null===(g=h.dataset)||void 0===g?void 0:g.id)&&void 0!==f?f:0]=h;a.removeChild(h)}}}],[{key:"init",value:function init(a,d){return new b({element:document.getElementById(a),reactive:(0,c.getCurrentCourseEditor)(),selectors:d})}}]);return b}(b.BaseComponent);a.default=r;return a.default}); +//# sourceMappingURL=content.min.js.map diff --git a/course/format/amd/build/local/content.min.js.map b/course/format/amd/build/local/content.min.js.map new file mode 100644 index 00000000000..4aaa1c859f7 --- /dev/null +++ b/course/format/amd/build/local/content.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../src/local/content.js"],"names":["Component","name","selectors","SECTION","SECTION_ITEM","SECTION_TITLE","SECTION_CMLIST","COURSE_SECTIONLIST","CM","dettachedCms","dettachedSections","reactive","supportComponents","watch","handler","_reloadCm","_refreshSectionNumber","_startProcessing","_refreshCourseSectionlist","_refreshSectionCmlist","element","cmitem","getElement","id","courseActions","refreshModule","target","Error","number","dataset","sectionid","inplace","inplaceeditable","getInplaceEditable","querySelector","currentvalue","getValue","currentitemid","getItemId","rawtitle","setValue","cmlist","section","listparent","_fixOrder","sectionlist","container","neworder","selector","dettachedelements","length","classList","add","innerHTML","remove","forEach","itemid","index","item","currentitem","children","append","insertBefore","lastchild","lastChild","removeChild","document","getElementById","BaseComponent"],"mappings":"wPA0BA,OAEA,O,krDAEqBA,CAAAA,C,+HAKR,CAEL,KAAKC,IAAL,CAAY,eAAZ,CAEA,KAAKC,SAAL,CAAiB,CACbC,OAAO,uBADM,CAEbC,YAAY,4BAFC,CAGbC,aAAa,6BAHA,CAIbC,cAAc,sBAJD,CAKbC,kBAAkB,kCALL,CAMbC,EAAE,sBANW,CAAjB,CASA,KAAKC,YAAL,CAAoB,EAApB,CACA,KAAKC,iBAAL,CAAyB,EAC5B,C,iDAsBa,CAEV,GAAI,CAAC,KAAKC,QAAL,CAAcC,iBAAnB,CAAsC,CAClC,MAAO,EACV,CACD,MAAO,CAEH,CAACC,KAAK,qBAAN,CAA8BC,OAAO,CAAE,KAAKC,SAA5C,CAFG,CAIH,CAACF,KAAK,yBAAN,CAAkCC,OAAO,CAAE,KAAKE,qBAAhD,CAJG,CAMH,CAACH,KAAK,oBAAN,CAA6BC,OAAO,CAAE,KAAKG,gBAA3C,CANG,CAOH,CAACJ,KAAK,6BAAN,CAAsCC,OAAO,CAAE,KAAKI,yBAApD,CAPG,CAQH,CAACL,KAAK,yBAAN,CAAkCC,OAAO,CAAE,KAAKK,qBAAhD,CARG,CAUV,C,8CAUoB,IAAVC,CAAAA,CAAU,GAAVA,OAAU,CACXC,CAAM,CAAG,KAAKC,UAAL,CAAgB,KAAKpB,SAAL,CAAeM,EAA/B,CAAmCY,CAAO,CAACG,EAA3C,CADE,CAEjB,GAAIF,CAAJ,CAAY,CACRG,UAAcC,aAAd,CAA4BJ,CAA5B,CAAoCD,CAAO,CAACG,EAA5C,CACH,CACJ,C,2DASkB,CAGf,KAAKd,YAAL,CAAoB,EAApB,CACA,KAAKC,iBAAL,CAAyB,EAC5B,C,sEAcgC,IAAVU,CAAAA,CAAU,GAAVA,OAAU,CAEvBM,CAAM,CAAG,KAAKJ,UAAL,CAAgB,KAAKpB,SAAL,CAAeC,OAA/B,CAAwCiB,CAAO,CAACG,EAAhD,CAFc,CAG7B,GAAI,CAACG,CAAL,CAAa,CACT,KAAM,IAAIC,CAAAA,KAAJ,kCAAoCP,CAAO,CAACG,EAA5C,EACT,CAEDG,CAAM,CAACH,EAAP,mBAAuBH,CAAO,CAACQ,MAA/B,EAIAF,CAAM,CAACG,OAAP,CAAeC,SAAf,CAA2BV,CAAO,CAACQ,MAAnC,CAEAF,CAAM,CAACG,OAAP,CAAeD,MAAf,CAAwBR,CAAO,CAACQ,MAAhC,CAGA,GAAMG,CAAAA,CAAO,CAAGC,UAAgBC,kBAAhB,CAAmCP,CAAM,CAACQ,aAAP,CAAqB,KAAKhC,SAAL,CAAeG,aAApC,CAAnC,CAAhB,CACA,GAAI0B,CAAJ,CAAa,IAGHI,CAAAA,CAAY,CAAGJ,CAAO,CAACK,QAAR,EAHZ,CAIHC,CAAa,CAAGN,CAAO,CAACO,SAAR,EAJb,CAMT,GAA2B,EAAvB,GAAAP,CAAO,CAACK,QAAR,EAAJ,CAA+B,CAE3B,GAAIC,CAAa,EAAIjB,CAAO,CAACG,EAAzB,GAAgCY,CAAY,EAAIf,CAAO,CAACmB,QAAxB,EAAwD,EAApB,EAAAnB,CAAO,CAACmB,QAA5E,CAAJ,CAAiG,CAC7FR,CAAO,CAACS,QAAR,CAAiBpB,CAAO,CAACmB,QAAzB,CACH,CACJ,CACJ,CACJ,C,sEAOgC,OAAVnB,CAAU,GAAVA,OAAU,CACvBqB,CAAM,WAAGrB,CAAO,CAACqB,MAAX,gBAAqB,EADJ,CAEvBC,CAAO,CAAG,KAAKpB,UAAL,CAAgB,KAAKpB,SAAL,CAAeC,OAA/B,CAAwCiB,CAAO,CAACG,EAAhD,CAFa,CAGvBoB,CAAU,QAAGD,CAAH,WAAGA,CAAH,QAAGA,CAAO,CAAER,aAAT,CAAuB,KAAKhC,SAAL,CAAeI,cAAtC,CAHU,CAI7B,GAAIqC,CAAJ,CAAgB,CACZ,KAAKC,SAAL,CAAeD,CAAf,CAA2BF,CAA3B,CAAmC,KAAKvC,SAAL,CAAeM,EAAlD,CAAsD,KAAKC,YAA3D,CACH,CACJ,C,8EAOoC,OAAVW,CAAU,GAAVA,OAAU,CAC3ByB,CAAW,WAAGzB,CAAO,CAACyB,WAAX,gBAA0B,EADV,CAE3BF,CAAU,CAAG,KAAKrB,UAAL,CAAgB,KAAKpB,SAAL,CAAeK,kBAA/B,CAFc,CAGjC,GAAIoC,CAAJ,CAAgB,CACZ,KAAKC,SAAL,CAAeD,CAAf,CAA2BE,CAA3B,CAAwC,KAAK3C,SAAL,CAAeC,OAAvD,CAAgE,KAAKO,iBAArE,CACH,CACJ,C,4CAUSoC,C,CAAWC,C,CAAUC,C,CAAUC,C,CAAmB,YAGxD,GAAI,CAACF,CAAQ,CAACG,MAAd,CAAsB,CAClBJ,CAAS,CAACK,SAAV,CAAoBC,GAApB,CAAwB,QAAxB,EACAN,CAAS,CAACO,SAAV,CAAsB,EAAtB,CACA,MACH,CAGDP,CAAS,CAACK,SAAV,CAAoBG,MAApB,CAA2B,QAA3B,EAGAP,CAAQ,CAACQ,OAAT,CAAiB,SAACC,CAAD,CAASC,CAAT,CAAmB,OAC1BC,CAAI,WAAG,CAAI,CAACpC,UAAL,CAAgB0B,CAAhB,CAA0BQ,CAA1B,CAAH,gBAAwCP,CAAiB,CAACO,CAAD,CADnC,CAG1BG,CAAW,CAAGb,CAAS,CAACc,QAAV,CAAmBH,CAAnB,CAHY,CAIhC,GAAIE,CAAW,SAAf,CAA+B,CAC3Bb,CAAS,CAACe,MAAV,CAAiBH,CAAjB,EACA,MACH,CACD,GAAIC,CAAW,GAAKD,CAApB,CAA0B,CACtBZ,CAAS,CAACgB,YAAV,CAAuBJ,CAAvB,CAA6BC,CAA7B,CACH,CACJ,CAXD,EAaA,MAAOb,CAAS,CAACc,QAAV,CAAmBV,MAAnB,CAA4BH,CAAQ,CAACG,MAA5C,CAAoD,SAC1Ca,CAAS,CAAGjB,CAAS,CAACkB,SADoB,CAEhDf,CAAiB,kBAACc,CAAD,WAACA,CAAD,kBAACA,CAAS,CAAElC,OAAZ,qBAAC,EAAoBN,EAArB,gBAA2B,CAA3B,CAAjB,CAAiDwC,CAAjD,CACAjB,CAAS,CAACmB,WAAV,CAAsBF,CAAtB,CACH,CACJ,C,oCAzKWrC,C,CAAQxB,C,CAAW,CAC3B,MAAO,IAAIF,CAAAA,CAAJ,CAAc,CACjBoB,OAAO,CAAE8C,QAAQ,CAACC,cAAT,CAAwBzC,CAAxB,CADQ,CAEjBf,QAAQ,CAAE,8BAFO,CAGjBT,SAAS,CAATA,CAHiB,CAAd,CAKV,C,cAnCkCkE,e","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 {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport inplaceeditable from 'core/inplace_editable';\n// Course actions is needed for actions that are not migrated to components.\nimport courseActions from 'core_course/actions';\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\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_item']`,\n SECTION_TITLE: `[data-for='section_title']`,\n SECTION_CMLIST: `[data-for='cmlist']`,\n COURSE_SECTIONLIST: `[data-for='course_sectionlist']`,\n CM: `[data-for='cmitem']`,\n };\n // Array to save dettached elements during element resorting.\n this.dettachedCms = {};\n this.dettachedSections = {};\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 * @return {Component}\n */\n static init(target, selectors) {\n return new Component({\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n selectors,\n });\n }\n\n /**\n * Return the component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\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 // Update section number and title.\n {watch: `section.number:updated`, handler: this._refreshSectionNumber},\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 ];\n }\n\n /**\n * Reload a course module.\n *\n * Most course module HTML is still strongly backend dependant.\n * Some changes require to get a new version af the module.\n *\n * @param {Object} update the state update data\n */\n _reloadCm({element}) {\n const cmitem = this.getElement(this.selectors.CM, element.id);\n if (cmitem) {\n courseActions.refreshModule(cmitem, element.id);\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 * 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} 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 throw new Error(`Unkown section with ID ${element.id}`);\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_TITLE));\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} 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 if (listparent) {\n this._fixOrder(listparent, cmlist, this.selectors.CM, this.dettachedCms);\n }\n }\n\n /**\n * Refresh the section list.\n *\n * @param {Object} details the update details.\n */\n _refreshCourseSectionlist({element}) {\n const sectionlist = element.sectionlist ?? [];\n const listparent = this.getElement(this.selectors.COURSE_SECTIONLIST);\n if (listparent) {\n this._fixOrder(listparent, sectionlist, this.selectors.SECTION, this.dettachedSections);\n }\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 */\n _fixOrder(container, neworder, selector, dettachedelements) {\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 const item = this.getElement(selector, itemid) ?? dettachedelements[itemid];\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 // Remove the remaining elements.\n while (container.children.length > neworder.length) {\n const lastchild = container.lastChild;\n dettachedelements[lastchild?.dataset?.id ?? 0] = lastchild;\n container.removeChild(lastchild);\n }\n }\n}\n"],"file":"content.min.js"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/courseeditor.min.js b/course/format/amd/build/local/courseeditor/courseeditor.min.js index 21e7feb77ba..7940e7be3e2 100644 --- a/course/format/amd/build/local/courseeditor/courseeditor.min.js +++ b/course/format/amd/build/local/courseeditor/courseeditor.min.js @@ -1,2 +1,2 @@ -define ("core_courseformat/local/courseeditor/courseeditor",["exports","core/reactive","core/notification","core_courseformat/local/courseeditor/exporter","core/log","core/ajax"],function(a,b,c,d,e,f){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;c=g(c);d=g(d);e=g(e);f=g(f);function g(a){return a&&a.__esModule?a:{default:a}}function h(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){h=function(a){return typeof a}}else{h=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return h(a)}function i(a,b){var c=Object.keys(a);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(a);if(b)d=d.filter(function(b){return Object.getOwnPropertyDescriptor(a,b).enumerable});c.push.apply(c,d)}return c}function j(a){for(var b=1,c;b.\n\nimport {Reactive} from 'core/reactive';\nimport notification from 'core/notification';\nimport Exporter from 'core_courseformat/local/courseeditor/exporter';\nimport log from 'core/log';\nimport ajax from 'core/ajax';\n\n/**\n * Main course editor module.\n *\n * All formats can register new components on this object to create new reactive\n * UI components that watch the current course state.\n *\n * @module core_courseformat/local/courseeditor/courseeditor\n * @class core_courseformat/local/courseeditor/courseeditor\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class extends Reactive {\n\n /**\n * Set up the course editor when the page is ready.\n *\n * The course can only be loaded once per instance. Otherwise an error is thrown.\n *\n * @param {number} courseId course id\n */\n async loadCourse(courseId) {\n\n if (this.courseId) {\n throw new Error(`Cannot load ${courseId}, course already loaded with id ${this.courseId}`);\n }\n\n // Default view format setup.\n this._editing = false;\n\n this.courseId = courseId;\n\n let stateData;\n\n try {\n stateData = await this.getServerCourseState();\n } catch (error) {\n log.error(\"EXCEPTION RAISED WHILE INIT COURSE EDITOR\");\n log.error(error);\n return;\n }\n\n this.setInitialState(stateData);\n }\n\n /**\n * Setup the current view settings\n *\n * @param {Object} setup format, page and course settings\n * @property {boolean} setup.editing if the page is in edit mode\n */\n setViewFormat(setup) {\n this._editing = setup.editing ?? false;\n }\n\n /**\n * Load the current course state from the server.\n *\n * @returns {Object} the current course state\n */\n async getServerCourseState() {\n const courseState = await ajax.call([{\n methodname: 'core_courseformat_get_state',\n args: {\n courseid: this.courseId,\n }\n }])[0];\n\n const stateData = JSON.parse(courseState);\n\n return {\n course: {},\n section: [],\n cm: [],\n ...stateData,\n };\n }\n\n /**\n * Return the current edit mode.\n *\n * Components should use this method to check if edit mode is active.\n *\n * @return {boolean} if edit is enabled\n */\n get isEditing() {\n return this._editing ?? false;\n }\n\n /**\n * Return a data exporter to transform state part into mustache contexts.\n *\n * @return {Exporter} the exporter class\n */\n getExporter() {\n return new Exporter(this);\n }\n\n /**\n * Dispatch a change in the state.\n *\n * Usually reactive modules throw an error directly to the components when something\n * goes wrong. However, course editor can directly display a notification.\n *\n * @method dispatch\n * @param {string} actionName the action name (usually the mutation name)\n * @param {*} param any number of params the mutation needs.\n */\n async dispatch(...args) {\n try {\n await super.dispatch(...args);\n } catch (error) {\n notification.exception(error);\n }\n }\n}\n"],"file":"courseeditor.min.js"} \ No newline at end of file +{"version":3,"sources":["../../../src/local/courseeditor/courseeditor.js"],"names":["courseId","Error","_editing","_supportscomponents","getServerCourseState","stateData","log","error","setInitialState","setup","editing","supportscomponents","ajax","call","methodname","args","courseid","courseState","JSON","parse","course","section","cm","Exporter","notification","exception","Reactive"],"mappings":"wRAgBA,OACA,OACA,OACA,O,qrGAsBqBA,C,6FAET,KAAKA,Q,sBACC,IAAIC,CAAAA,KAAJ,uBAAyBD,CAAzB,4CAAoE,KAAKA,QAAzE,E,QAIV,KAAKE,QAAL,IACA,KAAKC,mBAAL,IAEA,KAAKH,QAAL,CAAgBA,CAAhB,C,wBAKsB,MAAKI,oBAAL,E,QAAlBC,C,6DAEAC,UAAIC,KAAJ,CAAU,2CAAV,EACAD,UAAIC,KAAJ,O,kCAIJ,KAAKC,eAAL,CAAqBH,CAArB,E,6KAUUI,C,CAAO,SACjB,KAAKP,QAAL,WAAgBO,CAAK,CAACC,OAAtB,mBACA,KAAKP,mBAAL,WAA2BM,CAAK,CAACE,kBAAjC,kBACH,C,oMAQ6BC,WAAKC,IAAL,CAAU,CAAC,CACjCC,UAAU,CAAE,6BADqB,CAEjCC,IAAI,CAAE,CACFC,QAAQ,CAAE,KAAKhB,QADb,CAF2B,CAAD,CAAV,EAKtB,CALsB,C,QAApBiB,C,QAOAZ,C,CAAYa,IAAI,CAACC,KAAL,CAAWF,CAAX,C,6BAGdG,MAAM,CAAE,E,CACRC,OAAO,CAAE,E,CACTC,EAAE,CAAE,E,EACDjB,C,6KAoBG,CACV,MAAO,IAAIkB,UAAJ,CAAa,IAAb,CACV,C,iNAqBiBR,C,uBAAAA,C,yFAEcA,C,4DAExBS,UAAaC,SAAb,O,0JApCQ,OACZ,iBAAO,KAAKvB,QAAZ,kBACH,C,6CAgBuB,OACpB,iBAAO,KAAKC,mBAAZ,kBACH,C,cAhGwBuB,U","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 {Reactive} from 'core/reactive';\nimport notification from 'core/notification';\nimport Exporter from 'core_courseformat/local/courseeditor/exporter';\nimport log from 'core/log';\nimport ajax from 'core/ajax';\n\n/**\n * Main course editor module.\n *\n * All formats can register new components on this object to create new reactive\n * UI components that watch the current course state.\n *\n * @module core_courseformat/local/courseeditor/courseeditor\n * @class core_courseformat/local/courseeditor/courseeditor\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default class extends Reactive {\n\n /**\n * Set up the course editor when the page is ready.\n *\n * The course can only be loaded once per instance. Otherwise an error is thrown.\n *\n * @param {number} courseId course id\n */\n async loadCourse(courseId) {\n\n if (this.courseId) {\n throw new Error(`Cannot load ${courseId}, course already loaded with id ${this.courseId}`);\n }\n\n // Default view format setup.\n this._editing = false;\n this._supportscomponents = false;\n\n this.courseId = courseId;\n\n let stateData;\n\n try {\n stateData = await this.getServerCourseState();\n } catch (error) {\n log.error(\"EXCEPTION RAISED WHILE INIT COURSE EDITOR\");\n log.error(error);\n return;\n }\n\n this.setInitialState(stateData);\n }\n\n /**\n * Setup the current view settings\n *\n * @param {Object} setup format, page and course settings\n * @property {boolean} setup.editing if the page is in edit mode\n * @property {boolean} setup.supportscomponents if the format supports components for content\n */\n setViewFormat(setup) {\n this._editing = setup.editing ?? false;\n this._supportscomponents = setup.supportscomponents ?? false;\n }\n\n /**\n * Load the current course state from the server.\n *\n * @returns {Object} the current course state\n */\n async getServerCourseState() {\n const courseState = await ajax.call([{\n methodname: 'core_courseformat_get_state',\n args: {\n courseid: this.courseId,\n }\n }])[0];\n\n const stateData = JSON.parse(courseState);\n\n return {\n course: {},\n section: [],\n cm: [],\n ...stateData,\n };\n }\n\n /**\n * Return the current edit mode.\n *\n * Components should use this method to check if edit mode is active.\n *\n * @return {boolean} if edit is enabled\n */\n get isEditing() {\n return this._editing ?? false;\n }\n\n /**\n * Return a data exporter to transform state part into mustache contexts.\n *\n * @return {Exporter} the exporter class\n */\n getExporter() {\n return new Exporter(this);\n }\n\n /**\n * Return if the current course support components to refresh the content.\n *\n * @returns {boolean} if the current content support components\n */\n get supportComponents() {\n return this._supportscomponents ?? false;\n }\n\n /**\n * Dispatch a change in the state.\n *\n * Usually reactive modules throw an error directly to the components when something\n * goes wrong. However, course editor can directly display a notification.\n *\n * @method dispatch\n * @param {string} actionName the action name (usually the mutation name)\n * @param {*} param any number of params the mutation needs.\n */\n async dispatch(...args) {\n try {\n await super.dispatch(...args);\n } catch (error) {\n notification.exception(error);\n }\n }\n}\n"],"file":"courseeditor.min.js"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/dndcmitem.min.js b/course/format/amd/build/local/courseeditor/dndcmitem.min.js new file mode 100644 index 00000000000..7bd1d49c60f --- /dev/null +++ b/course/format/amd/build/local/courseeditor/dndcmitem.min.js @@ -0,0 +1,2 @@ +define ("core_courseformat/local/courseeditor/dndcmitem",["exports","core/reactive"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;function c(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){c=function(a){return typeof a}}else{c=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return c(a)}function d(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function e(a,b){for(var c=0,d;c.\n\n/**\n * Course index cm component.\n *\n * This component is used to control specific course modules interactions like drag and drop\n * in both course index and course content.\n *\n * @module core_courseformat/local/courseeditor/dndcmitem\n * @class core_courseformat/local/courseeditor/dndcmitem\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\n\nexport default class extends BaseComponent {\n\n /**\n * Configure the component drag and drop.\n *\n * @param {number} cmid course module id\n */\n configDragDrop(cmid) {\n\n this.id = cmid;\n\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init element drag and drop.\n this.dragdrop = new DragDrop(this);\n // Save dropzone classes.\n this.classes = this.dragdrop.getClasses();\n }\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n // Drag and drop methods.\n\n /**\n * Get the draggable data of this component.\n *\n * @returns {Object} exported course module drop data\n */\n getDraggableData() {\n const exporter = this.reactive.getExporter();\n return exporter.cmDraggableData(this.reactive.state, this.id);\n }\n\n /**\n * Validate if the drop data can be dropped over the component.\n *\n * @param {Object} dropdata the exported drop data.\n * @returns {boolean}\n */\n validateDropData(dropdata) {\n return dropdata?.type === 'cm';\n }\n\n /**\n * Display the component dropzone.\n *\n * @param {Object} dropdata the accepted drop data\n */\n showDropZone(dropdata) {\n // If we are the next cmid of the dragged element we accept the drop because otherwise it\n // will get captured by the section. However, we won't trigger any mutation.\n if (dropdata.nextcmid != this.id && dropdata.id != this.id) {\n this.element.classList.add(this.classes.DROPUP);\n }\n }\n\n /**\n * Hide the component dropzone.\n */\n hideDropZone() {\n this.element.classList.remove(this.classes.DROPUP);\n }\n\n /**\n * Drop event handler.\n *\n * @param {Object} dropdata the accepted drop data\n */\n drop(dropdata) {\n // Call the move mutation if necessary.\n if (dropdata.id != this.id && dropdata.nextcmid != this.id) {\n this.reactive.dispatch('cmMove', [dropdata.id], null, this.id);\n }\n }\n\n}\n"],"file":"dndcmitem.min.js"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/dndsection.min.js b/course/format/amd/build/local/courseeditor/dndsection.min.js new file mode 100644 index 00000000000..df8e2755369 --- /dev/null +++ b/course/format/amd/build/local/courseeditor/dndsection.min.js @@ -0,0 +1,2 @@ +define ("core_courseformat/local/courseeditor/dndsection",["exports","core/reactive"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;function c(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){c=function(a){return typeof a}}else{c=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return c(a)}function d(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function e(a,b){for(var c=0,d;ca.number){this.element.classList.remove(this.classes.DROPUP);this.element.classList.add(this.classes.DROPDOWN)}else{this.element.classList.add(this.classes.DROPUP);this.element.classList.remove(this.classes.DROPDOWN)}}}},{key:"hideDropZone",value:function hideDropZone(){var a;null===(a=this.getLastCm())||void 0===a?void 0:a.classList.remove(this.classes.DROPDOWN);this.element.classList.remove(this.classes.DROPUP);this.element.classList.remove(this.classes.DROPDOWN)}},{key:"drop",value:function drop(a){if("cm"==a.type){this.reactive.dispatch("cmMove",[a.id],this.id)}if("section"==a.type){this.reactive.dispatch("sectionMove",[a.id],this.id)}}}]);return c}(b.BaseComponent);a.default=n;return a.default}); +//# sourceMappingURL=dndsection.min.js.map diff --git a/course/format/amd/build/local/courseeditor/dndsection.min.js.map b/course/format/amd/build/local/courseeditor/dndsection.min.js.map new file mode 100644 index 00000000000..0e1b3ff474e --- /dev/null +++ b/course/format/amd/build/local/courseeditor/dndsection.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../src/local/courseeditor/dndsection.js"],"names":["state","id","element","dataset","section","get","course","sectionitem","reactive","isEditing","supportComponents","dragdrop","DragDrop","classes","getClasses","unregister","dropdata","type","sectionzeroid","sectionlist","getLastCm","classList","add","DROPDOWN","number","remove","DROPUP","dispatch","BaseComponent"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Course index section component.\n *\n * This component is used to control specific course section interactions like drag and drop\n * in both course index and course content.\n *\n * @module core_courseformat/local/courseeditor/dndsection\n * @class core_courseformat/local/courseeditor/dndsection\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\n\nexport default class extends BaseComponent {\n\n /**\n * Save some values form the state.\n *\n * @param {Object} state the current state\n */\n configState(state) {\n this.id = this.element.dataset.id;\n this.section = state.section.get(this.id);\n this.course = state.course;\n }\n\n /**\n * Register state values and the drag and drop subcomponent.\n *\n * @param {BaseComponent} sectionitem section item component\n */\n configDragDrop(sectionitem) {\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init the inner dragable element.\n this.sectionitem = sectionitem;\n // Init the dropzone.\n this.dragdrop = new DragDrop(this);\n // Save dropzone classes.\n this.classes = this.dragdrop.getClasses();\n }\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.sectionitem !== undefined) {\n this.sectionitem.unregister();\n }\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n /**\n * Get the last CM element of that section.\n *\n * @returns {element|null} the las course module element of the section.\n */\n getLastCm() {\n return null;\n }\n\n // Drag and drop methods.\n\n /**\n * Validate if the drop data can be dropped over the component.\n *\n * @param {Object} dropdata the exported drop data.\n * @returns {boolean}\n */\n validateDropData(dropdata) {\n // We accept any course module.\n if (dropdata?.type === 'cm') {\n return true;\n }\n // We accept any section bu the section 0 or ourself\n if (dropdata?.type === 'section') {\n const sectionzeroid = this.course.sectionlist[0];\n return dropdata?.id != this.id && dropdata?.id != sectionzeroid && this.id != sectionzeroid;\n }\n return false;\n }\n\n /**\n * Display the component dropzone.\n *\n * @param {Object} dropdata the accepted drop data\n */\n showDropZone(dropdata) {\n if (dropdata.type == 'cm') {\n this.getLastCm()?.classList.add(this.classes.DROPDOWN);\n }\n if (dropdata.type == 'section') {\n // The relative move of section depends on the section number.\n if (this.section.number > dropdata.number) {\n this.element.classList.remove(this.classes.DROPUP);\n this.element.classList.add(this.classes.DROPDOWN);\n } else {\n this.element.classList.add(this.classes.DROPUP);\n this.element.classList.remove(this.classes.DROPDOWN);\n }\n }\n }\n\n /**\n * Hide the component dropzone.\n */\n hideDropZone() {\n this.getLastCm()?.classList.remove(this.classes.DROPDOWN);\n this.element.classList.remove(this.classes.DROPUP);\n this.element.classList.remove(this.classes.DROPDOWN);\n }\n\n /**\n * Drop event handler.\n *\n * @param {Object} dropdata the accepted drop data\n */\n drop(dropdata) {\n // Call the move mutation.\n if (dropdata.type == 'cm') {\n this.reactive.dispatch('cmMove', [dropdata.id], this.id);\n }\n if (dropdata.type == 'section') {\n this.reactive.dispatch('sectionMove', [dropdata.id], this.id);\n }\n }\n}\n"],"file":"dndsection.min.js"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/dndsectionitem.min.js b/course/format/amd/build/local/courseeditor/dndsectionitem.min.js new file mode 100644 index 00000000000..b8638579b77 --- /dev/null +++ b/course/format/amd/build/local/courseeditor/dndsectionitem.min.js @@ -0,0 +1,2 @@ +define ("core_courseformat/local/courseeditor/dndsectionitem",["exports","core/reactive"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;function c(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){c=function(a){return typeof a}}else{c=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return c(a)}function d(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function e(a,b){for(var c=0,d;c.\n\n/**\n * Course index section title draggable component.\n *\n * This component is used to control specific course section interactions like drag and drop\n * in both course index and course content.\n *\n * @module core_courseformat/local/courseeditor/dndsectionitem\n * @class core_courseformat/local/courseeditor/dndsectionitem\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent, DragDrop} from 'core/reactive';\n\nexport default class extends BaseComponent {\n\n /**\n * Initial state ready method.\n *\n * @param {number} sectionid the section id\n * @param {Object} state the initial state\n * @param {Element} fullregion the complete section region to mark as dragged\n */\n configDragDrop(sectionid, state, fullregion) {\n\n this.id = sectionid;\n if (this.section === undefined) {\n this.section = state.section.get(this.id);\n }\n if (this.course === undefined) {\n this.course = state.course;\n }\n\n // Prevent topic zero from being draggable.\n if (this.section.number > 0) {\n this.getDraggableData = this._getDraggableData;\n }\n\n this.fullregion = fullregion;\n\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init the dropzone.\n this.dragdrop = new DragDrop(this);\n // Save dropzone classes.\n this.classes = this.dragdrop.getClasses();\n }\n }\n\n /**\n * Remove all subcomponents dependencies.\n */\n destroy() {\n if (this.dragdrop !== undefined) {\n this.dragdrop.unregister();\n }\n }\n\n // Drag and drop methods.\n\n /**\n * Get the draggable data of this component.\n *\n * @returns {Object} exported course module drop data\n */\n _getDraggableData() {\n const exporter = this.reactive.getExporter();\n return exporter.sectionDraggableData(this.reactive.state, this.id);\n }\n\n /**\n * Validate if the drop data can be dropped over the component.\n *\n * @param {Object} dropdata the exported drop data.\n * @returns {boolean}\n */\n validateDropData(dropdata) {\n // Course module validation.\n if (dropdata?.type === 'cm') {\n // The first section element is already there so we can ignore it.\n const firstcmid = this.section?.cmlist[0];\n return dropdata.id !== firstcmid;\n }\n return false;\n }\n\n /**\n * Display the component dropzone.\n *\n * @param {Object} dropdata the accepted drop data\n */\n showDropZone() {\n this.element.classList.add(this.classes.DROPZONE);\n }\n\n /**\n * Hide the component dropzone.\n */\n hideDropZone() {\n this.element.classList.remove(this.classes.DROPZONE);\n }\n\n /**\n * Drop event handler.\n *\n * @param {Object} dropdata the accepted drop data\n */\n drop(dropdata) {\n // Call the move mutation.\n if (dropdata.type == 'cm') {\n this.reactive.dispatch('cmMove', [dropdata.id], this.id, this.section?.cmlist[0]);\n }\n }\n}\n"],"file":"dndsectionitem.min.js"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseeditor/exporter.min.js b/course/format/amd/build/local/courseeditor/exporter.min.js index 7f492602506..09973029b2f 100644 --- a/course/format/amd/build/local/courseeditor/exporter.min.js +++ b/course/format/amd/build/local/courseeditor/exporter.min.js @@ -1,2 +1,2 @@ -define ("core_courseformat/local/courseeditor/exporter",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;function b(a,b){var c=Object.keys(a);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(a);if(b)d=d.filter(function(b){return Object.getOwnPropertyDescriptor(a,b).enumerable});c.push.apply(c,d)}return c}function c(a){for(var c=1,e;c.\n\n/**\n * Module to export parts of the state and transform them to be used in templates\n * and as draggable data.\n *\n * @module core_courseformat/local/courseeditor/exporter\n * @class core_courseformat/local/courseeditor/exporter\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 /**\n * Class constructor.\n *\n * @param {CourseEditor} reactive the course editor object\n */\n constructor(reactive) {\n this.reactive = reactive;\n }\n\n /**\n * Generate the course export data from the state.\n *\n * @param {Object} state the current state.\n * @returns {Object}\n */\n course(state) {\n // Collect section information from the state.\n const data = {\n sections: [],\n editmode: this.reactive.isEditing,\n };\n const sectionlist = state.course.sectionlist ?? [];\n sectionlist.forEach(sectionid => {\n const sectioninfo = state.section.get(sectionid) ?? {};\n const section = this.section(state, sectioninfo);\n data.sections.push(section);\n });\n data.hassections = (data.sections.length != 0);\n\n return data;\n }\n\n /**\n * Generate a section export data from the state.\n *\n * @param {Object} state the current state.\n * @param {Object} sectioninfo the section state data.\n * @returns {Object}\n */\n section(state, sectioninfo) {\n const section = {\n ...sectioninfo,\n cms: [],\n isactive: false,\n };\n const cmlist = sectioninfo.cmlist ?? [];\n cmlist.forEach(cmid => {\n const cminfo = state.cm.get(cmid);\n const cm = this.cm(state, cminfo);\n section.cms.push(cm);\n });\n section.hascms = (section.cms.length != 0);\n\n return section;\n }\n\n /**\n * Generate a cm export data from the state.\n *\n * @param {Object} state the current state.\n * @param {Object} cminfo the course module state data.\n * @returns {Object}\n */\n cm(state, cminfo) {\n const cm = {\n ...cminfo,\n isactive: false,\n };\n return cm;\n }\n}\n"],"file":"exporter.min.js"} \ No newline at end of file +{"version":3,"sources":["../../../src/local/courseeditor/exporter.js"],"names":["reactive","state","data","sections","editmode","isEditing","sectionlist","course","forEach","sectionid","sectioninfo","section","get","push","hassections","length","cms","isactive","cmlist","cmid","cminfo","cm","hascms","nextcmid","currentindex","indexOf","id","type","name","number"],"mappings":"4rCA+BI,WAAYA,CAAZ,CAAsB,WAClB,KAAKA,QAAL,CAAgBA,CACnB,C,yCAQMC,C,CAAO,cAEJC,CAAI,CAAG,CACTC,QAAQ,CAAE,EADD,CAETC,QAAQ,CAAE,KAAKJ,QAAL,CAAcK,SAFf,CAFH,CAMJC,CAAW,WAAGL,CAAK,CAACM,MAAN,CAAaD,WAAhB,gBAA+B,EANtC,CAOVA,CAAW,CAACE,OAAZ,CAAoB,SAAAC,CAAS,CAAI,OACvBC,CAAW,WAAGT,CAAK,CAACU,OAAN,CAAcC,GAAd,CAAkBH,CAAlB,CAAH,gBAAmC,EADvB,CAEvBE,CAAO,CAAG,CAAI,CAACA,OAAL,CAAaV,CAAb,CAAoBS,CAApB,CAFa,CAG7BR,CAAI,CAACC,QAAL,CAAcU,IAAd,CAAmBF,CAAnB,CACH,CAJD,EAKAT,CAAI,CAACY,WAAL,CAA4C,CAAxB,EAAAZ,CAAI,CAACC,QAAL,CAAcY,MAAlC,CAEA,MAAOb,CAAAA,CACV,C,gCASOD,C,CAAOS,C,CAAa,cAClBC,CAAO,MACND,CADM,EAETM,GAAG,CAAE,EAFI,CAGTC,QAAQ,GAHC,EADW,CAMlBC,CAAM,WAAGR,CAAW,CAACQ,MAAf,gBAAyB,EANb,CAOxBA,CAAM,CAACV,OAAP,CAAe,SAAAW,CAAI,CAAI,IACbC,CAAAA,CAAM,CAAGnB,CAAK,CAACoB,EAAN,CAAST,GAAT,CAAaO,CAAb,CADI,CAEbE,CAAE,CAAG,CAAI,CAACA,EAAL,CAAQpB,CAAR,CAAemB,CAAf,CAFQ,CAGnBT,CAAO,CAACK,GAAR,CAAYH,IAAZ,CAAiBQ,CAAjB,CACH,CAJD,EAKAV,CAAO,CAACW,MAAR,CAAwC,CAAtB,EAAAX,CAAO,CAACK,GAAR,CAAYD,MAA9B,CAEA,MAAOJ,CAAAA,CACV,C,2BASEV,C,CAAOmB,C,CAAQ,CACd,GAAMC,CAAAA,CAAE,MACDD,CADC,EAEJH,QAAQ,GAFJ,EAAR,CAIA,MAAOI,CAAAA,CACV,C,wDAYepB,C,CAAOkB,C,CAAM,CACzB,GAAMC,CAAAA,CAAM,CAAGnB,CAAK,CAACoB,EAAN,CAAST,GAAT,CAAaO,CAAb,CAAf,CACA,GAAI,CAACC,CAAL,CAAa,CACT,MAAO,KACV,CAJwB,GAOrBG,CAAAA,CAPqB,CAQnBZ,CAAO,CAAGV,CAAK,CAACU,OAAN,CAAcC,GAAd,CAAkBQ,CAAM,CAACX,SAAzB,CARS,CASnBe,CAAY,QAAGb,CAAH,WAAGA,CAAH,QAAGA,CAAO,CAAEO,MAAT,CAAgBO,OAAhB,CAAwBL,CAAM,CAACM,EAA/B,CATI,CAUzB,GAAIF,CAAY,SAAhB,CAAgC,CAC5BD,CAAQ,QAAGZ,CAAH,WAAGA,CAAH,QAAGA,CAAO,CAAEO,MAAT,CAAgBM,CAAY,CAAG,CAA/B,CACd,CAED,MAAO,CACHG,IAAI,CAAE,IADH,CAEHD,EAAE,CAAEN,CAAM,CAACM,EAFR,CAGHE,IAAI,CAAER,CAAM,CAACQ,IAHV,CAIHL,QAAQ,CAARA,CAJG,CAMV,C,kEAYoBtB,C,CAAOQ,C,CAAW,CACnC,GAAMC,CAAAA,CAAW,CAAGT,CAAK,CAACU,OAAN,CAAcC,GAAd,CAAkBH,CAAlB,CAApB,CACA,GAAI,CAACC,CAAL,CAAkB,CACd,MAAO,KACV,CACD,MAAO,CACHiB,IAAI,CAAE,SADH,CAEHD,EAAE,CAAEhB,CAAW,CAACgB,EAFb,CAGHE,IAAI,CAAElB,CAAW,CAACkB,IAHf,CAIHC,MAAM,CAAEnB,CAAW,CAACmB,MAJjB,CAMV,C","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 * Module to export parts of the state and transform them to be used in templates\n * and as draggable data.\n *\n * @module core_courseformat/local/courseeditor/exporter\n * @class core_courseformat/local/courseeditor/exporter\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 /**\n * Class constructor.\n *\n * @param {CourseEditor} reactive the course editor object\n */\n constructor(reactive) {\n this.reactive = reactive;\n }\n\n /**\n * Generate the course export data from the state.\n *\n * @param {Object} state the current state.\n * @returns {Object}\n */\n course(state) {\n // Collect section information from the state.\n const data = {\n sections: [],\n editmode: this.reactive.isEditing,\n };\n const sectionlist = state.course.sectionlist ?? [];\n sectionlist.forEach(sectionid => {\n const sectioninfo = state.section.get(sectionid) ?? {};\n const section = this.section(state, sectioninfo);\n data.sections.push(section);\n });\n data.hassections = (data.sections.length != 0);\n\n return data;\n }\n\n /**\n * Generate a section export data from the state.\n *\n * @param {Object} state the current state.\n * @param {Object} sectioninfo the section state data.\n * @returns {Object}\n */\n section(state, sectioninfo) {\n const section = {\n ...sectioninfo,\n cms: [],\n isactive: false,\n };\n const cmlist = sectioninfo.cmlist ?? [];\n cmlist.forEach(cmid => {\n const cminfo = state.cm.get(cmid);\n const cm = this.cm(state, cminfo);\n section.cms.push(cm);\n });\n section.hascms = (section.cms.length != 0);\n\n return section;\n }\n\n /**\n * Generate a cm export data from the state.\n *\n * @param {Object} state the current state.\n * @param {Object} cminfo the course module state data.\n * @returns {Object}\n */\n cm(state, cminfo) {\n const cm = {\n ...cminfo,\n isactive: false,\n };\n return cm;\n }\n\n /**\n * Generate a dragable cm data structure.\n *\n * This method is used by any draggable course module element to generate drop data\n * for its reactive/dragdrop instance.\n *\n * @param {*} state the state object\n * @param {*} cmid the cours emodule id\n * @returns {Object|null}\n */\n cmDraggableData(state, cmid) {\n const cminfo = state.cm.get(cmid);\n if (!cminfo) {\n return null;\n }\n\n // Drop an activity over the next activity is the same as doing anything.\n let nextcmid;\n const section = state.section.get(cminfo.sectionid);\n const currentindex = section?.cmlist.indexOf(cminfo.id);\n if (currentindex !== undefined) {\n nextcmid = section?.cmlist[currentindex + 1];\n }\n\n return {\n type: 'cm',\n id: cminfo.id,\n name: cminfo.name,\n nextcmid,\n };\n }\n\n /**\n * Generate a dragable cm data structure.\n *\n * This method is used by any draggable section element to generate drop data\n * for its reactive/dragdrop instance.\n *\n * @param {*} state the state object\n * @param {*} sectionid the cours section id\n * @returns {Object|null}\n */\n sectionDraggableData(state, sectionid) {\n const sectioninfo = state.section.get(sectionid);\n if (!sectioninfo) {\n return null;\n }\n return {\n type: 'section',\n id: sectioninfo.id,\n name: sectioninfo.name,\n number: sectioninfo.number,\n };\n }\n}\n"],"file":"exporter.min.js"} \ 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 c247ae2c35b..bafbd33d1a2 100644 --- a/course/format/amd/build/local/courseeditor/mutations.min.js +++ b/course/format/amd/build/local/courseeditor/mutations.min.js @@ -1,2 +1,2 @@ -define ("core_courseformat/local/courseeditor/mutations",["exports","core/ajax"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);function c(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function d(a){return function(){var b=this,d=arguments;return new Promise(function(e,f){var i=a.apply(b,d);function g(a){c(i,e,f,g,h,"next",a)}function h(a){c(i,e,f,g,h,"throw",a)}g(void 0)})}}function e(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function f(a,b){for(var c=0,d;c.\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 */\n async _callEditWebservice(action, courseId, ids) {\n let ajaxresult = await ajax.call([{\n methodname: 'core_courseformat_update_course',\n args: {\n action,\n courseid: courseId,\n ids,\n }\n }])[0];\n return JSON.parse(ajaxresult);\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 const course = stateManager.get('course');\n const updates = await this._callEditWebservice('cm_state', course.id, cmids);\n stateManager.processUpdates(updates);\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 const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_state', course.id, sectionIds);\n stateManager.processUpdates(updates);\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"],"file":"mutations.min.js"} \ No newline at end of file +{"version":3,"sources":["../../../src/local/courseeditor/mutations.js"],"names":["action","courseId","ids","targetSectionId","targetCmId","args","courseid","targetsectionid","targetcmid","ajax","call","methodname","ajaxresult","JSON","parse","stateManager","cmids","Error","course","get","_callEditWebservice","id","updates","processUpdates","sectionIds"],"mappings":"8KAeA,uD,owBAwB8BA,C,CAAQC,C,CAAUC,C,CAAKC,C,CAAiBC,C,2FACxDC,C,CAAO,CACTL,MAAM,CAANA,CADS,CAETM,QAAQ,CAAEL,CAFD,CAGTC,GAAG,CAAHA,CAHS,C,CAKb,GAAIC,CAAJ,CAAqB,CACjBE,CAAI,CAACE,eAAL,CAAuBJ,CAC1B,CACD,GAAIC,CAAJ,CAAgB,CACZC,CAAI,CAACG,UAAL,CAAkBJ,CACrB,C,eACsBK,WAAKC,IAAL,CAAU,CAAC,CAC9BC,UAAU,CAAE,iCADkB,CAE9BN,IAAI,CAAJA,CAF8B,CAAD,CAAV,EAGnB,CAHmB,C,QAAnBO,C,iCAIGC,IAAI,CAACC,KAAL,CAAWF,CAAX,C,iMAkBEG,C,CAAcC,C,CAAOb,C,CAAiBC,C,gGAC3C,CAACD,CAAD,EAAoB,CAACC,C,uBACf,IAAIa,CAAAA,KAAJ,0D,QAEJC,C,CAASH,CAAY,CAACI,GAAb,CAAiB,QAAjB,C,gBACO,MAAKC,mBAAL,CAAyB,SAAzB,CAAoCF,CAAM,CAACG,EAA3C,CAA+CL,CAA/C,CAAsDb,CAAtD,CAAuEC,CAAvE,C,QAAhBkB,C,QACNP,CAAY,CAACQ,cAAb,CAA4BD,CAA5B,E,4LAUcP,C,CAAcS,C,CAAYrB,C,8FACnCA,C,sBACK,IAAIc,CAAAA,KAAJ,iD,QAEJC,C,CAASH,CAAY,CAACI,GAAb,CAAiB,QAAjB,C,gBACO,MAAKC,mBAAL,CAAyB,cAAzB,CAAyCF,CAAM,CAACG,EAAhD,CAAoDG,CAApD,CAAgErB,CAAhE,C,QAAhBmB,C,QACNP,CAAY,CAACQ,cAAb,CAA4BD,CAA5B,E,6LAUUP,C,CAAcC,C,2FAClBE,C,CAASH,CAAY,CAACI,GAAb,CAAiB,QAAjB,C,gBACO,MAAKC,mBAAL,CAAyB,UAAzB,CAAqCF,CAAM,CAACG,EAA5C,CAAgDL,CAAhD,C,QAAhBM,C,QACNP,CAAY,CAACQ,cAAb,CAA4BD,CAA5B,E,8LAUeP,C,CAAcS,C,2FACvBN,C,CAASH,CAAY,CAACI,GAAb,CAAiB,QAAjB,C,gBACO,MAAKC,mBAAL,CAAyB,eAAzB,CAA0CF,CAAM,CAACG,EAAjD,CAAqDG,CAArD,C,QAAhBF,C,QACNP,CAAY,CAACQ,cAAb,CAA4BD,CAA5B,E,kMAQcP,C,2FACRG,C,CAASH,CAAY,CAACI,GAAb,CAAiB,QAAjB,C,gBACO,MAAKC,mBAAL,CAAyB,cAAzB,CAAyCF,CAAM,CAACG,EAAhD,C,QAAhBC,C,QACNP,CAAY,CAACQ,cAAb,CAA4BD,CAA5B,E","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 * 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 const updates = await this._callEditWebservice('cm_move', course.id, cmids, targetSectionId, targetCmId);\n stateManager.processUpdates(updates);\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 const updates = await this._callEditWebservice('section_move', course.id, sectionIds, targetSectionId);\n stateManager.processUpdates(updates);\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 const course = stateManager.get('course');\n const updates = await this._callEditWebservice('cm_state', course.id, cmids);\n stateManager.processUpdates(updates);\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 const course = stateManager.get('course');\n const updates = await this._callEditWebservice('section_state', course.id, sectionIds);\n stateManager.processUpdates(updates);\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"],"file":"mutations.min.js"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseindex/cm.min.js b/course/format/amd/build/local/courseindex/cm.min.js index 9c94cb35821..3e0d337a941 100644 --- a/course/format/amd/build/local/courseindex/cm.min.js +++ b/course/format/amd/build/local/courseindex/cm.min.js @@ -1,2 +1,2 @@ -define ("core_courseformat/local/courseindex/cm",["exports","core/reactive"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;function c(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){c=function(a){return typeof a}}else{c=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return c(a)}function d(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function e(a,b){for(var c=0,d;c.\n\n/**\n * Course index cm component.\n *\n * This component is used to control specific course modules interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/cm\n * @class core_courseformat/local/courseindex/cm\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';\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'courseindex_cm';\n // Default query selectors.\n this.selectors = {\n };\n // We need our id to watch specific events.\n this.id = this.element.dataset.id;\n }\n\n /**\n * Static method to create a component instance form the mustache template.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new Component({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n */\n stateReady() {\n // Activate drag and drop soon.\n }\n\n getWatchers() {\n return [\n {watch: `cm[${this.id}]:deleted`, handler: this.remove},\n ];\n }\n\n}\n"],"file":"cm.min.js"} \ No newline at end of file +{"version":3,"sources":["../../../src/local/courseindex/cm.js"],"names":["Component","name","id","element","dataset","configDragDrop","watch","handler","remove","target","selectors","document","getElementById","DndCmItem"],"mappings":"2MA0BA,uD,+nDAEqBA,CAAAA,C,+HAKR,CAEL,KAAKC,IAAL,CAAY,gBAAZ,CAEA,KAAKC,EAAL,CAAU,KAAKC,OAAL,CAAaC,OAAb,CAAqBF,EAClC,C,+CAmBY,CACT,KAAKG,cAAL,CAAoB,KAAKH,EAAzB,CACH,C,iDAOa,CACV,MAAO,CACH,CAACI,KAAK,cAAQ,KAAKJ,EAAb,aAAN,CAAkCK,OAAO,CAAE,KAAKC,MAAhD,CADG,CAGV,C,oCAvBWC,C,CAAQC,C,CAAW,CAC3B,MAAO,IAAIV,CAAAA,CAAJ,CAAc,CACjBG,OAAO,CAAEQ,QAAQ,CAACC,cAAT,CAAwBH,CAAxB,CADQ,CAEjBC,SAAS,CAATA,CAFiB,CAAd,CAIV,C,cAxBkCG,S","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 cm component.\n *\n * This component is used to control specific course modules interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/cm\n * @class core_courseformat/local/courseindex/cm\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DndCmItem from 'core_courseformat/local/courseeditor/dndcmitem';\n\nexport default class Component extends DndCmItem {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'courseindex_cm';\n // We need our id to watch specific events.\n this.id = this.element.dataset.id;\n }\n\n /**\n * Static method to create a component instance form the mustache template.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new Component({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n */\n stateReady() {\n this.configDragDrop(this.id);\n }\n\n /**\n * Component watchers.\n *\n * @returns {Array} of watchers\n */\n getWatchers() {\n return [\n {watch: `cm[${this.id}]:deleted`, handler: this.remove},\n ];\n }\n\n}\n"],"file":"cm.min.js"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseindex/section.min.js b/course/format/amd/build/local/courseindex/section.min.js new file mode 100644 index 00000000000..6f408af9f75 --- /dev/null +++ b/course/format/amd/build/local/courseindex/section.min.js @@ -0,0 +1,2 @@ +define ("core_courseformat/local/courseindex/section",["exports","core_courseformat/local/courseindex/sectiontitle","core_courseformat/local/courseeditor/dndsection"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=d(b);c=d(c);function d(a){return a&&a.__esModule?a:{default:a}}function e(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){e=function(a){return typeof a}}else{e=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return e(a)}function f(a,b){var c=Object.keys(a);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(a);if(b)d=d.filter(function(b){return Object.getOwnPropertyDescriptor(a,b).enumerable});c.push.apply(c,d)}return c}function g(a){for(var b=1,c;b.\n\n/**\n * Course index section component.\n *\n * This component is used to control specific course section interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/section\n * @class core_courseformat/local/courseindex/section\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport SectionTitle from 'core_courseformat/local/courseindex/sectiontitle';\nimport DndSection from 'core_courseformat/local/courseeditor/dndsection';\n\nexport default class Component extends DndSection {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'courseindex_section';\n // Default query selectors.\n this.selectors = {\n SECTION_ITEM: `[data-for='section_item']`,\n CM_LAST: `[data-for=\"cm\"]:last-child`,\n };\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 * @return {Component}\n */\n static init(target, selectors) {\n return new Component({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the initial state\n */\n stateReady(state) {\n this.configState(state);\n // Drag and drop is only available for components compatible course formats.\n if (this.reactive.isEditing && this.reactive.supportComponents) {\n // Init the inner dragable element passing the full section as affected region.\n const titleitem = new SectionTitle({\n ...this,\n element: this.getElement(this.selectors.SECTION_ITEM),\n fullregion: this.element,\n });\n this.configDragDrop(titleitem);\n }\n }\n\n /**\n * Get the last CM element of that section.\n *\n * @returns {element|null}\n */\n getLastCm() {\n return this.getElement(this.selectors.CM_LAST);\n }\n}\n"],"file":"section.min.js"} \ No newline at end of file diff --git a/course/format/amd/build/local/courseindex/sectiontitle.min.js b/course/format/amd/build/local/courseindex/sectiontitle.min.js new file mode 100644 index 00000000000..f63442fa0a5 --- /dev/null +++ b/course/format/amd/build/local/courseindex/sectiontitle.min.js @@ -0,0 +1,2 @@ +define ("core_courseformat/local/courseindex/sectiontitle",["exports","core_courseformat/local/courseeditor/dndsectionitem"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);function c(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){c=function(a){return typeof a}}else{c=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return c(a)}function d(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function e(a,b){for(var c=0,d;c.\n\n/**\n * Course index section title component.\n *\n * This component is used to control specific course section interactions like drag and drop.\n *\n * @module core_courseformat/local/courseindex/sectiontitle\n * @class core_courseformat/local/courseindex/sectiontitle\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport DndSectionItem from 'core_courseformat/local/courseeditor/dndsectionitem';\n\nexport default class Component extends DndSectionItem {\n\n /**\n * Constructor hook.\n *\n * @param {Object} descriptor\n */\n create(descriptor) {\n // Optional component name for debugging.\n this.name = 'courseindex_sectiontitle';\n\n this.id = descriptor.id;\n this.section = descriptor.section;\n this.course = descriptor.course;\n this.fullregion = descriptor.fullregion;\n\n // Prevent topic zero from being draggable.\n if (this.section.number > 0) {\n this.getDraggableData = this._getDraggableData;\n }\n }\n\n /**\n * Static method to create a component instance form the mustahce template.\n *\n * @param {element|string} target the DOM main element or its ID\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n return new Component({\n element: document.getElementById(target),\n selectors,\n });\n }\n\n /**\n * Initial state ready method.\n *\n * @param {Object} state the initial state\n */\n stateReady(state) {\n this.configDragDrop(this.id, state, this.fullregion);\n }\n}\n"],"file":"sectiontitle.min.js"} \ No newline at end of file diff --git a/course/format/amd/src/courseeditor.js b/course/format/amd/src/courseeditor.js index a17a5f91a96..056434e0bb6 100644 --- a/course/format/amd/src/courseeditor.js +++ b/course/format/amd/src/courseeditor.js @@ -54,6 +54,7 @@ function dispatchStateChangedEvent(detail, target) { * @param {number} courseId the course id * @param {setup} setup format, page and course settings * @property {boolean} setup.editing if the page is in edit mode + * @property {boolean} setup.supportscomponents if the format supports components for content */ export const setViewFormat = (courseId, setup) => { const editor = getCourseEditor(courseId); diff --git a/course/format/amd/src/local/content.js b/course/format/amd/src/local/content.js new file mode 100644 index 00000000000..1a251504ad3 --- /dev/null +++ b/course/format/amd/src/local/content.js @@ -0,0 +1,230 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Course index main component. + * + * @module core_courseformat/local/content + * @class core_courseformat/local/content + * @copyright 2020 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import {BaseComponent} from 'core/reactive'; +import {getCurrentCourseEditor} from 'core_courseformat/courseeditor'; +import inplaceeditable from 'core/inplace_editable'; +// Course actions is needed for actions that are not migrated to components. +import courseActions from 'core_course/actions'; + +export default class Component extends BaseComponent { + + /** + * Constructor hook. + */ + create() { + // Optional component name for debugging. + this.name = 'course_format'; + // Default query selectors. + this.selectors = { + SECTION: `[data-for='section']`, + SECTION_ITEM: `[data-for='section_item']`, + SECTION_TITLE: `[data-for='section_title']`, + SECTION_CMLIST: `[data-for='cmlist']`, + COURSE_SECTIONLIST: `[data-for='course_sectionlist']`, + CM: `[data-for='cmitem']`, + }; + // Array to save dettached elements during element resorting. + this.dettachedCms = {}; + this.dettachedSections = {}; + } + + /** + * Static method to create a component instance form the mustahce template. + * + * @param {string} target the DOM main element or its ID + * @param {object} selectors optional css selector overrides + * @return {Component} + */ + static init(target, selectors) { + return new Component({ + element: document.getElementById(target), + reactive: getCurrentCourseEditor(), + selectors, + }); + } + + /** + * Return the component watchers. + * + * @returns {Array} of watchers + */ + getWatchers() { + // Check if the course format is compatible with reactive components. + if (!this.reactive.supportComponents) { + return []; + } + return [ + // State changes that require to reload some course modules. + {watch: `cm.visible:updated`, handler: this._reloadCm}, + // Update section number and title. + {watch: `section.number:updated`, handler: this._refreshSectionNumber}, + // Sections and cm sorting. + {watch: `transaction:start`, handler: this._startProcessing}, + {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionlist}, + {watch: `section.cmlist:updated`, handler: this._refreshSectionCmlist}, + ]; + } + + /** + * Reload a course module. + * + * Most course module HTML is still strongly backend dependant. + * Some changes require to get a new version af the module. + * + * @param {Object} update the state update data + */ + _reloadCm({element}) { + const cmitem = this.getElement(this.selectors.CM, element.id); + if (cmitem) { + courseActions.refreshModule(cmitem, element.id); + } + } + + /** + * Setup the component to start a transaction. + * + * Some of the course actions replaces the current DOM element with a new one before updating the + * course state. This means the component cannot preload any index properly until the transaction starts. + * + */ + _startProcessing() { + // During a section or cm sorting, some elements could be dettached from the DOM and we + // need to store somewhare in case they are needed later. + this.dettachedCms = {}; + this.dettachedSections = {}; + } + + /** + * Update a course section when the section number changes. + * + * The courseActions module used for most course section tools still depends on css classes and + * section numbers (not id). To prevent inconsistencies when a section is moved, we need to refresh + * the + * + * Course formats can override the section title rendering so the frontend depends heavily on backend + * rendering. Luckily in edit mode we can trigger a title update using the inplace_editable module. + * + * @param {Object} details the update details. + */ + _refreshSectionNumber({element}) { + // Find the element. + const target = this.getElement(this.selectors.SECTION, element.id); + if (!target) { + throw new Error(`Unkown section with ID ${element.id}`); + } + // Update section numbers in all data, css and YUI attributes. + target.id = `section-${element.number}`; + // YUI uses section number as section id in data-sectionid, in principle if a format use components + // don't need this sectionid attribute anymore, but we keep the compatibility in case some plugin + // use it for legacy purposes. + target.dataset.sectionid = element.number; + // The data-number is the attribute used by components to store the section number. + target.dataset.number = element.number; + + // Update title and title inplace editable, if any. + const inplace = inplaceeditable.getInplaceEditable(target.querySelector(this.selectors.SECTION_TITLE)); + if (inplace) { + // The course content HTML can be modified at any moment, so the function need to do some checkings + // to make sure the inplace editable still represents the same itemid. + const currentvalue = inplace.getValue(); + const currentitemid = inplace.getItemId(); + // Unnamed sections must be recalculated. + if (inplace.getValue() === '') { + // The value to send can be an empty value if it is a default name. + if (currentitemid == element.id && (currentvalue != element.rawtitle || element.rawtitle == '')) { + inplace.setValue(element.rawtitle); + } + } + } + } + + /** + * Refresh a section cm list. + * + * @param {Object} details the update details. + */ + _refreshSectionCmlist({element}) { + const cmlist = element.cmlist ?? []; + const section = this.getElement(this.selectors.SECTION, element.id); + const listparent = section?.querySelector(this.selectors.SECTION_CMLIST); + if (listparent) { + this._fixOrder(listparent, cmlist, this.selectors.CM, this.dettachedCms); + } + } + + /** + * Refresh the section list. + * + * @param {Object} details the update details. + */ + _refreshCourseSectionlist({element}) { + const sectionlist = element.sectionlist ?? []; + const listparent = this.getElement(this.selectors.COURSE_SECTIONLIST); + if (listparent) { + this._fixOrder(listparent, sectionlist, this.selectors.SECTION, this.dettachedSections); + } + } + + /** + * Fix/reorder the section or cms order. + * + * @param {Element} container the HTML element to reorder. + * @param {Array} neworder an array with the ids order + * @param {string} selector the element selector + * @param {Object} dettachedelements a list of dettached elements + */ + _fixOrder(container, neworder, selector, dettachedelements) { + + // Empty lists should not be visible. + if (!neworder.length) { + container.classList.add('hidden'); + container.innerHTML = ''; + return; + } + + // Grant the list is visible (in case it was empty). + container.classList.remove('hidden'); + + // Move the elements in order at the beginning of the list. + neworder.forEach((itemid, index) => { + const item = this.getElement(selector, itemid) ?? dettachedelements[itemid]; + // Get the current elemnt at that position. + const currentitem = container.children[index]; + if (currentitem === undefined) { + container.append(item); + return; + } + if (currentitem !== item) { + container.insertBefore(item, currentitem); + } + }); + // Remove the remaining elements. + while (container.children.length > neworder.length) { + const lastchild = container.lastChild; + dettachedelements[lastchild?.dataset?.id ?? 0] = lastchild; + container.removeChild(lastchild); + } + } +} diff --git a/course/format/amd/src/local/courseeditor/courseeditor.js b/course/format/amd/src/local/courseeditor/courseeditor.js index 557841715de..c8f44c6fcc1 100644 --- a/course/format/amd/src/local/courseeditor/courseeditor.js +++ b/course/format/amd/src/local/courseeditor/courseeditor.js @@ -47,6 +47,7 @@ export default class extends Reactive { // Default view format setup. this._editing = false; + this._supportscomponents = false; this.courseId = courseId; @@ -68,9 +69,11 @@ export default class extends Reactive { * * @param {Object} setup format, page and course settings * @property {boolean} setup.editing if the page is in edit mode + * @property {boolean} setup.supportscomponents if the format supports components for content */ setViewFormat(setup) { this._editing = setup.editing ?? false; + this._supportscomponents = setup.supportscomponents ?? false; } /** @@ -116,6 +119,15 @@ export default class extends Reactive { return new Exporter(this); } + /** + * Return if the current course support components to refresh the content. + * + * @returns {boolean} if the current content support components + */ + get supportComponents() { + return this._supportscomponents ?? false; + } + /** * Dispatch a change in the state. * diff --git a/course/format/amd/src/local/courseeditor/dndcmitem.js b/course/format/amd/src/local/courseeditor/dndcmitem.js new file mode 100644 index 00000000000..19e3aef0ca4 --- /dev/null +++ b/course/format/amd/src/local/courseeditor/dndcmitem.js @@ -0,0 +1,113 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Course index cm component. + * + * This component is used to control specific course modules interactions like drag and drop + * in both course index and course content. + * + * @module core_courseformat/local/courseeditor/dndcmitem + * @class core_courseformat/local/courseeditor/dndcmitem + * @copyright 2021 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import {BaseComponent, DragDrop} from 'core/reactive'; + +export default class extends BaseComponent { + + /** + * Configure the component drag and drop. + * + * @param {number} cmid course module id + */ + configDragDrop(cmid) { + + this.id = cmid; + + // Drag and drop is only available for components compatible course formats. + if (this.reactive.isEditing && this.reactive.supportComponents) { + // Init element drag and drop. + this.dragdrop = new DragDrop(this); + // Save dropzone classes. + this.classes = this.dragdrop.getClasses(); + } + } + + /** + * Remove all subcomponents dependencies. + */ + destroy() { + if (this.dragdrop !== undefined) { + this.dragdrop.unregister(); + } + } + + // Drag and drop methods. + + /** + * Get the draggable data of this component. + * + * @returns {Object} exported course module drop data + */ + getDraggableData() { + const exporter = this.reactive.getExporter(); + return exporter.cmDraggableData(this.reactive.state, this.id); + } + + /** + * Validate if the drop data can be dropped over the component. + * + * @param {Object} dropdata the exported drop data. + * @returns {boolean} + */ + validateDropData(dropdata) { + return dropdata?.type === 'cm'; + } + + /** + * Display the component dropzone. + * + * @param {Object} dropdata the accepted drop data + */ + showDropZone(dropdata) { + // If we are the next cmid of the dragged element we accept the drop because otherwise it + // will get captured by the section. However, we won't trigger any mutation. + if (dropdata.nextcmid != this.id && dropdata.id != this.id) { + this.element.classList.add(this.classes.DROPUP); + } + } + + /** + * Hide the component dropzone. + */ + hideDropZone() { + this.element.classList.remove(this.classes.DROPUP); + } + + /** + * Drop event handler. + * + * @param {Object} dropdata the accepted drop data + */ + drop(dropdata) { + // Call the move mutation if necessary. + if (dropdata.id != this.id && dropdata.nextcmid != this.id) { + this.reactive.dispatch('cmMove', [dropdata.id], null, this.id); + } + } + +} diff --git a/course/format/amd/src/local/courseeditor/dndsection.js b/course/format/amd/src/local/courseeditor/dndsection.js new file mode 100644 index 00000000000..9d6568f8989 --- /dev/null +++ b/course/format/amd/src/local/courseeditor/dndsection.js @@ -0,0 +1,146 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Course index section component. + * + * This component is used to control specific course section interactions like drag and drop + * in both course index and course content. + * + * @module core_courseformat/local/courseeditor/dndsection + * @class core_courseformat/local/courseeditor/dndsection + * @copyright 2021 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import {BaseComponent, DragDrop} from 'core/reactive'; + +export default class extends BaseComponent { + + /** + * Save some values form the state. + * + * @param {Object} state the current state + */ + configState(state) { + this.id = this.element.dataset.id; + this.section = state.section.get(this.id); + this.course = state.course; + } + + /** + * Register state values and the drag and drop subcomponent. + * + * @param {BaseComponent} sectionitem section item component + */ + configDragDrop(sectionitem) { + // Drag and drop is only available for components compatible course formats. + if (this.reactive.isEditing && this.reactive.supportComponents) { + // Init the inner dragable element. + this.sectionitem = sectionitem; + // Init the dropzone. + this.dragdrop = new DragDrop(this); + // Save dropzone classes. + this.classes = this.dragdrop.getClasses(); + } + } + + /** + * Remove all subcomponents dependencies. + */ + destroy() { + if (this.sectionitem !== undefined) { + this.sectionitem.unregister(); + } + if (this.dragdrop !== undefined) { + this.dragdrop.unregister(); + } + } + + /** + * Get the last CM element of that section. + * + * @returns {element|null} the las course module element of the section. + */ + getLastCm() { + return null; + } + + // Drag and drop methods. + + /** + * Validate if the drop data can be dropped over the component. + * + * @param {Object} dropdata the exported drop data. + * @returns {boolean} + */ + validateDropData(dropdata) { + // We accept any course module. + if (dropdata?.type === 'cm') { + return true; + } + // We accept any section bu the section 0 or ourself + if (dropdata?.type === 'section') { + const sectionzeroid = this.course.sectionlist[0]; + return dropdata?.id != this.id && dropdata?.id != sectionzeroid && this.id != sectionzeroid; + } + return false; + } + + /** + * Display the component dropzone. + * + * @param {Object} dropdata the accepted drop data + */ + showDropZone(dropdata) { + if (dropdata.type == 'cm') { + this.getLastCm()?.classList.add(this.classes.DROPDOWN); + } + if (dropdata.type == 'section') { + // The relative move of section depends on the section number. + if (this.section.number > dropdata.number) { + this.element.classList.remove(this.classes.DROPUP); + this.element.classList.add(this.classes.DROPDOWN); + } else { + this.element.classList.add(this.classes.DROPUP); + this.element.classList.remove(this.classes.DROPDOWN); + } + } + } + + /** + * Hide the component dropzone. + */ + hideDropZone() { + this.getLastCm()?.classList.remove(this.classes.DROPDOWN); + this.element.classList.remove(this.classes.DROPUP); + this.element.classList.remove(this.classes.DROPDOWN); + } + + /** + * Drop event handler. + * + * @param {Object} dropdata the accepted drop data + */ + drop(dropdata) { + // Call the move mutation. + if (dropdata.type == 'cm') { + this.reactive.dispatch('cmMove', [dropdata.id], this.id); + } + if (dropdata.type == 'section') { + this.reactive.dispatch('sectionMove', [dropdata.id], this.id); + } + } +} diff --git a/course/format/amd/src/local/courseeditor/dndsectionitem.js b/course/format/amd/src/local/courseeditor/dndsectionitem.js new file mode 100644 index 00000000000..850c6e5c93a --- /dev/null +++ b/course/format/amd/src/local/courseeditor/dndsectionitem.js @@ -0,0 +1,129 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Course index section title draggable component. + * + * This component is used to control specific course section interactions like drag and drop + * in both course index and course content. + * + * @module core_courseformat/local/courseeditor/dndsectionitem + * @class core_courseformat/local/courseeditor/dndsectionitem + * @copyright 2021 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import {BaseComponent, DragDrop} from 'core/reactive'; + +export default class extends BaseComponent { + + /** + * Initial state ready method. + * + * @param {number} sectionid the section id + * @param {Object} state the initial state + * @param {Element} fullregion the complete section region to mark as dragged + */ + configDragDrop(sectionid, state, fullregion) { + + this.id = sectionid; + if (this.section === undefined) { + this.section = state.section.get(this.id); + } + if (this.course === undefined) { + this.course = state.course; + } + + // Prevent topic zero from being draggable. + if (this.section.number > 0) { + this.getDraggableData = this._getDraggableData; + } + + this.fullregion = fullregion; + + // Drag and drop is only available for components compatible course formats. + if (this.reactive.isEditing && this.reactive.supportComponents) { + // Init the dropzone. + this.dragdrop = new DragDrop(this); + // Save dropzone classes. + this.classes = this.dragdrop.getClasses(); + } + } + + /** + * Remove all subcomponents dependencies. + */ + destroy() { + if (this.dragdrop !== undefined) { + this.dragdrop.unregister(); + } + } + + // Drag and drop methods. + + /** + * Get the draggable data of this component. + * + * @returns {Object} exported course module drop data + */ + _getDraggableData() { + const exporter = this.reactive.getExporter(); + return exporter.sectionDraggableData(this.reactive.state, this.id); + } + + /** + * Validate if the drop data can be dropped over the component. + * + * @param {Object} dropdata the exported drop data. + * @returns {boolean} + */ + validateDropData(dropdata) { + // Course module validation. + if (dropdata?.type === 'cm') { + // The first section element is already there so we can ignore it. + const firstcmid = this.section?.cmlist[0]; + return dropdata.id !== firstcmid; + } + return false; + } + + /** + * Display the component dropzone. + * + * @param {Object} dropdata the accepted drop data + */ + showDropZone() { + this.element.classList.add(this.classes.DROPZONE); + } + + /** + * Hide the component dropzone. + */ + hideDropZone() { + this.element.classList.remove(this.classes.DROPZONE); + } + + /** + * Drop event handler. + * + * @param {Object} dropdata the accepted drop data + */ + drop(dropdata) { + // Call the move mutation. + if (dropdata.type == 'cm') { + this.reactive.dispatch('cmMove', [dropdata.id], this.id, this.section?.cmlist[0]); + } + } +} diff --git a/course/format/amd/src/local/courseeditor/exporter.js b/course/format/amd/src/local/courseeditor/exporter.js index 80b9ebf18b4..e54fe439f55 100644 --- a/course/format/amd/src/local/courseeditor/exporter.js +++ b/course/format/amd/src/local/courseeditor/exporter.js @@ -94,4 +94,59 @@ export default class { }; return cm; } + + /** + * Generate a dragable cm data structure. + * + * This method is used by any draggable course module element to generate drop data + * for its reactive/dragdrop instance. + * + * @param {*} state the state object + * @param {*} cmid the cours emodule id + * @returns {Object|null} + */ + cmDraggableData(state, cmid) { + const cminfo = state.cm.get(cmid); + if (!cminfo) { + return null; + } + + // Drop an activity over the next activity is the same as doing anything. + let nextcmid; + const section = state.section.get(cminfo.sectionid); + const currentindex = section?.cmlist.indexOf(cminfo.id); + if (currentindex !== undefined) { + nextcmid = section?.cmlist[currentindex + 1]; + } + + return { + type: 'cm', + id: cminfo.id, + name: cminfo.name, + nextcmid, + }; + } + + /** + * Generate a dragable cm data structure. + * + * This method is used by any draggable section element to generate drop data + * for its reactive/dragdrop instance. + * + * @param {*} state the state object + * @param {*} sectionid the cours section id + * @returns {Object|null} + */ + sectionDraggableData(state, sectionid) { + const sectioninfo = state.section.get(sectionid); + if (!sectioninfo) { + return null; + } + return { + type: 'section', + id: sectioninfo.id, + name: sectioninfo.name, + number: sectioninfo.number, + }; + } } diff --git a/course/format/amd/src/local/courseeditor/mutations.js b/course/format/amd/src/local/courseeditor/mutations.js index 23d3e332730..6b431d6e126 100644 --- a/course/format/amd/src/local/courseeditor/mutations.js +++ b/course/format/amd/src/local/courseeditor/mutations.js @@ -34,19 +34,68 @@ export default class { * @param {string} action * @param {number} courseId * @param {array} ids + * @param {number} targetSectionId optional target section id (for moving actions) + * @param {number} targetCmId optional target cm id (for moving actions) */ - async _callEditWebservice(action, courseId, ids) { + async _callEditWebservice(action, courseId, ids, targetSectionId, targetCmId) { + const args = { + action, + courseid: courseId, + ids, + }; + if (targetSectionId) { + args.targetsectionid = targetSectionId; + } + if (targetCmId) { + args.targetcmid = targetCmId; + } let ajaxresult = await ajax.call([{ methodname: 'core_courseformat_update_course', - args: { - action, - courseid: courseId, - ids, - } + args, }])[0]; return JSON.parse(ajaxresult); } + /** + * Move course modules to specific course location. + * + * Note that one of targetSectionId or targetCmId should be provided in order to identify the + * new location: + * - targetCmId: the activities will be located avobe the target cm. The targetSectionId + * value will be ignored in this case. + * - targetSectionId: the activities will be appended to the section. In this case + * targetSectionId should not be present. + * + * @param {StateManager} stateManager the current state manager + * @param {array} cmids the list of cm ids to move + * @param {number} targetSectionId the target section id + * @param {number} targetCmId the target course module id + */ + async cmMove(stateManager, cmids, targetSectionId, targetCmId) { + if (!targetSectionId && !targetCmId) { + throw new Error(`Mutation cmMove requires targetSectionId or targetCmId`); + } + const course = stateManager.get('course'); + const updates = await this._callEditWebservice('cm_move', course.id, cmids, targetSectionId, targetCmId); + stateManager.processUpdates(updates); + } + + /** + * Move course modules to specific course location. + * + * @param {StateManager} stateManager the current state manager + * @param {array} sectionIds the list of section ids to move + * @param {number} targetSectionId the target section id + */ + async sectionMove(stateManager, sectionIds, targetSectionId) { + if (!targetSectionId) { + throw new Error(`Mutation sectionMove requires targetSectionId`); + } + const course = stateManager.get('course'); + const updates = await this._callEditWebservice('section_move', course.id, sectionIds, targetSectionId); + stateManager.processUpdates(updates); + } + /** * Get updated state data related to some cm ids. * diff --git a/course/format/amd/src/local/courseindex/cm.js b/course/format/amd/src/local/courseindex/cm.js index b264bcbeded..f0e8b8cf5fb 100644 --- a/course/format/amd/src/local/courseindex/cm.js +++ b/course/format/amd/src/local/courseindex/cm.js @@ -24,9 +24,9 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -import {BaseComponent} from 'core/reactive'; +import DndCmItem from 'core_courseformat/local/courseeditor/dndcmitem'; -export default class Component extends BaseComponent { +export default class Component extends DndCmItem { /** * Constructor hook. @@ -34,9 +34,6 @@ export default class Component extends BaseComponent { create() { // Optional component name for debugging. this.name = 'courseindex_cm'; - // Default query selectors. - this.selectors = { - }; // We need our id to watch specific events. this.id = this.element.dataset.id; } @@ -59,9 +56,14 @@ export default class Component extends BaseComponent { * Initial state ready method. */ stateReady() { - // Activate drag and drop soon. + this.configDragDrop(this.id); } + /** + * Component watchers. + * + * @returns {Array} of watchers + */ getWatchers() { return [ {watch: `cm[${this.id}]:deleted`, handler: this.remove}, diff --git a/course/format/amd/src/local/courseindex/section.js b/course/format/amd/src/local/courseindex/section.js new file mode 100644 index 00000000000..98b1a48205f --- /dev/null +++ b/course/format/amd/src/local/courseindex/section.js @@ -0,0 +1,86 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Course index section component. + * + * This component is used to control specific course section interactions like drag and drop. + * + * @module core_courseformat/local/courseindex/section + * @class core_courseformat/local/courseindex/section + * @copyright 2021 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import SectionTitle from 'core_courseformat/local/courseindex/sectiontitle'; +import DndSection from 'core_courseformat/local/courseeditor/dndsection'; + +export default class Component extends DndSection { + + /** + * Constructor hook. + */ + create() { + // Optional component name for debugging. + this.name = 'courseindex_section'; + // Default query selectors. + this.selectors = { + SECTION_ITEM: `[data-for='section_item']`, + CM_LAST: `[data-for="cm"]:last-child`, + }; + } + + /** + * Static method to create a component instance form the mustahce template. + * + * @param {string} target the DOM main element or its ID + * @param {object} selectors optional css selector overrides + * @return {Component} + */ + static init(target, selectors) { + return new Component({ + element: document.getElementById(target), + selectors, + }); + } + + /** + * Initial state ready method. + * + * @param {Object} state the initial state + */ + stateReady(state) { + this.configState(state); + // Drag and drop is only available for components compatible course formats. + if (this.reactive.isEditing && this.reactive.supportComponents) { + // Init the inner dragable element passing the full section as affected region. + const titleitem = new SectionTitle({ + ...this, + element: this.getElement(this.selectors.SECTION_ITEM), + fullregion: this.element, + }); + this.configDragDrop(titleitem); + } + } + + /** + * Get the last CM element of that section. + * + * @returns {element|null} + */ + getLastCm() { + return this.getElement(this.selectors.CM_LAST); + } +} diff --git a/course/format/amd/src/local/courseindex/sectiontitle.js b/course/format/amd/src/local/courseindex/sectiontitle.js new file mode 100644 index 00000000000..12d05eeddd2 --- /dev/null +++ b/course/format/amd/src/local/courseindex/sectiontitle.js @@ -0,0 +1,73 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Course index section title component. + * + * This component is used to control specific course section interactions like drag and drop. + * + * @module core_courseformat/local/courseindex/sectiontitle + * @class core_courseformat/local/courseindex/sectiontitle + * @copyright 2021 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import DndSectionItem from 'core_courseformat/local/courseeditor/dndsectionitem'; + +export default class Component extends DndSectionItem { + + /** + * Constructor hook. + * + * @param {Object} descriptor + */ + create(descriptor) { + // Optional component name for debugging. + this.name = 'courseindex_sectiontitle'; + + this.id = descriptor.id; + this.section = descriptor.section; + this.course = descriptor.course; + this.fullregion = descriptor.fullregion; + + // Prevent topic zero from being draggable. + if (this.section.number > 0) { + this.getDraggableData = this._getDraggableData; + } + } + + /** + * Static method to create a component instance form the mustahce template. + * + * @param {element|string} target the DOM main element or its ID + * @param {object} selectors optional css selector overrides + * @return {Component} + */ + static init(target, selectors) { + return new Component({ + element: document.getElementById(target), + selectors, + }); + } + + /** + * Initial state ready method. + * + * @param {Object} state the initial state + */ + stateReady(state) { + this.configDragDrop(this.id, state, this.fullregion); + } +} diff --git a/course/format/classes/base.php b/course/format/classes/base.php index 333edd754e7..dc8f5847658 100644 --- a/course/format/classes/base.php +++ b/course/format/classes/base.php @@ -494,6 +494,21 @@ abstract class base { return $ajaxsupport; } + /** + * Returns true if this course format is compatible with content components. + * + * Using components means the content elements can watch the frontend course state and + * react to the changes. Formats with component compatibility can have more interactions + * without refreshing the page, like having drag and drop from the course index to reorder + * sections and activities. + * + * @return bool if the format is compatible with components. + */ + public function supports_components() { + return false; + } + + /** * Custom action after section has been moved in AJAX mode * diff --git a/course/format/classes/output/local/state/section.php b/course/format/classes/output/local/state/section.php index b56671aca0f..4c3f8cc9eb3 100644 --- a/course/format/classes/output/local/state/section.php +++ b/course/format/classes/output/local/state/section.php @@ -64,6 +64,7 @@ class section implements renderable { 'section' => $section->section, 'number' => $section->section, 'title' => $format->get_section_name($section), + 'rawtitle' => $section->name, 'cmlist' => [], 'visible' => !empty($section->visible), 'sectionurl' => course_get_url($course, $section->section)->out(), diff --git a/course/format/classes/stateactions.php b/course/format/classes/stateactions.php index f37bf700702..c42d463ac62 100644 --- a/course/format/classes/stateactions.php +++ b/course/format/classes/stateactions.php @@ -17,9 +17,13 @@ namespace core_courseformat; use core_courseformat\stateupdates; +use cm_info; +use section_info; use stdClass; use course_modinfo; use moodle_exception; +use context_module; +use context_course; /** * Contains the core course state actions. @@ -36,6 +40,159 @@ use moodle_exception; */ class stateactions { + /** + * Move course modules to another location in the same course. + * + * @param stateupdates $updates the affected course elements track + * @param stdClass $course the course object + * @param int[] $ids the list of affected course module ids + * @param int $targetsectionid optional target section id + * @param int $targetcmid optional target cm id + */ + public function cm_move( + stateupdates $updates, + stdClass $course, + array $ids, + ?int $targetsectionid = null, + ?int $targetcmid = null + ): void { + // Validate target elements. + if (!$targetsectionid && !$targetcmid) { + throw new moodle_exception("Action cm_move requires targetsectionid or targetcmid"); + } + + $this->validate_cms($course, $ids, __FUNCTION__); + + // Check capabilities on every activity context. + foreach ($ids as $cmid) { + $modcontext = context_module::instance($cmid); + require_capability('moodle/course:manageactivities', $modcontext); + } + + $modinfo = get_fast_modinfo($course); + + // Target cm has more priority than target section. + if (!empty($targetcmid)) { + $this->validate_cms($course, [$targetcmid], __FUNCTION__); + $targetcm = $modinfo->get_cm($targetcmid); + $targetsection = $modinfo->get_section_info_by_id($targetcm->section, MUST_EXIST); + } else { + $this->validate_sections($course, [$targetsectionid], __FUNCTION__); + $targetcm = null; + $targetsection = $modinfo->get_section_info_by_id($targetsectionid, MUST_EXIST); + } + + // The origin sections must be updated as well. + $originalsections = []; + + $cms = $this->get_cm_info($modinfo, $ids); + foreach ($cms as $cm) { + $currentsection = $modinfo->get_section_info_by_id($cm->section, MUST_EXIST); + moveto_module($cm, $targetsection, $targetcm); + $updates->add_cm_put($cm->id); + if ($currentsection->id != $targetsection->id) { + $originalsections[$currentsection->id] = true; + } + // If some of the original sections are also target sections, we don't need to update them. + if (array_key_exists($targetsection->id, $originalsections)) { + unset($originalsections[$targetsection->id]); + } + } + + // Use section_state to return the full affected section and activities updated state. + $this->cm_state($updates, $course, $ids, $targetsectionid, $targetcmid); + + foreach (array_keys($originalsections) as $sectionid) { + $updates->add_section_put($sectionid); + } + } + + /** + * Move course sections to another location in the same course. + * + * @param stateupdates $updates the affected course elements track + * @param stdClass $course the course object + * @param int[] $ids the list of affected course module ids + * @param int $targetsectionid optional target section id + * @param int $targetcmid optional target cm id + */ + public function section_move( + stateupdates $updates, + stdClass $course, + array $ids, + ?int $targetsectionid = null, + ?int $targetcmid = null + ): void { + // Validate target elements. + if (!$targetsectionid) { + throw new moodle_exception("Action cm_move requires targetsectionid"); + } + + $this->validate_sections($course, $ids, __FUNCTION__); + + $coursecontext = context_course::instance($course->id); + require_capability('moodle/course:movesections', $coursecontext); + + $modinfo = get_fast_modinfo($course); + + // Target section. + $this->validate_sections($course, [$targetsectionid], __FUNCTION__); + $targetsection = $modinfo->get_section_info_by_id($targetsectionid, MUST_EXIST); + + $affectedsections = [$targetsection->section => true]; + + $sections = $this->get_section_info($modinfo, $ids); + foreach ($sections as $section) { + $affectedsections[$section->section] = true; + move_section_to($course, $section->section, $targetsection->section); + } + + // Use section_state to return the section and activities updated state. + $this->section_state($updates, $course, $ids, $targetsectionid); + + // All course sections can be renamed because of the resort. + $allsections = $modinfo->get_section_info_all(); + foreach ($allsections as $section) { + // Ignore the affected sections because they are already in the updates. + if (isset($affectedsections[$section->section])) { + continue; + } + $updates->add_section_put($section->id); + } + // The section order is at a course level. + $updates->add_course_put(); + } + + /** + * Extract several cm_info from the course_modinfo. + * + * @param course_modinfo $modinfo the course modinfo. + * @param int[] $ids the course modules $ids + * @return cm_info[] the extracted cm_info objects + */ + protected function get_cm_info (course_modinfo $modinfo, array $ids): array { + $cms = []; + foreach ($ids as $cmid) { + $cms[$cmid] = $modinfo->get_cm($cmid); + } + return $cms; + } + + /** + * Extract several section_info from the course_modinfo. + * + * @param course_modinfo $modinfo the course modinfo. + * @param int[] $ids the course modules $ids + * @return section_info[] the extracted section_info objects + */ + protected function get_section_info(course_modinfo $modinfo, array $ids): array { + $sections = []; + foreach ($ids as $sectionid) { + $sections[$sectionid] = $modinfo->get_section_info_by_id($sectionid); + } + return $sections; + } + /** * Add the update messages of the updated version of any cm and section related to the cm ids. * diff --git a/course/format/templates/local/content.mustache b/course/format/templates/local/content.mustache index 1b6ab8fa90f..b36d7be1918 100644 --- a/course/format/templates/local/content.mustache +++ b/course/format/templates/local/content.mustache @@ -157,25 +157,32 @@ } } }} -

{{{title}}}

-{{{completionhelp}}} -
    - {{#initialsection}} - {{> core_courseformat/local/content/section }} - {{/initialsection}} - {{#sections}} - {{> core_courseformat/local/content/section }} - {{/sections}} -
-{{#hasnavigation}} -
- {{#sectionnavigation}} {{> core_courseformat/local/content/sectionnavigation }} {{/sectionnavigation}} -
+{{#js}} +require(['core_courseformat/local/courseindex/section'], function(component) { + component.init('{{uniqid}}-course-index-section-{{id}}'); +}); +{{/js}} diff --git a/course/format/topics/lib.php b/course/format/topics/lib.php index b0e84ef9659..d4e34e8a2c5 100644 --- a/course/format/topics/lib.php +++ b/course/format/topics/lib.php @@ -159,6 +159,10 @@ class format_topics extends core_courseformat\base { return $ajaxsupport; } + public function supports_components() { + return true; + } + /** * Loads all of the course sections into the navigation. * diff --git a/course/format/upgrade.txt b/course/format/upgrade.txt index bf173ba590a..ecf5c18a592 100644 --- a/course/format/upgrade.txt +++ b/course/format/upgrade.txt @@ -3,7 +3,8 @@ This files describes API changes for course formats Overview of this plugin type at http://docs.moodle.org/dev/Course_formats === 4.0 === -* New core_courseformat\uses_course_index() to define whether the course format uses course index or not. +* New core_courseformat\base::uses_course_index() to define whether the course format uses course index or not. +* New core_courseformat\base::supports_components() to specify if the format is compatible with reactive components. === 3.10 === * Added the missing callback supports_ajax() to format_social. diff --git a/course/format/weeks/lib.php b/course/format/weeks/lib.php index 5526b01f7b0..cdb38b355ac 100644 --- a/course/format/weeks/lib.php +++ b/course/format/weeks/lib.php @@ -160,6 +160,10 @@ class format_weeks extends core_courseformat\base { return $ajaxsupport; } + public function supports_components() { + return true; + } + /** * Loads all of the course sections into the navigation * diff --git a/course/lib.php b/course/lib.php index 1f6b8765e7f..5b9bd4f013c 100644 --- a/course/lib.php +++ b/course/lib.php @@ -3308,6 +3308,7 @@ function include_course_editor(course_format $format) { // Edition mode and some format specs must be passed to the init method. $setup = (object)[ 'editing' => $format->show_editor(), + 'supportscomponents' => $format->supports_components(), ]; // All the new editor elements will be loaded after the course is presented and // the initial course state will be generated using core_course_get_state webservice. diff --git a/lib/amd/build/inplace_editable.min.js b/lib/amd/build/inplace_editable.min.js index c7381bb06c8..f0bc8b9d0bc 100644 --- a/lib/amd/build/inplace_editable.min.js +++ b/lib/amd/build/inplace_editable.min.js @@ -1,2 +1,2 @@ -define ("core/inplace_editable",["jquery","core/ajax","core/templates","core/notification","core/str","core/config","core/url","core/form-autocomplete","core/pending"],function(a,b,c,d,e,f,g,h,i){a("body").on("click keypress","[data-inplaceeditable] [data-inplaceeditablelink]",function(j){if("keypress"===j.type&&13!==j.keyCode){return}var k=new i("autocomplete-start-editing");j.stopImmediatePropagation();j.preventDefault();var l=a(this),m=l.closest("[data-inplaceeditable]"),n=function(b){b.addClass("updating");var c=b.find("img.spinner");if(c.length){c.show()}else{c=a("").attr("src",g.imageUrl("i/loading_small")).addClass("spinner").addClass("smallicon");b.append(c)}},o=function(a){a.removeClass("updating");a.find("img.spinner").hide()},p=function(f,g){var h=[f.attr("data-itemid"),f.attr("data-component"),f.attr("data-itemtype")].join("-"),j=new i(h);n(f);b.call([{methodname:"core_update_inplace_editable",args:{itemid:f.attr("data-itemid"),component:f.attr("data-component"),itemtype:f.attr("data-itemtype"),value:g}}])[0].then(function(b){return c.render("core/inplace_editable",b).then(function(d,e){var g=f.attr("data-value"),h=a(d);c.replaceNode(f,h,e);h.find("[data-inplaceeditablelink]").focus();h.trigger({type:"updated",ajaxreturn:b,oldvalue:g})})}).then(function(){return j.resolve()}).fail(function(b){var c=a.Event("updatefailed",{exception:b,newvalue:g});o(f);M.util.js_complete(h);f.trigger(c);if(!c.isDefaultPrevented()){d.exception(b)}})},q=function(a){a.find("input").off();a.find("select").off();a.html(a.attr("data-oldcontent"));a.removeAttr("data-oldcontent");a.removeClass("inplaceeditingon");a.find("[data-inplaceeditablelink]").focus()},r=function(b,c){var d=b,e;for(e=0;e"+c+"").attr("id",r("id_editinstructions_",20)),e=a("").attr("id",r("id_inplacevalue_",20)).attr("value",b.attr("data-value")).attr("aria-describedby",d.attr("id")).addClass("ignoredirty").addClass("form-control"),g=a("").attr("for",e.attr("id"));b.html("").append(d).append(g).append(e);e.focus();e.select();e.on("keyup keypress focusout",function(a){if(f.behatsiterunning&&"focusout"===a.type){return}if("keypress"===a.type&&13===a.keyCode){var c=e.val();q(b);p(b,c)}if("keyup"===a.type&&27===a.keyCode||"focusout"===a.type){q(b)}})})},t=function(a,b){q(a);p(a,b)},u=function(b,c){var d,e=a("").attr("id",r("id_inplacevalue_",20)).addClass("custom-select"),g=a("").attr("for",e.attr("id"));for(d in c){e.append(a("