diff --git a/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-debug.js b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-debug.js
new file mode 100644
index 00000000000..1f1a6ae1b0d
--- /dev/null
+++ b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-debug.js
@@ -0,0 +1,492 @@
+YUI.add('moodle-course-dragdrop', function (Y, NAME) {
+
+/**
+ * Drag and Drop for course sections and course modules.
+ *
+ * @module moodle-course-dragdrop
+ */
+
+var CSS = {
+ ACTIONAREA: '.actions',
+ ACTIVITY: 'activity',
+ ACTIVITYINSTANCE: 'activityinstance',
+ CONTENT: 'content',
+ COURSECONTENT: 'course-content',
+ EDITINGMOVE: 'editing_move',
+ ICONCLASS: 'iconsmall',
+ JUMPMENU: 'jumpmenu',
+ LEFT: 'left',
+ LIGHTBOX: 'lightbox',
+ MOVEDOWN: 'movedown',
+ MOVEUP: 'moveup',
+ PAGECONTENT: 'page-content',
+ RIGHT: 'right',
+ SECTION: 'section',
+ SECTIONADDMENUS: 'section_add_menus',
+ SECTIONHANDLE: 'section-handle',
+ SUMMARY: 'summary',
+ SECTIONDRAGGABLE: 'sectiondraggable'
+};
+
+M.course = M.course || {};
+/**
+ * Section drag and drop.
+ *
+ * @class M.course.dragdrop.section
+ * @constructor
+ * @extends M.core.dragdrop
+ */
+var DRAGSECTION = function() {
+ DRAGSECTION.superclass.constructor.apply(this, arguments);
+};
+Y.extend(DRAGSECTION, M.core.dragdrop, {
+ sectionlistselector: null,
+
+ initializer: function() {
+ // Set group for parent class
+ this.groups = [ CSS.SECTIONDRAGGABLE ];
+ this.samenodeclass = M.course.format.get_sectionwrapperclass();
+ this.parentnodeclass = M.course.format.get_containerclass();
+
+ // Check if we are in single section mode
+ if (Y.Node.one('.' + CSS.JUMPMENU)) {
+ return false;
+ }
+ // Initialise sections dragging
+ this.sectionlistselector = M.course.format.get_section_wrapper(Y);
+ if (this.sectionlistselector) {
+ this.sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + this.sectionlistselector;
+
+ this.setup_for_section(this.sectionlistselector);
+
+ // Make each li element in the lists of sections draggable
+ var del = new Y.DD.Delegate({
+ container: '.' + CSS.COURSECONTENT,
+ nodes: '.' + CSS.SECTIONDRAGGABLE,
+ target: true,
+ handles: ['.' + CSS.LEFT],
+ dragConfig: {groups: this.groups}
+ });
+ del.dd.plug(Y.Plugin.DDProxy, {
+ // Don't move the node at the end of the drag
+ moveOnEnd: false
+ });
+ del.dd.plug(Y.Plugin.DDConstrained, {
+ // Keep it inside the .course-content
+ constrain: '#' + CSS.PAGECONTENT,
+ stickY: true
+ });
+ del.dd.plug(Y.Plugin.DDWinScroll);
+ }
+ },
+
+ /**
+ * Apply dragdrop features to the specified selector or node that refers to section(s)
+ *
+ * @method setup_for_section
+ * @param {String} baseselector The CSS selector or node to limit scope to
+ */
+ setup_for_section: function(baseselector) {
+ Y.Node.all(baseselector).each(function(sectionnode) {
+ // Determine the section ID
+ var sectionid = Y.Moodle.core_course.util.section.getId(sectionnode);
+
+ // We skip the top section as it is not draggable
+ if (sectionid > 0) {
+ // Remove move icons
+ var movedown = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEDOWN);
+ var moveup = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEUP);
+
+ // Add dragger icon
+ var title = M.util.get_string('movesection', 'moodle', sectionid);
+ var cssleft = sectionnode.one('.' + CSS.LEFT);
+
+ if ((movedown || moveup) && cssleft) {
+ cssleft.setStyle('cursor', 'move');
+ cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true));
+
+ if (moveup) {
+ moveup.remove();
+ }
+ if (movedown) {
+ movedown.remove();
+ }
+
+ // This section can be moved - add the class to indicate this to Y.DD.
+ sectionnode.addClass(CSS.SECTIONDRAGGABLE);
+ }
+ }
+ }, this);
+ },
+
+ /*
+ * Drag-dropping related functions
+ */
+ drag_start: function(e) {
+ // Get our drag object
+ var drag = e.target;
+ // Creat a dummy structure of the outer elemnents for clean styles application
+ var containernode = Y.Node.create('<' + M.course.format.get_containernode() + '>' + M.course.format.get_containernode() + '>');
+ containernode.addClass(M.course.format.get_containerclass());
+ var sectionnode = Y.Node.create('<' + M.course.format.get_sectionwrappernode() + '>' + M.course.format.get_sectionwrappernode() + '>');
+ sectionnode.addClass( M.course.format.get_sectionwrapperclass());
+ sectionnode.setStyle('margin', 0);
+ sectionnode.setContent(drag.get('node').get('innerHTML'));
+ containernode.appendChild(sectionnode);
+ drag.get('dragNode').setContent(containernode);
+ drag.get('dragNode').addClass(CSS.COURSECONTENT);
+ },
+
+ drag_dropmiss: function(e) {
+ // Missed the target, but we assume the user intended to drop it
+ // on the last last ghost node location, e.drag and e.drop should be
+ // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
+ this.drop_hit(e);
+ },
+
+ get_section_index: function(node) {
+ var sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + M.course.format.get_section_selector(Y),
+ sectionList = Y.all(sectionlistselector),
+ nodeIndex = sectionList.indexOf(node),
+ zeroIndex = sectionList.indexOf(Y.one('#section-0'));
+
+ return (nodeIndex - zeroIndex);
+ },
+
+ drop_hit: function(e) {
+ var drag = e.drag;
+
+ // Get references to our nodes and their IDs.
+ var dragnode = drag.get('node'),
+ dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode),
+ loopstart = dragnodeid,
+
+ dropnodeindex = this.get_section_index(dragnode),
+ loopend = dropnodeindex;
+
+ if (dragnodeid === dropnodeindex) {
+ Y.log("Skipping move - same location moving " + dragnodeid + " to " + dropnodeindex, 'debug', 'moodle-course-dragdrop');
+ return;
+ }
+
+ Y.log("Moving from position " + dragnodeid + " to position " + dropnodeindex, 'debug', 'moodle-course-dragdrop');
+
+ if (loopstart > loopend) {
+ // If we're going up, we need to swap the loop order
+ // because loops can't go backwards.
+ loopstart = dropnodeindex;
+ loopend = dragnodeid;
+ }
+
+ // Get the list of nodes.
+ drag.get('dragNode').removeClass(CSS.COURSECONTENT);
+ var sectionlist = Y.Node.all(this.sectionlistselector);
+
+ // Add a lightbox if it's not there.
+ var lightbox = M.util.add_lightbox(Y, dragnode);
+
+ // Handle any variables which we must pass via AJAX.
+ var params = {},
+ pageparams = this.get('config').pageparams,
+ varname;
+
+ for (varname in pageparams) {
+ if (!pageparams.hasOwnProperty(varname)) {
+ continue;
+ }
+ params[varname] = pageparams[varname];
+ }
+
+ // Prepare request parameters
+ params.sesskey = M.cfg.sesskey;
+ params.courseId = this.get('courseid');
+ params['class'] = 'section';
+ params.field = 'move';
+ params.id = dragnodeid;
+ params.value = dropnodeindex;
+
+ // Perform the AJAX request.
+ var uri = M.cfg.wwwroot + this.get('ajaxurl');
+ Y.io(uri, {
+ method: 'POST',
+ data: params,
+ on: {
+ start: function() {
+ lightbox.show();
+ },
+ success: function(tid, response) {
+ // Update section titles, we can't simply swap them as
+ // they might have custom title
+ try {
+ var responsetext = Y.JSON.parse(response.responseText);
+ if (responsetext.error) {
+ new M.core.ajaxException(responsetext);
+ }
+ M.course.format.process_sections(Y, sectionlist, responsetext, loopstart, loopend);
+ } catch (e) {}
+
+ // Update all of the section IDs - first unset them, then set them
+ // to avoid duplicates in the DOM.
+ var index;
+
+ // Classic bubble sort algorithm is applied to the section
+ // nodes between original drag node location and the new one.
+ var swapped = false;
+ do {
+ swapped = false;
+ for (index = loopstart; index <= loopend; index++) {
+ if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) >
+ Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) {
+ Y.log("Swapping " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) +
+ " with " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index)));
+ // Swap section id.
+ var sectionid = sectionlist.item(index - 1).get('id');
+ sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id'));
+ sectionlist.item(index).set('id', sectionid);
+
+ // See what format needs to swap.
+ M.course.format.swap_sections(Y, index - 1, index);
+
+ // Update flag.
+ swapped = true;
+ }
+ }
+ loopend = loopend - 1;
+ } while (swapped);
+
+ window.setTimeout(function() {
+ lightbox.hide();
+ }, 250);
+ },
+
+ failure: function(tid, response) {
+ this.ajax_failure(response);
+ lightbox.hide();
+ }
+ },
+ context:this
+ });
+ }
+
+}, {
+ NAME: 'course-dragdrop-section',
+ ATTRS: {
+ courseid: {
+ value: null
+ },
+ ajaxurl: {
+ value: 0
+ },
+ config: {
+ value: 0
+ }
+ }
+});
+
+M.course = M.course || {};
+M.course.init_section_dragdrop = function(params) {
+ new DRAGSECTION(params);
+};
+/**
+ * Resource drag and drop.
+ *
+ * @class M.course.dragdrop.resource
+ * @constructor
+ * @extends M.core.dragdrop
+ */
+var DRAGRESOURCE = function() {
+ DRAGRESOURCE.superclass.constructor.apply(this, arguments);
+};
+Y.extend(DRAGRESOURCE, M.core.dragdrop, {
+ initializer: function() {
+ // Set group for parent class
+ this.groups = ['resource'];
+ this.samenodeclass = CSS.ACTIVITY;
+ this.parentnodeclass = CSS.SECTION;
+ this.resourcedraghandle = this.get_drag_handle(M.str.moodle.move, CSS.EDITINGMOVE, CSS.ICONCLASS, true);
+
+ // Go through all sections
+ var sectionlistselector = M.course.format.get_section_selector(Y);
+ if (sectionlistselector) {
+ sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + sectionlistselector;
+ this.setup_for_section(sectionlistselector);
+
+ // Initialise drag & drop for all resources/activities
+ var nodeselector = sectionlistselector.slice(CSS.COURSECONTENT.length + 2) + ' li.' + CSS.ACTIVITY;
+ var del = new Y.DD.Delegate({
+ container: '.' + CSS.COURSECONTENT,
+ nodes: nodeselector,
+ target: true,
+ handles: ['.' + CSS.EDITINGMOVE],
+ dragConfig: {groups: this.groups}
+ });
+ del.dd.plug(Y.Plugin.DDProxy, {
+ // Don't move the node at the end of the drag
+ moveOnEnd: false,
+ cloneNode: true
+ });
+ del.dd.plug(Y.Plugin.DDConstrained, {
+ // Keep it inside the .course-content
+ constrain: '#' + CSS.PAGECONTENT
+ });
+ del.dd.plug(Y.Plugin.DDWinScroll);
+
+ M.course.coursebase.register_module(this);
+ M.course.dragres = this;
+ }
+ },
+
+ /**
+ * Apply dragdrop features to the specified selector or node that refers to section(s)
+ *
+ * @method setup_for_section
+ * @param {String} baseselector The CSS selector or node to limit scope to
+ */
+ setup_for_section: function(baseselector) {
+ Y.Node.all(baseselector).each(function(sectionnode) {
+ var resources = sectionnode.one('.' + CSS.CONTENT + ' ul.' + CSS.SECTION);
+ // See if resources ul exists, if not create one
+ if (!resources) {
+ resources = Y.Node.create('
');
+ resources.addClass(CSS.SECTION);
+ sectionnode.one('.' + CSS.CONTENT + ' div.' + CSS.SUMMARY).insert(resources, 'after');
+ }
+ resources.setAttribute('data-draggroups', this.groups.join(' '));
+ // Define empty ul as droptarget, so that item could be moved to empty list
+ new Y.DD.Drop({
+ node: resources,
+ groups: this.groups,
+ padding: '20 0 20 0'
+ });
+
+ // Initialise each resource/activity in this section
+ this.setup_for_resource('#' + sectionnode.get('id') + ' li.' + CSS.ACTIVITY);
+ }, this);
+ },
+
+ /**
+ * Apply dragdrop features to the specified selector or node that refers to resource(s)
+ *
+ * @method setup_for_resource
+ * @param {String} baseselector The CSS selector or node to limit scope to
+ */
+ setup_for_resource: function(baseselector) {
+ Y.Node.all(baseselector).each(function(resourcesnode) {
+ // Replace move icons
+ var move = resourcesnode.one('a.' + CSS.EDITINGMOVE);
+ if (move) {
+ move.replace(this.resourcedraghandle.cloneNode(true));
+ }
+ }, this);
+ },
+
+ drag_start: function(e) {
+ // Get our drag object
+ var drag = e.target;
+ drag.get('dragNode').setContent(drag.get('node').get('innerHTML'));
+ drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline');
+ },
+
+ drag_dropmiss: function(e) {
+ // Missed the target, but we assume the user intended to drop it
+ // on the last last ghost node location, e.drag and e.drop should be
+ // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
+ this.drop_hit(e);
+ },
+
+ drop_hit: function(e) {
+ var drag = e.drag;
+ // Get a reference to our drag node
+ var dragnode = drag.get('node');
+ var dropnode = e.drop.get('node');
+
+ // Add spinner if it not there
+ var actionarea = dragnode.one(CSS.ACTIONAREA);
+ var spinner = M.util.add_spinner(Y, actionarea);
+
+ var params = {};
+
+ // Handle any variables which we must pass back through to
+ var pageparams = this.get('config').pageparams;
+ var varname;
+ for (varname in pageparams) {
+ params[varname] = pageparams[varname];
+ }
+
+ // Prepare request parameters
+ params.sesskey = M.cfg.sesskey;
+ params.courseId = this.get('courseid');
+ params['class'] = 'resource';
+ params.field = 'move';
+ params.id = Number(Y.Moodle.core_course.util.cm.getId(dragnode));
+ params.sectionId = Y.Moodle.core_course.util.section.getId(dropnode.ancestor(M.course.format.get_section_wrapper(Y), true));
+
+ if (dragnode.next()) {
+ params.beforeId = Number(Y.Moodle.core_course.util.cm.getId(dragnode.next()));
+ }
+
+ // Do AJAX request
+ var uri = M.cfg.wwwroot + this.get('ajaxurl');
+
+ Y.io(uri, {
+ method: 'POST',
+ data: params,
+ on: {
+ start: function() {
+ this.lock_drag_handle(drag, CSS.EDITINGMOVE);
+ spinner.show();
+ },
+ success: function(tid, response) {
+ var responsetext = Y.JSON.parse(response.responseText);
+ var params = {element: dragnode, visible: responsetext.visible};
+ M.course.coursebase.invoke_function('set_visibility_resource_ui', params);
+ this.unlock_drag_handle(drag, CSS.EDITINGMOVE);
+ window.setTimeout(function() {
+ spinner.hide();
+ }, 250);
+ },
+ failure: function(tid, response) {
+ this.ajax_failure(response);
+ this.unlock_drag_handle(drag, CSS.SECTIONHANDLE);
+ spinner.hide();
+ // TODO: revert nodes location
+ }
+ },
+ context:this
+ });
+ }
+}, {
+ NAME: 'course-dragdrop-resource',
+ ATTRS: {
+ courseid: {
+ value: null
+ },
+ ajaxurl: {
+ value: 0
+ },
+ config: {
+ value: 0
+ }
+ }
+});
+
+M.course = M.course || {};
+M.course.init_resource_dragdrop = function(params) {
+ new DRAGRESOURCE(params);
+};
+
+
+}, '@VERSION@', {
+ "requires": [
+ "base",
+ "node",
+ "io",
+ "dom",
+ "dd",
+ "dd-scroll",
+ "moodle-core-dragdrop",
+ "moodle-core-notification",
+ "moodle-course-coursebase",
+ "moodle-course-util"
+ ]
+});
diff --git a/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-min.js b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-min.js
new file mode 100644
index 00000000000..90f29135e47
--- /dev/null
+++ b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-min.js
@@ -0,0 +1,2 @@
+YUI.add("moodle-course-dragdrop",function(e,t){var n={ACTIONAREA:".actions",ACTIVITY:"activity",ACTIVITYINSTANCE:"activityinstance",CONTENT:"content",COURSECONTENT:"course-content",EDITINGMOVE:"editing_move",ICONCLASS:"iconsmall",JUMPMENU:"jumpmenu",LEFT:"left",LIGHTBOX:"lightbox",MOVEDOWN:"movedown",MOVEUP:"moveup",PAGECONTENT:"page-content",RIGHT:"right",SECTION:"section",SECTIONADDMENUS:"section_add_menus",SECTIONHANDLE:"section-handle",SUMMARY:"summary",SECTIONDRAGGABLE:"sectiondraggable"};M.course=M.course||{};var r=function(){r.superclass.constructor.apply(this,arguments)};e.extend(r,M.core.dragdrop,{sectionlistselector:null,initializer:function(){this.groups=[n.SECTIONDRAGGABLE],this.samenodeclass=M.course.format.get_sectionwrapperclass(),this.parentnodeclass=M.course.format.get_containerclass();if(e.Node.one("."+n.JUMPMENU))return!1;this.sectionlistselector=M.course.format.get_section_wrapper(e);if(this.sectionlistselector){this.sectionlistselector="."+n.COURSECONTENT+" "+this.sectionlistselector,this.setup_for_section(this.sectionlistselector);var t=new e.DD.Delegate({container:"."+n.COURSECONTENT,nodes:"."+n.SECTIONDRAGGABLE,target:!0,handles:["."+n.LEFT],dragConfig:{groups:this.groups}});t.dd.plug(e.Plugin.DDProxy,{moveOnEnd:!1}),t.dd.plug(e.Plugin.DDConstrained,{constrain:"#"+n.PAGECONTENT,stickY:!0}),t.dd.plug(e.Plugin.DDWinScroll)}},setup_for_section:function(t){e.Node.all(t).each(function(t){var r=e.Moodle.core_course.util.section.getId(t);if(r>0){var i=t.one("."+n.RIGHT+" a."+n.MOVEDOWN),s=t.one("."+n.RIGHT+" a."+n.MOVEUP),o=M.util.get_string("movesection","moodle",r),u=t.one("."+n.LEFT);(i||s)&&u&&(u.setStyle("cursor","move"),u.appendChild(this.get_drag_handle(o,n.SECTIONHANDLE,"icon",!0)),s&&s.remove(),i&&i.remove(),t.addClass(n.SECTIONDRAGGABLE))}},this)},drag_start:function(t){var r=t.target,i=e.Node.create("<"+M.course.format.get_containernode()+">"+M.course.format.get_containernode()+">");i.addClass(M.course.format.get_containerclass());var s=e.Node.create("<"+M.course.format.get_sectionwrappernode()+">"+M.course.format.get_sectionwrappernode()+">");s.addClass(M.course.format.get_sectionwrapperclass()),s.setStyle("margin",0),s.setContent(r.get("node").get("innerHTML")),i.appendChild(s),r.get("dragNode").setContent(i),r.get("dragNode").addClass(n.COURSECONTENT)},drag_dropmiss:function(e){this.drop_hit(e)},get_section_index:function(t){var r="."+n.COURSECONTENT+" "+M.course.format.get_section_selector(e),i=e.all(r),s=i.indexOf(t),o=i.indexOf(e.one("#section-0"));return s-o},drop_hit:function(t){var r=t.drag,i=r.get("node"),s=e.Moodle.core_course.util.section.getId(i),o=s,u=this.get_section_index(i),a=u;if(s===u)return;o>a&&(o=u,a=s),r.get("dragNode").removeClass(n.COURSECONTENT);var f=e.Node.all(this.sectionlistselector),l=M.util.add_lightbox(e,i),c={},h=this.get("config").pageparams,p;for(p in h){if(!h.hasOwnProperty(p))continue;c[p]=h[p]}c.sesskey=M.cfg.sesskey,c.courseId=this.get("courseid"),c["class"]="section",c.field="move",c.id=s,c.value=u;var d=M.cfg.wwwroot+this.get("ajaxurl");e.io(d,{method:"POST",data:c,on:{start:function(){l.show()},success:function(t,n){try{var r=e.JSON.parse(n.responseText);r.error&&new M.core.ajaxException(r),M.course.format.process_sections(e,f,r,o,a)}catch(i){}var s,u=!1;do{u=!1;for(s=o;s<=a;s++)if(e.Moodle.core_course.util.section.getId(f.item(s-1))>e.Moodle.core_course.util.section.getId(f.item(s))){var c=f.item(s-1).get("id");f.item(s-1).set("id",f.item(s).get("id")),f.item(s).set("id",c),M.course.format.swap_sections(e,s-1,s),u=!0}a-=1}while(u);window.setTimeout(function(){l.hide()},250)},failure:function(e,t){this.ajax_failure(t),l.hide()}},context:this})}},{NAME:"course-dragdrop-section",ATTRS:{courseid:{value:null},ajaxurl:{value:0},config:{value:0}}}),M.course=M.course||{},M.course.init_section_dragdrop=function(e){new r(e)};var i=function(){i.superclass.constructor.apply(this,arguments)};e.extend(i,M.core.dragdrop,{initializer:function(){this.groups=["resource"],this.samenodeclass=n.ACTIVITY,this.parentnodeclass=n.SECTION,this.resourcedraghandle=this.get_drag_handle(M.str.moodle.move,n.EDITINGMOVE,n.ICONCLASS,!0);var t=M.course.format.get_section_selector(e);if(t){t="."+n.COURSECONTENT+" "+t,this.setup_for_section(t);var r=t.slice(n.COURSECONTENT.length+2)+" li."+n.ACTIVITY,i=new e.DD.Delegate({container:"."+n.COURSECONTENT,nodes:r,target:!0,handles:["."+n.EDITINGMOVE],dragConfig:{groups:this.groups}});i.dd.plug(e.Plugin.DDProxy,{moveOnEnd:!1,cloneNode:!0}),i.dd.plug(e.Plugin.DDConstrained,{constrain:"#"+n.PAGECONTENT}),i.dd.plug(e.Plugin.DDWinScroll),M.course.coursebase.register_module(this),M.course.dragres=this}},setup_for_section:function(t){e.Node.all(t).each(function(t){var r=t.one("."+n.CONTENT+" ul."+n.SECTION);r||(r=e.Node.create(""),r.addClass(n.SECTION),t.one("."+n.CONTENT+" div."+n.SUMMARY).insert(r,"after")),r.setAttribute("data-draggroups",this.groups.join(" ")),new e.DD.Drop({node:r,groups:this.groups,padding:"20 0 20 0"}),this.setup_for_resource("#"+t.get("id")+" li."+n.ACTIVITY)},this)},setup_for_resource:function(t){e.Node.all(t).each(function(e){var t=e.one("a."+n.EDITINGMOVE);t&&t.replace(this.resourcedraghandle.cloneNode(!0))},this)},drag_start:function(e){var t=e.target;t.get("dragNode").setContent(t.get("node").get("innerHTML")),t.get("dragNode").all("img.iconsmall").setStyle("vertical-align","baseline")},drag_dropmiss:function(e){this.drop_hit(e)},drop_hit:function(t){var r=t.drag,i=r.get("node"),s=t.drop.get("node"),o=i.one(n.ACTIONAREA),u=M.util.add_spinner(e,o),a={},f=this.get("config").pageparams,l;for(l in f)a[l]=f[l];a.sesskey=M.cfg.sesskey,a.courseId=this.get("courseid"),a["class"]="resource",a.field="move",a.id=Number(e.Moodle.core_course.util.cm.getId(i)),a.sectionId=e.Moodle.core_course.util.section.getId(s.ancestor(M.course.format.get_section_wrapper(e),!0)),i.next()&&(a.beforeId=Number(e.Moodle.core_course.util.cm.getId(i.next())));var c=M.cfg.wwwroot+this.get("ajaxurl");e.io(c,
+{method:"POST",data:a,on:{start:function(){this.lock_drag_handle(r,n.EDITINGMOVE),u.show()},success:function(t,s){var o=e.JSON.parse(s.responseText),a={element:i,visible:o.visible};M.course.coursebase.invoke_function("set_visibility_resource_ui",a),this.unlock_drag_handle(r,n.EDITINGMOVE),window.setTimeout(function(){u.hide()},250)},failure:function(e,t){this.ajax_failure(t),this.unlock_drag_handle(r,n.SECTIONHANDLE),u.hide()}},context:this})}},{NAME:"course-dragdrop-resource",ATTRS:{courseid:{value:null},ajaxurl:{value:0},config:{value:0}}}),M.course=M.course||{},M.course.init_resource_dragdrop=function(e){new i(e)}},"@VERSION@",{requires:["base","node","io","dom","dd","dd-scroll","moodle-core-dragdrop","moodle-core-notification","moodle-course-coursebase","moodle-course-util"]});
diff --git a/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop.js b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop.js
new file mode 100644
index 00000000000..795c5bbf0e3
--- /dev/null
+++ b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop.js
@@ -0,0 +1,488 @@
+YUI.add('moodle-course-dragdrop', function (Y, NAME) {
+
+/**
+ * Drag and Drop for course sections and course modules.
+ *
+ * @module moodle-course-dragdrop
+ */
+
+var CSS = {
+ ACTIONAREA: '.actions',
+ ACTIVITY: 'activity',
+ ACTIVITYINSTANCE: 'activityinstance',
+ CONTENT: 'content',
+ COURSECONTENT: 'course-content',
+ EDITINGMOVE: 'editing_move',
+ ICONCLASS: 'iconsmall',
+ JUMPMENU: 'jumpmenu',
+ LEFT: 'left',
+ LIGHTBOX: 'lightbox',
+ MOVEDOWN: 'movedown',
+ MOVEUP: 'moveup',
+ PAGECONTENT: 'page-content',
+ RIGHT: 'right',
+ SECTION: 'section',
+ SECTIONADDMENUS: 'section_add_menus',
+ SECTIONHANDLE: 'section-handle',
+ SUMMARY: 'summary',
+ SECTIONDRAGGABLE: 'sectiondraggable'
+};
+
+M.course = M.course || {};
+/**
+ * Section drag and drop.
+ *
+ * @class M.course.dragdrop.section
+ * @constructor
+ * @extends M.core.dragdrop
+ */
+var DRAGSECTION = function() {
+ DRAGSECTION.superclass.constructor.apply(this, arguments);
+};
+Y.extend(DRAGSECTION, M.core.dragdrop, {
+ sectionlistselector: null,
+
+ initializer: function() {
+ // Set group for parent class
+ this.groups = [ CSS.SECTIONDRAGGABLE ];
+ this.samenodeclass = M.course.format.get_sectionwrapperclass();
+ this.parentnodeclass = M.course.format.get_containerclass();
+
+ // Check if we are in single section mode
+ if (Y.Node.one('.' + CSS.JUMPMENU)) {
+ return false;
+ }
+ // Initialise sections dragging
+ this.sectionlistselector = M.course.format.get_section_wrapper(Y);
+ if (this.sectionlistselector) {
+ this.sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + this.sectionlistselector;
+
+ this.setup_for_section(this.sectionlistselector);
+
+ // Make each li element in the lists of sections draggable
+ var del = new Y.DD.Delegate({
+ container: '.' + CSS.COURSECONTENT,
+ nodes: '.' + CSS.SECTIONDRAGGABLE,
+ target: true,
+ handles: ['.' + CSS.LEFT],
+ dragConfig: {groups: this.groups}
+ });
+ del.dd.plug(Y.Plugin.DDProxy, {
+ // Don't move the node at the end of the drag
+ moveOnEnd: false
+ });
+ del.dd.plug(Y.Plugin.DDConstrained, {
+ // Keep it inside the .course-content
+ constrain: '#' + CSS.PAGECONTENT,
+ stickY: true
+ });
+ del.dd.plug(Y.Plugin.DDWinScroll);
+ }
+ },
+
+ /**
+ * Apply dragdrop features to the specified selector or node that refers to section(s)
+ *
+ * @method setup_for_section
+ * @param {String} baseselector The CSS selector or node to limit scope to
+ */
+ setup_for_section: function(baseselector) {
+ Y.Node.all(baseselector).each(function(sectionnode) {
+ // Determine the section ID
+ var sectionid = Y.Moodle.core_course.util.section.getId(sectionnode);
+
+ // We skip the top section as it is not draggable
+ if (sectionid > 0) {
+ // Remove move icons
+ var movedown = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEDOWN);
+ var moveup = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEUP);
+
+ // Add dragger icon
+ var title = M.util.get_string('movesection', 'moodle', sectionid);
+ var cssleft = sectionnode.one('.' + CSS.LEFT);
+
+ if ((movedown || moveup) && cssleft) {
+ cssleft.setStyle('cursor', 'move');
+ cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true));
+
+ if (moveup) {
+ moveup.remove();
+ }
+ if (movedown) {
+ movedown.remove();
+ }
+
+ // This section can be moved - add the class to indicate this to Y.DD.
+ sectionnode.addClass(CSS.SECTIONDRAGGABLE);
+ }
+ }
+ }, this);
+ },
+
+ /*
+ * Drag-dropping related functions
+ */
+ drag_start: function(e) {
+ // Get our drag object
+ var drag = e.target;
+ // Creat a dummy structure of the outer elemnents for clean styles application
+ var containernode = Y.Node.create('<' + M.course.format.get_containernode() + '>' + M.course.format.get_containernode() + '>');
+ containernode.addClass(M.course.format.get_containerclass());
+ var sectionnode = Y.Node.create('<' + M.course.format.get_sectionwrappernode() + '>' + M.course.format.get_sectionwrappernode() + '>');
+ sectionnode.addClass( M.course.format.get_sectionwrapperclass());
+ sectionnode.setStyle('margin', 0);
+ sectionnode.setContent(drag.get('node').get('innerHTML'));
+ containernode.appendChild(sectionnode);
+ drag.get('dragNode').setContent(containernode);
+ drag.get('dragNode').addClass(CSS.COURSECONTENT);
+ },
+
+ drag_dropmiss: function(e) {
+ // Missed the target, but we assume the user intended to drop it
+ // on the last last ghost node location, e.drag and e.drop should be
+ // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
+ this.drop_hit(e);
+ },
+
+ get_section_index: function(node) {
+ var sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + M.course.format.get_section_selector(Y),
+ sectionList = Y.all(sectionlistselector),
+ nodeIndex = sectionList.indexOf(node),
+ zeroIndex = sectionList.indexOf(Y.one('#section-0'));
+
+ return (nodeIndex - zeroIndex);
+ },
+
+ drop_hit: function(e) {
+ var drag = e.drag;
+
+ // Get references to our nodes and their IDs.
+ var dragnode = drag.get('node'),
+ dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode),
+ loopstart = dragnodeid,
+
+ dropnodeindex = this.get_section_index(dragnode),
+ loopend = dropnodeindex;
+
+ if (dragnodeid === dropnodeindex) {
+ return;
+ }
+
+
+ if (loopstart > loopend) {
+ // If we're going up, we need to swap the loop order
+ // because loops can't go backwards.
+ loopstart = dropnodeindex;
+ loopend = dragnodeid;
+ }
+
+ // Get the list of nodes.
+ drag.get('dragNode').removeClass(CSS.COURSECONTENT);
+ var sectionlist = Y.Node.all(this.sectionlistselector);
+
+ // Add a lightbox if it's not there.
+ var lightbox = M.util.add_lightbox(Y, dragnode);
+
+ // Handle any variables which we must pass via AJAX.
+ var params = {},
+ pageparams = this.get('config').pageparams,
+ varname;
+
+ for (varname in pageparams) {
+ if (!pageparams.hasOwnProperty(varname)) {
+ continue;
+ }
+ params[varname] = pageparams[varname];
+ }
+
+ // Prepare request parameters
+ params.sesskey = M.cfg.sesskey;
+ params.courseId = this.get('courseid');
+ params['class'] = 'section';
+ params.field = 'move';
+ params.id = dragnodeid;
+ params.value = dropnodeindex;
+
+ // Perform the AJAX request.
+ var uri = M.cfg.wwwroot + this.get('ajaxurl');
+ Y.io(uri, {
+ method: 'POST',
+ data: params,
+ on: {
+ start: function() {
+ lightbox.show();
+ },
+ success: function(tid, response) {
+ // Update section titles, we can't simply swap them as
+ // they might have custom title
+ try {
+ var responsetext = Y.JSON.parse(response.responseText);
+ if (responsetext.error) {
+ new M.core.ajaxException(responsetext);
+ }
+ M.course.format.process_sections(Y, sectionlist, responsetext, loopstart, loopend);
+ } catch (e) {}
+
+ // Update all of the section IDs - first unset them, then set them
+ // to avoid duplicates in the DOM.
+ var index;
+
+ // Classic bubble sort algorithm is applied to the section
+ // nodes between original drag node location and the new one.
+ var swapped = false;
+ do {
+ swapped = false;
+ for (index = loopstart; index <= loopend; index++) {
+ if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) >
+ Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) {
+ // Swap section id.
+ var sectionid = sectionlist.item(index - 1).get('id');
+ sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id'));
+ sectionlist.item(index).set('id', sectionid);
+
+ // See what format needs to swap.
+ M.course.format.swap_sections(Y, index - 1, index);
+
+ // Update flag.
+ swapped = true;
+ }
+ }
+ loopend = loopend - 1;
+ } while (swapped);
+
+ window.setTimeout(function() {
+ lightbox.hide();
+ }, 250);
+ },
+
+ failure: function(tid, response) {
+ this.ajax_failure(response);
+ lightbox.hide();
+ }
+ },
+ context:this
+ });
+ }
+
+}, {
+ NAME: 'course-dragdrop-section',
+ ATTRS: {
+ courseid: {
+ value: null
+ },
+ ajaxurl: {
+ value: 0
+ },
+ config: {
+ value: 0
+ }
+ }
+});
+
+M.course = M.course || {};
+M.course.init_section_dragdrop = function(params) {
+ new DRAGSECTION(params);
+};
+/**
+ * Resource drag and drop.
+ *
+ * @class M.course.dragdrop.resource
+ * @constructor
+ * @extends M.core.dragdrop
+ */
+var DRAGRESOURCE = function() {
+ DRAGRESOURCE.superclass.constructor.apply(this, arguments);
+};
+Y.extend(DRAGRESOURCE, M.core.dragdrop, {
+ initializer: function() {
+ // Set group for parent class
+ this.groups = ['resource'];
+ this.samenodeclass = CSS.ACTIVITY;
+ this.parentnodeclass = CSS.SECTION;
+ this.resourcedraghandle = this.get_drag_handle(M.str.moodle.move, CSS.EDITINGMOVE, CSS.ICONCLASS, true);
+
+ // Go through all sections
+ var sectionlistselector = M.course.format.get_section_selector(Y);
+ if (sectionlistselector) {
+ sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + sectionlistselector;
+ this.setup_for_section(sectionlistselector);
+
+ // Initialise drag & drop for all resources/activities
+ var nodeselector = sectionlistselector.slice(CSS.COURSECONTENT.length + 2) + ' li.' + CSS.ACTIVITY;
+ var del = new Y.DD.Delegate({
+ container: '.' + CSS.COURSECONTENT,
+ nodes: nodeselector,
+ target: true,
+ handles: ['.' + CSS.EDITINGMOVE],
+ dragConfig: {groups: this.groups}
+ });
+ del.dd.plug(Y.Plugin.DDProxy, {
+ // Don't move the node at the end of the drag
+ moveOnEnd: false,
+ cloneNode: true
+ });
+ del.dd.plug(Y.Plugin.DDConstrained, {
+ // Keep it inside the .course-content
+ constrain: '#' + CSS.PAGECONTENT
+ });
+ del.dd.plug(Y.Plugin.DDWinScroll);
+
+ M.course.coursebase.register_module(this);
+ M.course.dragres = this;
+ }
+ },
+
+ /**
+ * Apply dragdrop features to the specified selector or node that refers to section(s)
+ *
+ * @method setup_for_section
+ * @param {String} baseselector The CSS selector or node to limit scope to
+ */
+ setup_for_section: function(baseselector) {
+ Y.Node.all(baseselector).each(function(sectionnode) {
+ var resources = sectionnode.one('.' + CSS.CONTENT + ' ul.' + CSS.SECTION);
+ // See if resources ul exists, if not create one
+ if (!resources) {
+ resources = Y.Node.create('');
+ resources.addClass(CSS.SECTION);
+ sectionnode.one('.' + CSS.CONTENT + ' div.' + CSS.SUMMARY).insert(resources, 'after');
+ }
+ resources.setAttribute('data-draggroups', this.groups.join(' '));
+ // Define empty ul as droptarget, so that item could be moved to empty list
+ new Y.DD.Drop({
+ node: resources,
+ groups: this.groups,
+ padding: '20 0 20 0'
+ });
+
+ // Initialise each resource/activity in this section
+ this.setup_for_resource('#' + sectionnode.get('id') + ' li.' + CSS.ACTIVITY);
+ }, this);
+ },
+
+ /**
+ * Apply dragdrop features to the specified selector or node that refers to resource(s)
+ *
+ * @method setup_for_resource
+ * @param {String} baseselector The CSS selector or node to limit scope to
+ */
+ setup_for_resource: function(baseselector) {
+ Y.Node.all(baseselector).each(function(resourcesnode) {
+ // Replace move icons
+ var move = resourcesnode.one('a.' + CSS.EDITINGMOVE);
+ if (move) {
+ move.replace(this.resourcedraghandle.cloneNode(true));
+ }
+ }, this);
+ },
+
+ drag_start: function(e) {
+ // Get our drag object
+ var drag = e.target;
+ drag.get('dragNode').setContent(drag.get('node').get('innerHTML'));
+ drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline');
+ },
+
+ drag_dropmiss: function(e) {
+ // Missed the target, but we assume the user intended to drop it
+ // on the last last ghost node location, e.drag and e.drop should be
+ // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
+ this.drop_hit(e);
+ },
+
+ drop_hit: function(e) {
+ var drag = e.drag;
+ // Get a reference to our drag node
+ var dragnode = drag.get('node');
+ var dropnode = e.drop.get('node');
+
+ // Add spinner if it not there
+ var actionarea = dragnode.one(CSS.ACTIONAREA);
+ var spinner = M.util.add_spinner(Y, actionarea);
+
+ var params = {};
+
+ // Handle any variables which we must pass back through to
+ var pageparams = this.get('config').pageparams;
+ var varname;
+ for (varname in pageparams) {
+ params[varname] = pageparams[varname];
+ }
+
+ // Prepare request parameters
+ params.sesskey = M.cfg.sesskey;
+ params.courseId = this.get('courseid');
+ params['class'] = 'resource';
+ params.field = 'move';
+ params.id = Number(Y.Moodle.core_course.util.cm.getId(dragnode));
+ params.sectionId = Y.Moodle.core_course.util.section.getId(dropnode.ancestor(M.course.format.get_section_wrapper(Y), true));
+
+ if (dragnode.next()) {
+ params.beforeId = Number(Y.Moodle.core_course.util.cm.getId(dragnode.next()));
+ }
+
+ // Do AJAX request
+ var uri = M.cfg.wwwroot + this.get('ajaxurl');
+
+ Y.io(uri, {
+ method: 'POST',
+ data: params,
+ on: {
+ start: function() {
+ this.lock_drag_handle(drag, CSS.EDITINGMOVE);
+ spinner.show();
+ },
+ success: function(tid, response) {
+ var responsetext = Y.JSON.parse(response.responseText);
+ var params = {element: dragnode, visible: responsetext.visible};
+ M.course.coursebase.invoke_function('set_visibility_resource_ui', params);
+ this.unlock_drag_handle(drag, CSS.EDITINGMOVE);
+ window.setTimeout(function() {
+ spinner.hide();
+ }, 250);
+ },
+ failure: function(tid, response) {
+ this.ajax_failure(response);
+ this.unlock_drag_handle(drag, CSS.SECTIONHANDLE);
+ spinner.hide();
+ // TODO: revert nodes location
+ }
+ },
+ context:this
+ });
+ }
+}, {
+ NAME: 'course-dragdrop-resource',
+ ATTRS: {
+ courseid: {
+ value: null
+ },
+ ajaxurl: {
+ value: 0
+ },
+ config: {
+ value: 0
+ }
+ }
+});
+
+M.course = M.course || {};
+M.course.init_resource_dragdrop = function(params) {
+ new DRAGRESOURCE(params);
+};
+
+
+}, '@VERSION@', {
+ "requires": [
+ "base",
+ "node",
+ "io",
+ "dom",
+ "dd",
+ "dd-scroll",
+ "moodle-core-dragdrop",
+ "moodle-core-notification",
+ "moodle-course-coursebase",
+ "moodle-course-util"
+ ]
+});
diff --git a/course/yui/dragdrop/dragdrop.js b/course/yui/dragdrop/dragdrop.js
deleted file mode 100644
index 7d8a9dd0a68..00000000000
--- a/course/yui/dragdrop/dragdrop.js
+++ /dev/null
@@ -1,454 +0,0 @@
-YUI.add('moodle-course-dragdrop', function(Y) {
-
- var CSS = {
- ACTIONAREA: '.actions',
- ACTIVITY : 'activity',
- ACTIVITYINSTANCE : 'activityinstance',
- CONTENT : 'content',
- COURSECONTENT : 'course-content',
- EDITINGMOVE : 'editing_move',
- ICONCLASS : 'iconsmall',
- JUMPMENU : 'jumpmenu',
- LEFT : 'left',
- LIGHTBOX : 'lightbox',
- MOVEDOWN : 'movedown',
- MOVEUP : 'moveup',
- PAGECONTENT : 'page-content',
- RIGHT : 'right',
- SECTION : 'section',
- SECTIONADDMENUS : 'section_add_menus',
- SECTIONHANDLE : 'section-handle',
- SUMMARY : 'summary',
- SECTIONDRAGGABLE: 'sectiondraggable'
- };
-
- var DRAGSECTION = function() {
- DRAGSECTION.superclass.constructor.apply(this, arguments);
- };
- Y.extend(DRAGSECTION, M.core.dragdrop, {
- sectionlistselector : null,
-
- initializer : function() {
- // Set group for parent class
- this.groups = [ CSS.SECTIONDRAGGABLE ];
- this.samenodeclass = M.course.format.get_sectionwrapperclass();
- this.parentnodeclass = M.course.format.get_containerclass();
-
- // Check if we are in single section mode
- if (Y.Node.one('.'+CSS.JUMPMENU)) {
- return false;
- }
- // Initialise sections dragging
- this.sectionlistselector = M.course.format.get_section_wrapper(Y);
- if (this.sectionlistselector) {
- this.sectionlistselector = '.'+CSS.COURSECONTENT+' '+this.sectionlistselector;
-
- this.setup_for_section(this.sectionlistselector);
-
- // Make each li element in the lists of sections draggable
- var del = new Y.DD.Delegate({
- container: '.'+CSS.COURSECONTENT,
- nodes: '.' + CSS.SECTIONDRAGGABLE,
- target: true,
- handles: ['.'+CSS.LEFT],
- dragConfig: {groups: this.groups}
- });
- del.dd.plug(Y.Plugin.DDProxy, {
- // Don't move the node at the end of the drag
- moveOnEnd: false
- });
- del.dd.plug(Y.Plugin.DDConstrained, {
- // Keep it inside the .course-content
- constrain: '#'+CSS.PAGECONTENT,
- stickY: true
- });
- del.dd.plug(Y.Plugin.DDWinScroll);
- }
- },
-
- /**
- * Apply dragdrop features to the specified selector or node that refers to section(s)
- *
- * @param baseselector The CSS selector or node to limit scope to
- * @return void
- */
- setup_for_section : function(baseselector) {
- Y.Node.all(baseselector).each(function(sectionnode) {
- // Determine the section ID
- var sectionid = Y.Moodle.core_course.util.section.getId(sectionnode);
-
- // We skip the top section as it is not draggable
- if (sectionid > 0) {
- // Remove move icons
- var movedown = sectionnode.one('.'+CSS.RIGHT+' a.'+CSS.MOVEDOWN);
- var moveup = sectionnode.one('.'+CSS.RIGHT+' a.'+CSS.MOVEUP);
-
- // Add dragger icon
- var title = M.util.get_string('movesection', 'moodle', sectionid);
- var cssleft = sectionnode.one('.'+CSS.LEFT);
-
- if ((movedown || moveup) && cssleft) {
- cssleft.setStyle('cursor', 'move');
- cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true));
-
- if (moveup) {
- moveup.remove();
- }
- if (movedown) {
- movedown.remove();
- }
-
- // This section can be moved - add the class to indicate this to Y.DD.
- sectionnode.addClass(CSS.SECTIONDRAGGABLE);
- }
- }
- }, this);
- },
-
- /*
- * Drag-dropping related functions
- */
- drag_start : function(e) {
- // Get our drag object
- var drag = e.target;
- // Creat a dummy structure of the outer elemnents for clean styles application
- var containernode = Y.Node.create('<'+M.course.format.get_containernode()+'>'+M.course.format.get_containernode()+'>');
- containernode.addClass(M.course.format.get_containerclass());
- var sectionnode = Y.Node.create('<'+ M.course.format.get_sectionwrappernode()+'>'+ M.course.format.get_sectionwrappernode()+'>');
- sectionnode.addClass( M.course.format.get_sectionwrapperclass());
- sectionnode.setStyle('margin', 0);
- sectionnode.setContent(drag.get('node').get('innerHTML'));
- containernode.appendChild(sectionnode);
- drag.get('dragNode').setContent(containernode);
- drag.get('dragNode').addClass(CSS.COURSECONTENT);
- },
-
- drag_dropmiss : function(e) {
- // Missed the target, but we assume the user intended to drop it
- // on the last last ghost node location, e.drag and e.drop should be
- // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
- this.drop_hit(e);
- },
-
- get_section_index: function(node) {
- var sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + M.course.format.get_section_selector(Y),
- sectionList = Y.all(sectionlistselector),
- nodeIndex = sectionList.indexOf(node),
- zeroIndex = sectionList.indexOf(Y.one('#section-0'));
-
- return (nodeIndex - zeroIndex);
- },
-
- drop_hit : function(e) {
- var drag = e.drag;
-
- // Get references to our nodes and their IDs.
- var dragnode = drag.get('node'),
- dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode),
- loopstart = dragnodeid,
-
- dropnodeindex = this.get_section_index(dragnode),
- loopend = dropnodeindex;
-
- if (dragnodeid === dropnodeindex) {
- Y.log("Skipping move - same location moving " + dragnodeid + " to " + dropnodeindex, 'debug', 'moodle-course-dragdrop');
- return;
- }
-
- Y.log("Moving from position " + dragnodeid + " to position " + dropnodeindex, 'debug', 'moodle-course-dragdrop');
-
- if (loopstart > loopend) {
- // If we're going up, we need to swap the loop order
- // because loops can't go backwards.
- loopstart = dropnodeindex;
- loopend = dragnodeid;
- }
-
- // Get the list of nodes.
- drag.get('dragNode').removeClass(CSS.COURSECONTENT);
- var sectionlist = Y.Node.all(this.sectionlistselector);
-
- // Add a lightbox if it's not there.
- var lightbox = M.util.add_lightbox(Y, dragnode);
-
- // Handle any variables which we must pass via AJAX.
- var params = {},
- pageparams = this.get('config').pageparams,
- varname;
-
- for (varname in pageparams) {
- if (!pageparams.hasOwnProperty(varname)) {
- continue;
- }
- params[varname] = pageparams[varname];
- }
-
- // Prepare request parameters
- params.sesskey = M.cfg.sesskey;
- params.courseId = this.get('courseid');
- params['class'] = 'section';
- params.field = 'move';
- params.id = dragnodeid;
- params.value = dropnodeindex;
-
- // Perform the AJAX request.
- var uri = M.cfg.wwwroot + this.get('ajaxurl');
- Y.io(uri, {
- method: 'POST',
- data: params,
- on: {
- start : function() {
- lightbox.show();
- },
- success: function(tid, response) {
- // Update section titles, we can't simply swap them as
- // they might have custom title
- try {
- var responsetext = Y.JSON.parse(response.responseText);
- if (responsetext.error) {
- new M.core.ajaxException(responsetext);
- }
- M.course.format.process_sections(Y, sectionlist, responsetext, loopstart, loopend);
- } catch (e) {}
-
- // Update all of the section IDs - first unset them, then set them
- // to avoid duplicates in the DOM.
- var index;
-
- // Classic bubble sort algorithm is applied to the section
- // nodes between original drag node location and the new one.
- var swapped = false;
- do {
- swapped = false;
- for (index = loopstart; index <= loopend; index++) {
- if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) >
- Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) {
- Y.log("Swapping " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) +
- " with " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index)));
- // Swap section id.
- var sectionid = sectionlist.item(index - 1).get('id');
- sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id'));
- sectionlist.item(index).set('id', sectionid);
-
- // See what format needs to swap.
- M.course.format.swap_sections(Y, index - 1, index);
-
- // Update flag.
- swapped = true;
- }
- }
- loopend = loopend - 1;
- } while (swapped);
-
- window.setTimeout(function() {
- lightbox.hide();
- }, 250);
- },
-
- failure: function(tid, response) {
- this.ajax_failure(response);
- lightbox.hide();
- }
- },
- context:this
- });
- }
-
- }, {
- NAME : 'course-dragdrop-section',
- ATTRS : {
- courseid : {
- value : null
- },
- ajaxurl : {
- 'value' : 0
- },
- config : {
- 'value' : 0
- }
- }
- });
-
- var DRAGRESOURCE = function() {
- DRAGRESOURCE.superclass.constructor.apply(this, arguments);
- };
- Y.extend(DRAGRESOURCE, M.core.dragdrop, {
- initializer : function(params) {
- // Set group for parent class
- this.groups = ['resource'];
- this.samenodeclass = CSS.ACTIVITY;
- this.parentnodeclass = CSS.SECTION;
- this.resourcedraghandle = this.get_drag_handle(M.str.moodle.move, CSS.EDITINGMOVE, CSS.ICONCLASS, true);
-
- // Go through all sections
- var sectionlistselector = M.course.format.get_section_selector(Y);
- if (sectionlistselector) {
- sectionlistselector = '.'+CSS.COURSECONTENT+' '+sectionlistselector;
- this.setup_for_section(sectionlistselector);
-
- // Initialise drag & drop for all resources/activities
- var nodeselector = sectionlistselector.slice(CSS.COURSECONTENT.length+2)+' li.'+CSS.ACTIVITY;
- var del = new Y.DD.Delegate({
- container: '.'+CSS.COURSECONTENT,
- nodes: nodeselector,
- target: true,
- handles: ['.' + CSS.EDITINGMOVE],
- dragConfig: {groups: this.groups}
- });
- del.dd.plug(Y.Plugin.DDProxy, {
- // Don't move the node at the end of the drag
- moveOnEnd: false,
- cloneNode: true
- });
- del.dd.plug(Y.Plugin.DDConstrained, {
- // Keep it inside the .course-content
- constrain: '#'+CSS.PAGECONTENT
- });
- del.dd.plug(Y.Plugin.DDWinScroll);
-
- M.course.coursebase.register_module(this);
- M.course.dragres = this;
- }
- },
-
- /**
- * Apply dragdrop features to the specified selector or node that refers to section(s)
- *
- * @param baseselector The CSS selector or node to limit scope to
- * @return void
- */
- setup_for_section : function(baseselector) {
- Y.Node.all(baseselector).each(function(sectionnode) {
- var resources = sectionnode.one('.'+CSS.CONTENT+' ul.'+CSS.SECTION);
- // See if resources ul exists, if not create one
- if (!resources) {
- var resources = Y.Node.create('');
- resources.addClass(CSS.SECTION);
- sectionnode.one('.'+CSS.CONTENT+' div.'+CSS.SUMMARY).insert(resources, 'after');
- }
- resources.setAttribute('data-draggroups', this.groups.join(' '));
- // Define empty ul as droptarget, so that item could be moved to empty list
- var tar = new Y.DD.Drop({
- node: resources,
- groups: this.groups,
- padding: '20 0 20 0'
- });
-
- // Initialise each resource/activity in this section
- this.setup_for_resource('#'+sectionnode.get('id')+' li.'+CSS.ACTIVITY);
- }, this);
- },
- /**
- * Apply dragdrop features to the specified selector or node that refers to resource(s)
- *
- * @param baseselector The CSS selector or node to limit scope to
- * @return void
- */
- setup_for_resource : function(baseselector) {
- Y.Node.all(baseselector).each(function(resourcesnode) {
- // Replace move icons
- var move = resourcesnode.one('a.'+CSS.EDITINGMOVE);
- if (move) {
- move.replace(this.resourcedraghandle.cloneNode(true));
- }
- }, this);
- },
-
- drag_start : function(e) {
- // Get our drag object
- var drag = e.target;
- drag.get('dragNode').setContent(drag.get('node').get('innerHTML'));
- drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline');
- },
-
- drag_dropmiss : function(e) {
- // Missed the target, but we assume the user intended to drop it
- // on the last last ghost node location, e.drag and e.drop should be
- // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
- this.drop_hit(e);
- },
-
- drop_hit : function(e) {
- var drag = e.drag;
- // Get a reference to our drag node
- var dragnode = drag.get('node');
- var dropnode = e.drop.get('node');
-
- // Add spinner if it not there
- var actionarea = dragnode.one(CSS.ACTIONAREA);
- var spinner = M.util.add_spinner(Y, actionarea);
-
- var params = {};
-
- // Handle any variables which we must pass back through to
- var pageparams = this.get('config').pageparams;
- var varname;
- for (varname in pageparams) {
- params[varname] = pageparams[varname];
- }
-
- // Prepare request parameters
- params.sesskey = M.cfg.sesskey;
- params.courseId = this.get('courseid');
- params['class'] = 'resource';
- params.field = 'move';
- params.id = Number(Y.Moodle.core_course.util.cm.getId(dragnode));
- params.sectionId = Y.Moodle.core_course.util.section.getId(dropnode.ancestor(M.course.format.get_section_wrapper(Y), true));
-
- if (dragnode.next()) {
- params.beforeId = Number(Y.Moodle.core_course.util.cm.getId(dragnode.next()));
- }
-
- // Do AJAX request
- var uri = M.cfg.wwwroot + this.get('ajaxurl');
-
- Y.io(uri, {
- method: 'POST',
- data: params,
- on: {
- start : function(tid) {
- this.lock_drag_handle(drag, CSS.EDITINGMOVE);
- spinner.show();
- },
- success: function(tid, response) {
- var responsetext = Y.JSON.parse(response.responseText);
- var params = {element: dragnode, visible: responsetext.visible};
- M.course.coursebase.invoke_function('set_visibility_resource_ui', params);
- this.unlock_drag_handle(drag, CSS.EDITINGMOVE);
- window.setTimeout(function(e) {
- spinner.hide();
- }, 250);
- },
- failure: function(tid, response) {
- this.ajax_failure(response);
- this.unlock_drag_handle(drag, CSS.SECTIONHANDLE);
- spinner.hide();
- // TODO: revert nodes location
- }
- },
- context:this
- });
- }
- }, {
- NAME : 'course-dragdrop-resource',
- ATTRS : {
- courseid : {
- value : null
- },
- ajaxurl : {
- 'value' : 0
- },
- config : {
- 'value' : 0
- }
- }
- });
-
- M.course = M.course || {};
- M.course.init_resource_dragdrop = function(params) {
- new DRAGRESOURCE(params);
- }
- M.course.init_section_dragdrop = function(params) {
- new DRAGSECTION(params);
- }
-}, '@VERSION@', {requires:['base', 'node', 'io', 'dom', 'dd', 'dd-scroll', 'moodle-core-dragdrop', 'moodle-core-notification', 'moodle-course-coursebase', 'moodle-course-util']});
diff --git a/course/yui/src/dragdrop/build.json b/course/yui/src/dragdrop/build.json
new file mode 100644
index 00000000000..fb8e3cef30e
--- /dev/null
+++ b/course/yui/src/dragdrop/build.json
@@ -0,0 +1,12 @@
+{
+ "name": "moodle-course-dragdrop",
+ "builds": {
+ "moodle-course-dragdrop": {
+ "jsfiles": [
+ "dragdrop.js",
+ "section.js",
+ "resource.js"
+ ]
+ }
+ }
+}
diff --git a/course/yui/src/dragdrop/js/dragdrop.js b/course/yui/src/dragdrop/js/dragdrop.js
new file mode 100644
index 00000000000..00b30d4a493
--- /dev/null
+++ b/course/yui/src/dragdrop/js/dragdrop.js
@@ -0,0 +1,29 @@
+/**
+ * Drag and Drop for course sections and course modules.
+ *
+ * @module moodle-course-dragdrop
+ */
+
+var CSS = {
+ ACTIONAREA: '.actions',
+ ACTIVITY: 'activity',
+ ACTIVITYINSTANCE: 'activityinstance',
+ CONTENT: 'content',
+ COURSECONTENT: 'course-content',
+ EDITINGMOVE: 'editing_move',
+ ICONCLASS: 'iconsmall',
+ JUMPMENU: 'jumpmenu',
+ LEFT: 'left',
+ LIGHTBOX: 'lightbox',
+ MOVEDOWN: 'movedown',
+ MOVEUP: 'moveup',
+ PAGECONTENT: 'page-content',
+ RIGHT: 'right',
+ SECTION: 'section',
+ SECTIONADDMENUS: 'section_add_menus',
+ SECTIONHANDLE: 'section-handle',
+ SUMMARY: 'summary',
+ SECTIONDRAGGABLE: 'sectiondraggable'
+};
+
+M.course = M.course || {};
diff --git a/course/yui/src/dragdrop/js/resource.js b/course/yui/src/dragdrop/js/resource.js
new file mode 100644
index 00000000000..6523e8bbf24
--- /dev/null
+++ b/course/yui/src/dragdrop/js/resource.js
@@ -0,0 +1,187 @@
+/**
+ * Resource drag and drop.
+ *
+ * @class M.course.dragdrop.resource
+ * @constructor
+ * @extends M.core.dragdrop
+ */
+var DRAGRESOURCE = function() {
+ DRAGRESOURCE.superclass.constructor.apply(this, arguments);
+};
+Y.extend(DRAGRESOURCE, M.core.dragdrop, {
+ initializer: function() {
+ // Set group for parent class
+ this.groups = ['resource'];
+ this.samenodeclass = CSS.ACTIVITY;
+ this.parentnodeclass = CSS.SECTION;
+ this.resourcedraghandle = this.get_drag_handle(M.str.moodle.move, CSS.EDITINGMOVE, CSS.ICONCLASS, true);
+
+ // Go through all sections
+ var sectionlistselector = M.course.format.get_section_selector(Y);
+ if (sectionlistselector) {
+ sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + sectionlistselector;
+ this.setup_for_section(sectionlistselector);
+
+ // Initialise drag & drop for all resources/activities
+ var nodeselector = sectionlistselector.slice(CSS.COURSECONTENT.length + 2) + ' li.' + CSS.ACTIVITY;
+ var del = new Y.DD.Delegate({
+ container: '.' + CSS.COURSECONTENT,
+ nodes: nodeselector,
+ target: true,
+ handles: ['.' + CSS.EDITINGMOVE],
+ dragConfig: {groups: this.groups}
+ });
+ del.dd.plug(Y.Plugin.DDProxy, {
+ // Don't move the node at the end of the drag
+ moveOnEnd: false,
+ cloneNode: true
+ });
+ del.dd.plug(Y.Plugin.DDConstrained, {
+ // Keep it inside the .course-content
+ constrain: '#' + CSS.PAGECONTENT
+ });
+ del.dd.plug(Y.Plugin.DDWinScroll);
+
+ M.course.coursebase.register_module(this);
+ M.course.dragres = this;
+ }
+ },
+
+ /**
+ * Apply dragdrop features to the specified selector or node that refers to section(s)
+ *
+ * @method setup_for_section
+ * @param {String} baseselector The CSS selector or node to limit scope to
+ */
+ setup_for_section: function(baseselector) {
+ Y.Node.all(baseselector).each(function(sectionnode) {
+ var resources = sectionnode.one('.' + CSS.CONTENT + ' ul.' + CSS.SECTION);
+ // See if resources ul exists, if not create one
+ if (!resources) {
+ resources = Y.Node.create('');
+ resources.addClass(CSS.SECTION);
+ sectionnode.one('.' + CSS.CONTENT + ' div.' + CSS.SUMMARY).insert(resources, 'after');
+ }
+ resources.setAttribute('data-draggroups', this.groups.join(' '));
+ // Define empty ul as droptarget, so that item could be moved to empty list
+ new Y.DD.Drop({
+ node: resources,
+ groups: this.groups,
+ padding: '20 0 20 0'
+ });
+
+ // Initialise each resource/activity in this section
+ this.setup_for_resource('#' + sectionnode.get('id') + ' li.' + CSS.ACTIVITY);
+ }, this);
+ },
+
+ /**
+ * Apply dragdrop features to the specified selector or node that refers to resource(s)
+ *
+ * @method setup_for_resource
+ * @param {String} baseselector The CSS selector or node to limit scope to
+ */
+ setup_for_resource: function(baseselector) {
+ Y.Node.all(baseselector).each(function(resourcesnode) {
+ // Replace move icons
+ var move = resourcesnode.one('a.' + CSS.EDITINGMOVE);
+ if (move) {
+ move.replace(this.resourcedraghandle.cloneNode(true));
+ }
+ }, this);
+ },
+
+ drag_start: function(e) {
+ // Get our drag object
+ var drag = e.target;
+ drag.get('dragNode').setContent(drag.get('node').get('innerHTML'));
+ drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline');
+ },
+
+ drag_dropmiss: function(e) {
+ // Missed the target, but we assume the user intended to drop it
+ // on the last last ghost node location, e.drag and e.drop should be
+ // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
+ this.drop_hit(e);
+ },
+
+ drop_hit: function(e) {
+ var drag = e.drag;
+ // Get a reference to our drag node
+ var dragnode = drag.get('node');
+ var dropnode = e.drop.get('node');
+
+ // Add spinner if it not there
+ var actionarea = dragnode.one(CSS.ACTIONAREA);
+ var spinner = M.util.add_spinner(Y, actionarea);
+
+ var params = {};
+
+ // Handle any variables which we must pass back through to
+ var pageparams = this.get('config').pageparams;
+ var varname;
+ for (varname in pageparams) {
+ params[varname] = pageparams[varname];
+ }
+
+ // Prepare request parameters
+ params.sesskey = M.cfg.sesskey;
+ params.courseId = this.get('courseid');
+ params['class'] = 'resource';
+ params.field = 'move';
+ params.id = Number(Y.Moodle.core_course.util.cm.getId(dragnode));
+ params.sectionId = Y.Moodle.core_course.util.section.getId(dropnode.ancestor(M.course.format.get_section_wrapper(Y), true));
+
+ if (dragnode.next()) {
+ params.beforeId = Number(Y.Moodle.core_course.util.cm.getId(dragnode.next()));
+ }
+
+ // Do AJAX request
+ var uri = M.cfg.wwwroot + this.get('ajaxurl');
+
+ Y.io(uri, {
+ method: 'POST',
+ data: params,
+ on: {
+ start: function() {
+ this.lock_drag_handle(drag, CSS.EDITINGMOVE);
+ spinner.show();
+ },
+ success: function(tid, response) {
+ var responsetext = Y.JSON.parse(response.responseText);
+ var params = {element: dragnode, visible: responsetext.visible};
+ M.course.coursebase.invoke_function('set_visibility_resource_ui', params);
+ this.unlock_drag_handle(drag, CSS.EDITINGMOVE);
+ window.setTimeout(function() {
+ spinner.hide();
+ }, 250);
+ },
+ failure: function(tid, response) {
+ this.ajax_failure(response);
+ this.unlock_drag_handle(drag, CSS.SECTIONHANDLE);
+ spinner.hide();
+ // TODO: revert nodes location
+ }
+ },
+ context:this
+ });
+ }
+}, {
+ NAME: 'course-dragdrop-resource',
+ ATTRS: {
+ courseid: {
+ value: null
+ },
+ ajaxurl: {
+ value: 0
+ },
+ config: {
+ value: 0
+ }
+ }
+});
+
+M.course = M.course || {};
+M.course.init_resource_dragdrop = function(params) {
+ new DRAGRESOURCE(params);
+};
diff --git a/course/yui/src/dragdrop/js/section.js b/course/yui/src/dragdrop/js/section.js
new file mode 100644
index 00000000000..d1380306f1d
--- /dev/null
+++ b/course/yui/src/dragdrop/js/section.js
@@ -0,0 +1,258 @@
+/**
+ * Section drag and drop.
+ *
+ * @class M.course.dragdrop.section
+ * @constructor
+ * @extends M.core.dragdrop
+ */
+var DRAGSECTION = function() {
+ DRAGSECTION.superclass.constructor.apply(this, arguments);
+};
+Y.extend(DRAGSECTION, M.core.dragdrop, {
+ sectionlistselector: null,
+
+ initializer: function() {
+ // Set group for parent class
+ this.groups = [ CSS.SECTIONDRAGGABLE ];
+ this.samenodeclass = M.course.format.get_sectionwrapperclass();
+ this.parentnodeclass = M.course.format.get_containerclass();
+
+ // Check if we are in single section mode
+ if (Y.Node.one('.' + CSS.JUMPMENU)) {
+ return false;
+ }
+ // Initialise sections dragging
+ this.sectionlistselector = M.course.format.get_section_wrapper(Y);
+ if (this.sectionlistselector) {
+ this.sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + this.sectionlistselector;
+
+ this.setup_for_section(this.sectionlistselector);
+
+ // Make each li element in the lists of sections draggable
+ var del = new Y.DD.Delegate({
+ container: '.' + CSS.COURSECONTENT,
+ nodes: '.' + CSS.SECTIONDRAGGABLE,
+ target: true,
+ handles: ['.' + CSS.LEFT],
+ dragConfig: {groups: this.groups}
+ });
+ del.dd.plug(Y.Plugin.DDProxy, {
+ // Don't move the node at the end of the drag
+ moveOnEnd: false
+ });
+ del.dd.plug(Y.Plugin.DDConstrained, {
+ // Keep it inside the .course-content
+ constrain: '#' + CSS.PAGECONTENT,
+ stickY: true
+ });
+ del.dd.plug(Y.Plugin.DDWinScroll);
+ }
+ },
+
+ /**
+ * Apply dragdrop features to the specified selector or node that refers to section(s)
+ *
+ * @method setup_for_section
+ * @param {String} baseselector The CSS selector or node to limit scope to
+ */
+ setup_for_section: function(baseselector) {
+ Y.Node.all(baseselector).each(function(sectionnode) {
+ // Determine the section ID
+ var sectionid = Y.Moodle.core_course.util.section.getId(sectionnode);
+
+ // We skip the top section as it is not draggable
+ if (sectionid > 0) {
+ // Remove move icons
+ var movedown = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEDOWN);
+ var moveup = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEUP);
+
+ // Add dragger icon
+ var title = M.util.get_string('movesection', 'moodle', sectionid);
+ var cssleft = sectionnode.one('.' + CSS.LEFT);
+
+ if ((movedown || moveup) && cssleft) {
+ cssleft.setStyle('cursor', 'move');
+ cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true));
+
+ if (moveup) {
+ moveup.remove();
+ }
+ if (movedown) {
+ movedown.remove();
+ }
+
+ // This section can be moved - add the class to indicate this to Y.DD.
+ sectionnode.addClass(CSS.SECTIONDRAGGABLE);
+ }
+ }
+ }, this);
+ },
+
+ /*
+ * Drag-dropping related functions
+ */
+ drag_start: function(e) {
+ // Get our drag object
+ var drag = e.target;
+ // Creat a dummy structure of the outer elemnents for clean styles application
+ var containernode = Y.Node.create('<' + M.course.format.get_containernode() + '>' + M.course.format.get_containernode() + '>');
+ containernode.addClass(M.course.format.get_containerclass());
+ var sectionnode = Y.Node.create('<' + M.course.format.get_sectionwrappernode() + '>' + M.course.format.get_sectionwrappernode() + '>');
+ sectionnode.addClass( M.course.format.get_sectionwrapperclass());
+ sectionnode.setStyle('margin', 0);
+ sectionnode.setContent(drag.get('node').get('innerHTML'));
+ containernode.appendChild(sectionnode);
+ drag.get('dragNode').setContent(containernode);
+ drag.get('dragNode').addClass(CSS.COURSECONTENT);
+ },
+
+ drag_dropmiss: function(e) {
+ // Missed the target, but we assume the user intended to drop it
+ // on the last last ghost node location, e.drag and e.drop should be
+ // prepared by global_drag_dropmiss parent so simulate drop_hit(e).
+ this.drop_hit(e);
+ },
+
+ get_section_index: function(node) {
+ var sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + M.course.format.get_section_selector(Y),
+ sectionList = Y.all(sectionlistselector),
+ nodeIndex = sectionList.indexOf(node),
+ zeroIndex = sectionList.indexOf(Y.one('#section-0'));
+
+ return (nodeIndex - zeroIndex);
+ },
+
+ drop_hit: function(e) {
+ var drag = e.drag;
+
+ // Get references to our nodes and their IDs.
+ var dragnode = drag.get('node'),
+ dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode),
+ loopstart = dragnodeid,
+
+ dropnodeindex = this.get_section_index(dragnode),
+ loopend = dropnodeindex;
+
+ if (dragnodeid === dropnodeindex) {
+ Y.log("Skipping move - same location moving " + dragnodeid + " to " + dropnodeindex, 'debug', 'moodle-course-dragdrop');
+ return;
+ }
+
+ Y.log("Moving from position " + dragnodeid + " to position " + dropnodeindex, 'debug', 'moodle-course-dragdrop');
+
+ if (loopstart > loopend) {
+ // If we're going up, we need to swap the loop order
+ // because loops can't go backwards.
+ loopstart = dropnodeindex;
+ loopend = dragnodeid;
+ }
+
+ // Get the list of nodes.
+ drag.get('dragNode').removeClass(CSS.COURSECONTENT);
+ var sectionlist = Y.Node.all(this.sectionlistselector);
+
+ // Add a lightbox if it's not there.
+ var lightbox = M.util.add_lightbox(Y, dragnode);
+
+ // Handle any variables which we must pass via AJAX.
+ var params = {},
+ pageparams = this.get('config').pageparams,
+ varname;
+
+ for (varname in pageparams) {
+ if (!pageparams.hasOwnProperty(varname)) {
+ continue;
+ }
+ params[varname] = pageparams[varname];
+ }
+
+ // Prepare request parameters
+ params.sesskey = M.cfg.sesskey;
+ params.courseId = this.get('courseid');
+ params['class'] = 'section';
+ params.field = 'move';
+ params.id = dragnodeid;
+ params.value = dropnodeindex;
+
+ // Perform the AJAX request.
+ var uri = M.cfg.wwwroot + this.get('ajaxurl');
+ Y.io(uri, {
+ method: 'POST',
+ data: params,
+ on: {
+ start: function() {
+ lightbox.show();
+ },
+ success: function(tid, response) {
+ // Update section titles, we can't simply swap them as
+ // they might have custom title
+ try {
+ var responsetext = Y.JSON.parse(response.responseText);
+ if (responsetext.error) {
+ new M.core.ajaxException(responsetext);
+ }
+ M.course.format.process_sections(Y, sectionlist, responsetext, loopstart, loopend);
+ } catch (e) {}
+
+ // Update all of the section IDs - first unset them, then set them
+ // to avoid duplicates in the DOM.
+ var index;
+
+ // Classic bubble sort algorithm is applied to the section
+ // nodes between original drag node location and the new one.
+ var swapped = false;
+ do {
+ swapped = false;
+ for (index = loopstart; index <= loopend; index++) {
+ if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) >
+ Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) {
+ Y.log("Swapping " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) +
+ " with " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index)));
+ // Swap section id.
+ var sectionid = sectionlist.item(index - 1).get('id');
+ sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id'));
+ sectionlist.item(index).set('id', sectionid);
+
+ // See what format needs to swap.
+ M.course.format.swap_sections(Y, index - 1, index);
+
+ // Update flag.
+ swapped = true;
+ }
+ }
+ loopend = loopend - 1;
+ } while (swapped);
+
+ window.setTimeout(function() {
+ lightbox.hide();
+ }, 250);
+ },
+
+ failure: function(tid, response) {
+ this.ajax_failure(response);
+ lightbox.hide();
+ }
+ },
+ context:this
+ });
+ }
+
+}, {
+ NAME: 'course-dragdrop-section',
+ ATTRS: {
+ courseid: {
+ value: null
+ },
+ ajaxurl: {
+ value: 0
+ },
+ config: {
+ value: 0
+ }
+ }
+});
+
+M.course = M.course || {};
+M.course.init_section_dragdrop = function(params) {
+ new DRAGSECTION(params);
+};
diff --git a/course/yui/src/dragdrop/meta/dragdrop.json b/course/yui/src/dragdrop/meta/dragdrop.json
new file mode 100644
index 00000000000..5757d2822e7
--- /dev/null
+++ b/course/yui/src/dragdrop/meta/dragdrop.json
@@ -0,0 +1,16 @@
+{
+ "moodle-course-dragdrop": {
+ "requires": [
+ "base",
+ "node",
+ "io",
+ "dom",
+ "dd",
+ "dd-scroll",
+ "moodle-core-dragdrop",
+ "moodle-core-notification",
+ "moodle-course-coursebase",
+ "moodle-course-util"
+ ]
+ }
+}