1 YUI.add('moodle-course-dragdrop', function(Y) {
5 COMMANDSPAN : 'span.commands',
7 COURSECONTENT : 'course-content',
8 EDITINGMOVE : 'editing_move',
9 ICONCLASS : 'iconsmall',
10 JUMPMENU : 'jumpmenu',
12 LIGHTBOX : 'lightbox',
13 MOVEDOWN : 'movedown',
15 PAGECONTENT : 'page-content',
18 SECTIONADDMENUS : 'section_add_menus',
19 SECTIONHANDLE : 'section-handle',
22 WEEKDATES: 'weekdates'
25 var DRAGSECTION = function() {
26 DRAGSECTION.superclass.constructor.apply(this, arguments);
28 Y.extend(DRAGSECTION, M.core.dragdrop, {
29 sectionlistselector : null,
31 initializer : function(params) {
32 // Set group for parent class
33 this.groups = ['section'];
34 this.samenodeclass = CSS.SECTION;
35 this.parentnodeclass = CSS.TOPICS;
37 // Check if we are in single section mode
38 if (Y.Node.one('.'+CSS.JUMPMENU)) {
41 // Initialise sections dragging
42 if (M.course.format && M.course.format.get_section_selector && typeof(M.course.format.get_section_selector) == 'function') {
43 this.sectionlistselector = '.'+CSS.COURSECONTENT+' '+M.course.format.get_section_selector(Y);
44 this.setup_for_section(this.sectionlistselector);
49 * Apply dragdrop features to the specified selector or node that refers to section(s)
51 * @param baseselector The CSS selector or node to limit scope to
54 setup_for_section : function(baseselector) {
55 Y.Node.all(baseselector).each(function(sectionnode) {
56 // Determine the section ID
57 var sectionid = this.get_section_id(sectionnode);
59 // We skip the top section as it is not draggable
62 var movedown = sectionnode.one('.'+CSS.RIGHT+' a.'+CSS.MOVEDOWN);
63 var moveup = sectionnode.one('.'+CSS.RIGHT+' a.'+CSS.MOVEUP);
66 var title = M.util.get_string('movesection', 'moodle', sectionid);
67 var cssleft = sectionnode.one('.'+CSS.LEFT);
69 if ((movedown || moveup) && cssleft) {
70 cssleft.setStyle('cursor', 'move');
71 cssleft.appendChild(Y.Node.create('<br />'));
72 cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE));
81 // Make each li element in the lists of sections draggable
82 var dd = new Y.DD.Drag({
84 // Make each li a Drop target too
87 handles: ['.'+CSS.LEFT]
88 }).plug(Y.Plugin.DDProxy, {
89 // Don't move the node at the end of the drag
91 }).plug(Y.Plugin.DDConstrained, {
92 // Keep it inside the .course-content
93 constrain: '#'+CSS.PAGECONTENT,
101 get_section_id : function(node) {
102 return Number(node.get('id').replace(/section-/i, ''));
106 * Drag-dropping related functions
108 drag_start : function(e) {
109 // Get our drag object
111 // Creat a dummy structure of the outer elemnents for clean styles application
112 var ul = Y.Node.create('<ul></ul>');
113 ul.addClass(CSS.TOPICS);
114 var li = Y.Node.create('<li></li>');
115 li.addClass(CSS.SECTION);
116 li.setStyle('margin', 0);
117 li.setContent(drag.get('node').get('innerHTML'));
119 drag.get('dragNode').setContent(ul);
120 drag.get('dragNode').addClass(CSS.COURSECONTENT);
123 drop_hit : function(e) {
125 // Get a reference to our drag node
126 var dragnode = drag.get('node');
127 var dropnode = e.drop.get('node');
128 // Prepare some variables
129 var dragnodeid = Number(this.get_section_id(dragnode));
130 var dropnodeid = Number(this.get_section_id(dropnode));
132 var targetoffset = 0;
133 var loopstart = dragnodeid;
134 var loopend = dropnodeid;
138 loopstart = dropnodeid;
139 loopend = dragnodeid;
142 // Get the list of nodes
143 drag.get('dragNode').removeClass(CSS.COURSECONTENT);
144 var sectionlist = Y.Node.all(this.sectionlistselector);
146 // Add lightbox if it not there
147 var lightbox = M.util.add_lightbox(Y, dragnode);
151 // Handle any variables which we must pass back through to
152 var pageparams = this.get('config').pageparams;
153 for (varname in pageparams) {
154 params[varname] = pageparams[varname];
157 // Prepare request parameters
158 params.sesskey = M.cfg.sesskey;
159 params.courseId = this.get('courseid');
160 params['class'] = 'section';
161 params.field = 'move';
162 params.id = dragnodeid;
163 params.value = dropnodeid - targetoffset;
166 var uri = M.cfg.wwwroot + this.get('ajaxurl');
172 start : function(tid) {
175 success: function(tid, response) {
176 window.setTimeout(function(e) {
179 // Classic bubble sort algorithm is applied to the section
180 // nodes between original drag node location and the new one.
183 for (var i = loopstart; i <= loopend; i++) {
184 if (this.get_section_id(sectionlist.item(i-1)) > this.get_section_id(sectionlist.item(i))) {
186 var sectionid = sectionlist.item(i-1).get('id');
187 sectionlist.item(i-1).set('id', sectionlist.item(i).get('id'));
188 sectionlist.item(i).set('id', sectionid);
189 // See what format needs to be swapped
190 if (M.course.format && M.course.format.swap_sections && typeof(M.course.format.swap_sections) == 'function') {
191 M.course.format.swap_sections(Y, i-1, i);
197 loopend = loopend - 1;
200 failure: function(tid, response) {
201 this.ajax_failure(response);
210 NAME : 'course-dragdrop-section',
224 var DRAGRESOURCE = function() {
225 DRAGRESOURCE.superclass.constructor.apply(this, arguments);
227 Y.extend(DRAGRESOURCE, M.core.dragdrop, {
228 initializer : function(params) {
229 // Set group for parent class
230 this.groups = ['resource'];
231 this.samenodeclass = CSS.ACTIVITY;
232 this.parentnodeclass = CSS.SECTION;
234 // Go through all sections
235 if (M.course.format && M.course.format.get_section_selector && typeof(M.course.format.get_section_selector) == 'function') {
236 var sectionlistselector = '.'+CSS.COURSECONTENT+' '+M.course.format.get_section_selector(Y);
237 this.setup_for_section(sectionlistselector);
238 M.course.coursebase.register_module(this);
239 M.course.dragres = this;
244 * Apply dragdrop features to the specified selector or node that refers to section(s)
246 * @param baseselector The CSS selector or node to limit scope to
249 setup_for_section : function(baseselector) {
250 Y.Node.all(baseselector).each(function(sectionnode) {
251 var resources = sectionnode.one('.'+CSS.CONTENT+' ul.'+CSS.SECTION);
252 // See if resources ul exists, if not create one
254 var resources = Y.Node.create('<ul></ul>');
255 resources.addClass(CSS.SECTION);
256 sectionnode.one('.'+CSS.CONTENT+' div.'+CSS.SUMMARY).insert(resources, 'after');
259 // Define each ul as droptarget, so that item could be moved to empty list
260 var tar = new Y.DD.Drop({
265 // Go through each li element and make them draggable
266 this.setup_for_resource('li#'+sectionnode.get('id')+' li.'+CSS.ACTIVITY);
270 * Apply dragdrop features to the specified selector or node that refers to resource(s)
272 * @param baseselector The CSS selector or node to limit scope to
275 setup_for_resource : function(baseselector) {
276 Y.Node.all(baseselector).each(function(resourcesnode) {
277 // Replace move icons
278 var move = resourcesnode.one('a.'+CSS.EDITINGMOVE);
280 move.replace(this.get_drag_handle(M.str.moodle.move, CSS.EDITINGMOVE, CSS.ICONCLASS));
281 // Make each li element in the lists of sections draggable
282 var dd = new Y.DD.Drag({
285 // Make each li a Drop target too
287 handles: ['.' + CSS.EDITINGMOVE]
288 }).plug(Y.Plugin.DDProxy, {
289 // Don't move the node at the end of the drag
291 }).plug(Y.Plugin.DDConstrained, {
292 // Keep it inside the .course-content
293 constrain: '#'+CSS.PAGECONTENT
299 get_section_id : function(node) {
300 return Number(node.get('id').replace(/section-/i, ''));
303 get_resource_id : function(node) {
304 return Number(node.get('id').replace(/module-/i, ''));
307 drag_start : function(e) {
308 // Get our drag object
310 drag.get('dragNode').setContent(drag.get('node').get('innerHTML'));
311 drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline');
314 drop_hit : function(e) {
316 // Get a reference to our drag node
317 var dragnode = drag.get('node');
318 var dropnode = e.drop.get('node');
320 var sectionselector = M.course.format.get_section_selector(Y);
322 // Add spinner if it not there
323 var spinner = M.util.add_spinner(Y, dragnode.one(CSS.COMMANDSPAN));
327 // Handle any variables which we must pass back through to
328 var pageparams = this.get('config').pageparams;
329 for (varname in pageparams) {
330 params[varname] = pageparams[varname];
333 // Prepare request parameters
334 params.sesskey = M.cfg.sesskey;
335 params.courseId = this.get('courseid');
336 params['class'] = 'resource';
337 params.field = 'move';
338 params.id = Number(this.get_resource_id(dragnode));
339 params.sectionId = this.get_section_id(dropnode.ancestor(sectionselector));
341 if (dragnode.next()) {
342 params.beforeId = Number(this.get_resource_id(dragnode.next()));
346 var uri = M.cfg.wwwroot + this.get('ajaxurl');
352 start : function(tid) {
353 this.lock_drag_handle(drag, CSS.EDITINGMOVE);
356 success: function(tid, response) {
357 this.unlock_drag_handle(drag, CSS.EDITINGMOVE);
358 window.setTimeout(function(e) {
362 failure: function(tid, response) {
363 this.ajax_failure(response);
364 this.unlock_drag_handle(drag, CSS.SECTIONHANDLE);
366 // TODO: revert nodes location
373 NAME : 'course-dragdrop-resource',
387 M.course = M.course || {};
388 M.course.init_resource_dragdrop = function(params) {
389 new DRAGRESOURCE(params);
391 M.course.init_section_dragdrop = function(params) {
392 new DRAGSECTION(params);
394 }, '@VERSION@', {requires:['base', 'node', 'io', 'dom', 'dd', 'moodle-core-dragdrop', 'moodle-enrol-notification', 'moodle-course-coursebase']});