Commit | Line | Data |
---|---|---|
15e2552f RK |
1 | YUI.add('moodle-course-dragdrop', function(Y) { |
2 | ||
3 | var CSS = { | |
4 | ACTIVITY : 'activity', | |
45b364b9 | 5 | COMMANDSPAN : 'span.commands', |
15e2552f RK |
6 | CONTENT : 'content', |
7 | COURSECONTENT : 'course-content', | |
8 | EDITINGMOVE : 'editing_move', | |
9 | ICONCLASS : 'iconsmall', | |
10 | JUMPMENU : 'jumpmenu', | |
11 | LEFT : 'left', | |
12 | LIGHTBOX : 'lightbox', | |
13 | MOVEDOWN : 'movedown', | |
14 | MOVEUP : 'moveup', | |
15 | PAGECONTENT : 'page-content', | |
16 | RIGHT : 'right', | |
17 | SECTION : 'section', | |
18 | SECTIONADDMENUS : 'section_add_menus', | |
19 | SECTIONHANDLE : 'section-handle', | |
405eaac2 | 20 | SUMMARY : 'summary' |
15e2552f RK |
21 | }; |
22 | ||
23 | var DRAGSECTION = function() { | |
24 | DRAGSECTION.superclass.constructor.apply(this, arguments); | |
25 | }; | |
26 | Y.extend(DRAGSECTION, M.core.dragdrop, { | |
c77582fe RK |
27 | sectionlistselector : null, |
28 | ||
15e2552f RK |
29 | initializer : function(params) { |
30 | // Set group for parent class | |
31 | this.groups = ['section']; | |
405eaac2 RK |
32 | this.samenodeclass = M.course.format.get_sectionwrapperclass(); |
33 | this.parentnodeclass = M.course.format.get_containerclass(); | |
15e2552f RK |
34 | |
35 | // Check if we are in single section mode | |
36 | if (Y.Node.one('.'+CSS.JUMPMENU)) { | |
37 | return false; | |
38 | } | |
39 | // Initialise sections dragging | |
405eaac2 RK |
40 | this.sectionlistselector = M.course.format.get_section_wrapper(Y); |
41 | if (this.sectionlistselector) { | |
42 | this.sectionlistselector = '.'+CSS.COURSECONTENT+' '+this.sectionlistselector; | |
d95b77bd | 43 | this.setup_for_section(this.sectionlistselector); |
d2ce9252 PN |
44 | |
45 | // Make each li element in the lists of sections draggable | |
46 | var nodeselector = this.sectionlistselector.slice(CSS.COURSECONTENT.length+2); | |
47 | var del = new Y.DD.Delegate({ | |
48 | container: '.'+CSS.COURSECONTENT, | |
49 | nodes: nodeselector, | |
50 | target: true, | |
51 | handles: ['.'+CSS.LEFT], | |
52 | dragConfig: {groups: this.groups} | |
53 | }); | |
54 | del.dd.plug(Y.Plugin.DDProxy, { | |
55 | // Don't move the node at the end of the drag | |
56 | moveOnEnd: false | |
57 | }); | |
58 | del.dd.plug(Y.Plugin.DDConstrained, { | |
59 | // Keep it inside the .course-content | |
60 | constrain: '#'+CSS.PAGECONTENT, | |
61 | stickY: true | |
62 | }); | |
63 | del.dd.plug(Y.Plugin.DDWinScroll); | |
c77582fe | 64 | } |
15e2552f RK |
65 | }, |
66 | ||
67 | /** | |
68 | * Apply dragdrop features to the specified selector or node that refers to section(s) | |
69 | * | |
70 | * @param baseselector The CSS selector or node to limit scope to | |
71 | * @return void | |
72 | */ | |
73 | setup_for_section : function(baseselector) { | |
74 | Y.Node.all(baseselector).each(function(sectionnode) { | |
75 | // Determine the section ID | |
76 | var sectionid = this.get_section_id(sectionnode); | |
77 | ||
78 | // We skip the top section as it is not draggable | |
79 | if (sectionid > 0) { | |
80 | // Remove move icons | |
81 | var movedown = sectionnode.one('.'+CSS.RIGHT+' a.'+CSS.MOVEDOWN); | |
15e2552f | 82 | var moveup = sectionnode.one('.'+CSS.RIGHT+' a.'+CSS.MOVEUP); |
19d041bf | 83 | |
15e2552f RK |
84 | // Add dragger icon |
85 | var title = M.util.get_string('movesection', 'moodle', sectionid); | |
86 | var cssleft = sectionnode.one('.'+CSS.LEFT); | |
15e2552f | 87 | |
19d041bf ARN |
88 | if ((movedown || moveup) && cssleft) { |
89 | cssleft.setStyle('cursor', 'move'); | |
90 | cssleft.appendChild(Y.Node.create('<br />')); | |
91 | cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE)); | |
92 | ||
93 | if (moveup) { | |
94 | moveup.remove(); | |
95 | } | |
96 | if (movedown) { | |
97 | movedown.remove(); | |
98 | } | |
19d041bf | 99 | } |
15e2552f RK |
100 | } |
101 | }, this); | |
102 | }, | |
103 | ||
104 | get_section_id : function(node) { | |
105 | return Number(node.get('id').replace(/section-/i, '')); | |
106 | }, | |
107 | ||
108 | /* | |
109 | * Drag-dropping related functions | |
110 | */ | |
111 | drag_start : function(e) { | |
112 | // Get our drag object | |
113 | var drag = e.target; | |
114 | // Creat a dummy structure of the outer elemnents for clean styles application | |
405eaac2 RK |
115 | var containernode = Y.Node.create('<'+M.course.format.get_containernode()+'></'+M.course.format.get_containernode()+'>'); |
116 | containernode.addClass(M.course.format.get_containerclass()); | |
117 | var sectionnode = Y.Node.create('<'+ M.course.format.get_sectionwrappernode()+'></'+ M.course.format.get_sectionwrappernode()+'>'); | |
118 | sectionnode.addClass( M.course.format.get_sectionwrapperclass()); | |
119 | sectionnode.setStyle('margin', 0); | |
120 | sectionnode.setContent(drag.get('node').get('innerHTML')); | |
121 | containernode.appendChild(sectionnode); | |
122 | drag.get('dragNode').setContent(containernode); | |
15e2552f RK |
123 | drag.get('dragNode').addClass(CSS.COURSECONTENT); |
124 | }, | |
125 | ||
243e9bf9 RK |
126 | drag_dropmiss : function(e) { |
127 | // Missed the target, but we assume the user intended to drop it | |
128 | // on the last last ghost node location, e.drag and e.drop should be | |
129 | // prepared by global_drag_dropmiss parent so simulate drop_hit(e). | |
130 | this.drop_hit(e); | |
131 | }, | |
132 | ||
15e2552f RK |
133 | drop_hit : function(e) { |
134 | var drag = e.drag; | |
135 | // Get a reference to our drag node | |
136 | var dragnode = drag.get('node'); | |
137 | var dropnode = e.drop.get('node'); | |
138 | // Prepare some variables | |
139 | var dragnodeid = Number(this.get_section_id(dragnode)); | |
140 | var dropnodeid = Number(this.get_section_id(dropnode)); | |
141 | ||
15e2552f RK |
142 | var loopstart = dragnodeid; |
143 | var loopend = dropnodeid; | |
144 | ||
145 | if (this.goingup) { | |
15e2552f RK |
146 | loopstart = dropnodeid; |
147 | loopend = dragnodeid; | |
148 | } | |
149 | ||
150 | // Get the list of nodes | |
151 | drag.get('dragNode').removeClass(CSS.COURSECONTENT); | |
c77582fe | 152 | var sectionlist = Y.Node.all(this.sectionlistselector); |
15e2552f RK |
153 | |
154 | // Add lightbox if it not there | |
155 | var lightbox = M.util.add_lightbox(Y, dragnode); | |
156 | ||
157 | var params = {}; | |
158 | ||
159 | // Handle any variables which we must pass back through to | |
160 | var pageparams = this.get('config').pageparams; | |
161 | for (varname in pageparams) { | |
162 | params[varname] = pageparams[varname]; | |
163 | } | |
164 | ||
165 | // Prepare request parameters | |
166 | params.sesskey = M.cfg.sesskey; | |
167 | params.courseId = this.get('courseid'); | |
168 | params['class'] = 'section'; | |
169 | params.field = 'move'; | |
170 | params.id = dragnodeid; | |
eb01aa2c | 171 | params.value = dropnodeid; |
15e2552f RK |
172 | |
173 | // Do AJAX request | |
174 | var uri = M.cfg.wwwroot + this.get('ajaxurl'); | |
175 | ||
176 | Y.io(uri, { | |
177 | method: 'POST', | |
178 | data: params, | |
179 | on: { | |
180 | start : function(tid) { | |
181 | lightbox.show(); | |
182 | }, | |
183 | success: function(tid, response) { | |
9f3015ec RK |
184 | // Update section titles, we can't simply swap them as |
185 | // they might have custom title | |
186 | try { | |
187 | var responsetext = Y.JSON.parse(response.responseText); | |
188 | if (responsetext.error) { | |
189 | new M.core.ajaxException(responsetext); | |
190 | } | |
191 | M.course.format.process_sections(Y, sectionlist, responsetext, loopstart, loopend); | |
192 | } catch (e) {} | |
193 | ||
15e2552f RK |
194 | // Classic bubble sort algorithm is applied to the section |
195 | // nodes between original drag node location and the new one. | |
196 | do { | |
197 | var swapped = false; | |
198 | for (var i = loopstart; i <= loopend; i++) { | |
199 | if (this.get_section_id(sectionlist.item(i-1)) > this.get_section_id(sectionlist.item(i))) { | |
200 | // Swap section id | |
201 | var sectionid = sectionlist.item(i-1).get('id'); | |
202 | sectionlist.item(i-1).set('id', sectionlist.item(i).get('id')); | |
203 | sectionlist.item(i).set('id', sectionid); | |
405eaac2 RK |
204 | // See what format needs to swap |
205 | M.course.format.swap_sections(Y, i-1, i); | |
15e2552f RK |
206 | // Update flag |
207 | swapped = true; | |
208 | } | |
209 | } | |
210 | loopend = loopend - 1; | |
211 | } while (swapped); | |
9f3015ec RK |
212 | |
213 | // Finally, hide the lightbox | |
214 | window.setTimeout(function(e) { | |
215 | lightbox.hide(); | |
216 | }, 250); | |
15e2552f RK |
217 | }, |
218 | failure: function(tid, response) { | |
219 | this.ajax_failure(response); | |
220 | lightbox.hide(); | |
221 | } | |
222 | }, | |
223 | context:this | |
224 | }); | |
225 | } | |
226 | ||
227 | }, { | |
228 | NAME : 'course-dragdrop-section', | |
229 | ATTRS : { | |
230 | courseid : { | |
231 | value : null | |
232 | }, | |
233 | ajaxurl : { | |
234 | 'value' : 0 | |
235 | }, | |
236 | config : { | |
237 | 'value' : 0 | |
238 | } | |
239 | } | |
240 | }); | |
241 | ||
242 | var DRAGRESOURCE = function() { | |
243 | DRAGRESOURCE.superclass.constructor.apply(this, arguments); | |
244 | }; | |
245 | Y.extend(DRAGRESOURCE, M.core.dragdrop, { | |
246 | initializer : function(params) { | |
247 | // Set group for parent class | |
248 | this.groups = ['resource']; | |
249 | this.samenodeclass = CSS.ACTIVITY; | |
250 | this.parentnodeclass = CSS.SECTION; | |
d2ce9252 | 251 | this.resourcedraghandle = this.get_drag_handle(M.str.moodle.move, CSS.EDITINGMOVE, CSS.ICONCLASS); |
15e2552f RK |
252 | |
253 | // Go through all sections | |
405eaac2 RK |
254 | var sectionlistselector = M.course.format.get_section_selector(Y); |
255 | if (sectionlistselector) { | |
256 | sectionlistselector = '.'+CSS.COURSECONTENT+' '+sectionlistselector; | |
d95b77bd | 257 | this.setup_for_section(sectionlistselector); |
d2ce9252 PN |
258 | |
259 | // Initialise drag & drop for all resources/activities | |
260 | var nodeselector = sectionlistselector.slice(CSS.COURSECONTENT.length+2)+' li.'+CSS.ACTIVITY; | |
261 | var del = new Y.DD.Delegate({ | |
262 | container: '.'+CSS.COURSECONTENT, | |
263 | nodes: nodeselector, | |
264 | target: true, | |
265 | handles: ['.' + CSS.EDITINGMOVE], | |
266 | dragConfig: {groups: this.groups} | |
267 | }); | |
268 | del.dd.plug(Y.Plugin.DDProxy, { | |
269 | // Don't move the node at the end of the drag | |
270 | moveOnEnd: false | |
271 | }); | |
272 | del.dd.plug(Y.Plugin.DDConstrained, { | |
273 | // Keep it inside the .course-content | |
274 | constrain: '#'+CSS.PAGECONTENT | |
275 | }); | |
276 | del.dd.plug(Y.Plugin.DDWinScroll); | |
277 | ||
d95b77bd RK |
278 | M.course.coursebase.register_module(this); |
279 | M.course.dragres = this; | |
c77582fe | 280 | } |
15e2552f RK |
281 | }, |
282 | ||
283 | /** | |
284 | * Apply dragdrop features to the specified selector or node that refers to section(s) | |
285 | * | |
286 | * @param baseselector The CSS selector or node to limit scope to | |
287 | * @return void | |
288 | */ | |
289 | setup_for_section : function(baseselector) { | |
290 | Y.Node.all(baseselector).each(function(sectionnode) { | |
291 | var resources = sectionnode.one('.'+CSS.CONTENT+' ul.'+CSS.SECTION); | |
292 | // See if resources ul exists, if not create one | |
293 | if (!resources) { | |
294 | var resources = Y.Node.create('<ul></ul>'); | |
295 | resources.addClass(CSS.SECTION); | |
296 | sectionnode.one('.'+CSS.CONTENT+' div.'+CSS.SUMMARY).insert(resources, 'after'); | |
297 | } | |
9d7525fb RK |
298 | // Define empty ul as droptarget, so that item could be moved to empty list |
299 | var tar = new Y.DD.Drop({ | |
300 | node: resources, | |
301 | groups: this.groups, | |
302 | padding: '20 0 20 0' | |
303 | }); | |
15e2552f | 304 | |
d2ce9252 | 305 | // Initialise each resource/activity in this section |
405eaac2 | 306 | this.setup_for_resource('#'+sectionnode.get('id')+' li.'+CSS.ACTIVITY); |
15e2552f RK |
307 | }, this); |
308 | }, | |
309 | /** | |
310 | * Apply dragdrop features to the specified selector or node that refers to resource(s) | |
311 | * | |
312 | * @param baseselector The CSS selector or node to limit scope to | |
313 | * @return void | |
314 | */ | |
315 | setup_for_resource : function(baseselector) { | |
316 | Y.Node.all(baseselector).each(function(resourcesnode) { | |
317 | // Replace move icons | |
318 | var move = resourcesnode.one('a.'+CSS.EDITINGMOVE); | |
319 | if (move) { | |
d2ce9252 | 320 | move.replace(this.resourcedraghandle.cloneNode(true)); |
15e2552f RK |
321 | } |
322 | }, this); | |
323 | }, | |
324 | ||
325 | get_section_id : function(node) { | |
326 | return Number(node.get('id').replace(/section-/i, '')); | |
327 | }, | |
328 | ||
329 | get_resource_id : function(node) { | |
330 | return Number(node.get('id').replace(/module-/i, '')); | |
331 | }, | |
332 | ||
333 | drag_start : function(e) { | |
334 | // Get our drag object | |
335 | var drag = e.target; | |
336 | drag.get('dragNode').setContent(drag.get('node').get('innerHTML')); | |
337 | drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline'); | |
338 | }, | |
339 | ||
243e9bf9 RK |
340 | drag_dropmiss : function(e) { |
341 | // Missed the target, but we assume the user intended to drop it | |
342 | // on the last last ghost node location, e.drag and e.drop should be | |
343 | // prepared by global_drag_dropmiss parent so simulate drop_hit(e). | |
344 | this.drop_hit(e); | |
345 | }, | |
346 | ||
15e2552f RK |
347 | drop_hit : function(e) { |
348 | var drag = e.drag; | |
349 | // Get a reference to our drag node | |
350 | var dragnode = drag.get('node'); | |
351 | var dropnode = e.drop.get('node'); | |
352 | ||
45b364b9 ARN |
353 | // Add spinner if it not there |
354 | var spinner = M.util.add_spinner(Y, dragnode.one(CSS.COMMANDSPAN)); | |
355 | ||
15e2552f RK |
356 | var params = {}; |
357 | ||
358 | // Handle any variables which we must pass back through to | |
359 | var pageparams = this.get('config').pageparams; | |
360 | for (varname in pageparams) { | |
361 | params[varname] = pageparams[varname]; | |
362 | } | |
363 | ||
364 | // Prepare request parameters | |
365 | params.sesskey = M.cfg.sesskey; | |
366 | params.courseId = this.get('courseid'); | |
367 | params['class'] = 'resource'; | |
368 | params.field = 'move'; | |
369 | params.id = Number(this.get_resource_id(dragnode)); | |
405eaac2 | 370 | params.sectionId = this.get_section_id(dropnode.ancestor(M.course.format.get_section_wrapper(Y), true)); |
15e2552f RK |
371 | |
372 | if (dragnode.next()) { | |
373 | params.beforeId = Number(this.get_resource_id(dragnode.next())); | |
374 | } | |
375 | ||
376 | // Do AJAX request | |
377 | var uri = M.cfg.wwwroot + this.get('ajaxurl'); | |
378 | ||
379 | Y.io(uri, { | |
380 | method: 'POST', | |
381 | data: params, | |
382 | on: { | |
383 | start : function(tid) { | |
384 | this.lock_drag_handle(drag, CSS.EDITINGMOVE); | |
45b364b9 | 385 | spinner.show(); |
15e2552f RK |
386 | }, |
387 | success: function(tid, response) { | |
388 | this.unlock_drag_handle(drag, CSS.EDITINGMOVE); | |
45b364b9 ARN |
389 | window.setTimeout(function(e) { |
390 | spinner.hide(); | |
391 | }, 250); | |
15e2552f RK |
392 | }, |
393 | failure: function(tid, response) { | |
394 | this.ajax_failure(response); | |
395 | this.unlock_drag_handle(drag, CSS.SECTIONHANDLE); | |
45b364b9 | 396 | spinner.hide(); |
15e2552f RK |
397 | // TODO: revert nodes location |
398 | } | |
399 | }, | |
400 | context:this | |
401 | }); | |
402 | } | |
403 | }, { | |
404 | NAME : 'course-dragdrop-resource', | |
405 | ATTRS : { | |
406 | courseid : { | |
407 | value : null | |
408 | }, | |
409 | ajaxurl : { | |
410 | 'value' : 0 | |
411 | }, | |
412 | config : { | |
413 | 'value' : 0 | |
414 | } | |
415 | } | |
416 | }); | |
417 | ||
c77582fe RK |
418 | M.course = M.course || {}; |
419 | M.course.init_resource_dragdrop = function(params) { | |
15e2552f RK |
420 | new DRAGRESOURCE(params); |
421 | } | |
c77582fe | 422 | M.course.init_section_dragdrop = function(params) { |
15e2552f RK |
423 | new DRAGSECTION(params); |
424 | } | |
d2a27ab0 | 425 | }, '@VERSION@', {requires:['base', 'node', 'io', 'dom', 'dd', 'dd-scroll', 'moodle-core-dragdrop', 'moodle-core-notification', 'moodle-course-coursebase']}); |