[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 YUI.add('moodle-course-dragdrop', function (Y, NAME) { 2 3 /* eslint-disable no-unused-vars */ 4 /** 5 * Drag and Drop for course sections and course modules. 6 * 7 * @module moodle-course-dragdrop 8 */ 9 10 var CSS = { 11 ACTIONAREA: '.actions', 12 ACTIVITY: 'activity', 13 ACTIVITYINSTANCE: 'activityinstance', 14 CONTENT: 'content', 15 COURSECONTENT: 'course-content', 16 EDITINGMOVE: 'editing_move', 17 ICONCLASS: 'iconsmall', 18 JUMPMENU: 'jumpmenu', 19 LEFT: 'left', 20 LIGHTBOX: 'lightbox', 21 MOVEDOWN: 'movedown', 22 MOVEUP: 'moveup', 23 PAGECONTENT: 'page-content', 24 RIGHT: 'right', 25 SECTION: 'section', 26 SECTIONADDMENUS: 'section_add_menus', 27 SECTIONHANDLE: 'section-handle', 28 SUMMARY: 'summary', 29 SECTIONDRAGGABLE: 'sectiondraggable' 30 }; 31 32 M.course = M.course || {}; 33 /** 34 * Section drag and drop. 35 * 36 * @class M.course.dragdrop.section 37 * @constructor 38 * @extends M.core.dragdrop 39 */ 40 var DRAGSECTION = function() { 41 DRAGSECTION.superclass.constructor.apply(this, arguments); 42 }; 43 Y.extend(DRAGSECTION, M.core.dragdrop, { 44 sectionlistselector: null, 45 46 initializer: function() { 47 // Set group for parent class 48 this.groups = [CSS.SECTIONDRAGGABLE]; 49 this.samenodeclass = M.course.format.get_sectionwrapperclass(); 50 this.parentnodeclass = M.course.format.get_containerclass(); 51 52 // Check if we are in single section mode 53 if (Y.Node.one('.' + CSS.JUMPMENU)) { 54 return false; 55 } 56 // Initialise sections dragging 57 this.sectionlistselector = M.course.format.get_section_wrapper(Y); 58 if (this.sectionlistselector) { 59 this.sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + this.sectionlistselector; 60 61 this.setup_for_section(this.sectionlistselector); 62 63 // Make each li element in the lists of sections draggable 64 var del = new Y.DD.Delegate({ 65 container: '.' + CSS.COURSECONTENT, 66 nodes: '.' + CSS.SECTIONDRAGGABLE, 67 target: true, 68 handles: ['.' + CSS.LEFT], 69 dragConfig: {groups: this.groups} 70 }); 71 del.dd.plug(Y.Plugin.DDProxy, { 72 // Don't move the node at the end of the drag 73 moveOnEnd: false 74 }); 75 del.dd.plug(Y.Plugin.DDConstrained, { 76 // Keep it inside the .course-content 77 constrain: '#' + CSS.PAGECONTENT, 78 stickY: true 79 }); 80 del.dd.plug(Y.Plugin.DDWinScroll); 81 } 82 }, 83 84 /** 85 * Apply dragdrop features to the specified selector or node that refers to section(s) 86 * 87 * @method setup_for_section 88 * @param {String} baseselector The CSS selector or node to limit scope to 89 */ 90 setup_for_section: function(baseselector) { 91 Y.Node.all(baseselector).each(function(sectionnode) { 92 // Determine the section ID 93 var sectionid = Y.Moodle.core_course.util.section.getId(sectionnode); 94 95 // We skip the top section as it is not draggable 96 if (sectionid > 0) { 97 // Remove move icons 98 var movedown = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEDOWN); 99 var moveup = sectionnode.one('.' + CSS.RIGHT + ' a.' + CSS.MOVEUP); 100 101 // Add dragger icon 102 var title = M.util.get_string('movesection', 'moodle', sectionid); 103 var cssleft = sectionnode.one('.' + CSS.LEFT); 104 105 if ((movedown || moveup) && cssleft) { 106 cssleft.setStyle('cursor', 'move'); 107 cssleft.appendChild(this.get_drag_handle(title, CSS.SECTIONHANDLE, 'icon', true)); 108 109 if (moveup) { 110 if (moveup.previous('br')) { 111 moveup.previous('br').remove(); 112 } else if (moveup.next('br')) { 113 moveup.next('br').remove(); 114 } 115 116 if (moveup.ancestor('.section_action_menu')) { 117 moveup.ancestor('li').remove(); 118 } else { 119 moveup.remove(); 120 } 121 } 122 if (movedown) { 123 if (movedown.previous('br')) { 124 movedown.previous('br').remove(); 125 } else if (movedown.next('br')) { 126 movedown.next('br').remove(); 127 } 128 129 if (movedown.ancestor('.section_action_menu')) { 130 movedown.ancestor('li').remove(); 131 } else { 132 movedown.remove(); 133 } 134 } 135 136 // This section can be moved - add the class to indicate this to Y.DD. 137 sectionnode.addClass(CSS.SECTIONDRAGGABLE); 138 } 139 } 140 }, this); 141 }, 142 143 /* 144 * Drag-dropping related functions 145 */ 146 drag_start: function(e) { 147 // Get our drag object 148 var drag = e.target; 149 // Creat a dummy structure of the outer elemnents for clean styles application 150 var containernode = Y.Node.create('<' + M.course.format.get_containernode() + 151 '></' + M.course.format.get_containernode() + '>'); 152 containernode.addClass(M.course.format.get_containerclass()); 153 var sectionnode = Y.Node.create('<' + M.course.format.get_sectionwrappernode() + 154 '></' + M.course.format.get_sectionwrappernode() + '>'); 155 sectionnode.addClass(M.course.format.get_sectionwrapperclass()); 156 sectionnode.setStyle('margin', 0); 157 sectionnode.setContent(drag.get('node').get('innerHTML')); 158 containernode.appendChild(sectionnode); 159 drag.get('dragNode').setContent(containernode); 160 drag.get('dragNode').addClass(CSS.COURSECONTENT); 161 }, 162 163 drag_dropmiss: function(e) { 164 // Missed the target, but we assume the user intended to drop it 165 // on the last last ghost node location, e.drag and e.drop should be 166 // prepared by global_drag_dropmiss parent so simulate drop_hit(e). 167 this.drop_hit(e); 168 }, 169 170 get_section_index: function(node) { 171 var sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + M.course.format.get_section_selector(Y), 172 sectionList = Y.all(sectionlistselector), 173 nodeIndex = sectionList.indexOf(node), 174 zeroIndex = sectionList.indexOf(Y.one('#section-0')); 175 176 return (nodeIndex - zeroIndex); 177 }, 178 179 drop_hit: function(e) { 180 var drag = e.drag; 181 182 // Get references to our nodes and their IDs. 183 var dragnode = drag.get('node'), 184 dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode), 185 loopstart = dragnodeid, 186 187 dropnodeindex = this.get_section_index(dragnode), 188 loopend = dropnodeindex; 189 190 if (dragnodeid === dropnodeindex) { 191 Y.log("Skipping move - same location moving " + dragnodeid + " to " + dropnodeindex, 'debug', 'moodle-course-dragdrop'); 192 return; 193 } 194 195 Y.log("Moving from position " + dragnodeid + " to position " + dropnodeindex, 'debug', 'moodle-course-dragdrop'); 196 197 if (loopstart > loopend) { 198 // If we're going up, we need to swap the loop order 199 // because loops can't go backwards. 200 loopstart = dropnodeindex; 201 loopend = dragnodeid; 202 } 203 204 // Get the list of nodes. 205 drag.get('dragNode').removeClass(CSS.COURSECONTENT); 206 var sectionlist = Y.Node.all(this.sectionlistselector); 207 208 // Add a lightbox if it's not there. 209 var lightbox = M.util.add_lightbox(Y, dragnode); 210 211 // Handle any variables which we must pass via AJAX. 212 var params = {}, 213 pageparams = this.get('config').pageparams, 214 varname; 215 216 for (varname in pageparams) { 217 if (!pageparams.hasOwnProperty(varname)) { 218 continue; 219 } 220 params[varname] = pageparams[varname]; 221 } 222 223 // Prepare request parameters 224 params.sesskey = M.cfg.sesskey; 225 params.courseId = this.get('courseid'); 226 params['class'] = 'section'; 227 params.field = 'move'; 228 params.id = dragnodeid; 229 params.value = dropnodeindex; 230 231 // Perform the AJAX request. 232 var uri = M.cfg.wwwroot + this.get('ajaxurl'); 233 Y.io(uri, { 234 method: 'POST', 235 data: params, 236 on: { 237 start: function() { 238 lightbox.show(); 239 }, 240 success: function(tid, response) { 241 // Update section titles, we can't simply swap them as 242 // they might have custom title 243 try { 244 var responsetext = Y.JSON.parse(response.responseText); 245 if (responsetext.error) { 246 new M.core.ajaxException(responsetext); 247 } 248 M.course.format.process_sections(Y, sectionlist, responsetext, loopstart, loopend); 249 } catch (e) { 250 // Ignore. 251 } 252 253 // Update all of the section IDs - first unset them, then set them 254 // to avoid duplicates in the DOM. 255 var index; 256 257 // Classic bubble sort algorithm is applied to the section 258 // nodes between original drag node location and the new one. 259 var swapped = false; 260 do { 261 swapped = false; 262 for (index = loopstart; index <= loopend; index++) { 263 if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) > 264 Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) { 265 Y.log("Swapping " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) + 266 " with " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index))); 267 // Swap section id. 268 var sectionid = sectionlist.item(index - 1).get('id'); 269 sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id')); 270 sectionlist.item(index).set('id', sectionid); 271 272 // See what format needs to swap. 273 M.course.format.swap_sections(Y, index - 1, index); 274 275 // Update flag. 276 swapped = true; 277 } 278 } 279 loopend = loopend - 1; 280 } while (swapped); 281 282 window.setTimeout(function() { 283 lightbox.hide(); 284 }, 250); 285 }, 286 287 failure: function(tid, response) { 288 this.ajax_failure(response); 289 lightbox.hide(); 290 } 291 }, 292 context: this 293 }); 294 } 295 296 }, { 297 NAME: 'course-dragdrop-section', 298 ATTRS: { 299 courseid: { 300 value: null 301 }, 302 ajaxurl: { 303 value: 0 304 }, 305 config: { 306 value: 0 307 } 308 } 309 }); 310 311 M.course = M.course || {}; 312 M.course.init_section_dragdrop = function(params) { 313 new DRAGSECTION(params); 314 }; 315 /** 316 * Resource drag and drop. 317 * 318 * @class M.course.dragdrop.resource 319 * @constructor 320 * @extends M.core.dragdrop 321 */ 322 var DRAGRESOURCE = function() { 323 DRAGRESOURCE.superclass.constructor.apply(this, arguments); 324 }; 325 Y.extend(DRAGRESOURCE, M.core.dragdrop, { 326 initializer: function() { 327 // Set group for parent class 328 this.groups = ['resource']; 329 this.samenodeclass = CSS.ACTIVITY; 330 this.parentnodeclass = CSS.SECTION; 331 this.resourcedraghandle = this.get_drag_handle(M.util.get_string('movecoursemodule', 'moodle'), 332 CSS.EDITINGMOVE, CSS.ICONCLASS, true); 333 334 this.samenodelabel = { 335 identifier: 'afterresource', 336 component: 'moodle' 337 }; 338 this.parentnodelabel = { 339 identifier: 'totopofsection', 340 component: 'moodle' 341 }; 342 343 // Go through all sections 344 var sectionlistselector = M.course.format.get_section_selector(Y); 345 if (sectionlistselector) { 346 sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + sectionlistselector; 347 this.setup_for_section(sectionlistselector); 348 349 // Initialise drag & drop for all resources/activities 350 var nodeselector = sectionlistselector.slice(CSS.COURSECONTENT.length + 2) + ' li.' + CSS.ACTIVITY; 351 var del = new Y.DD.Delegate({ 352 container: '.' + CSS.COURSECONTENT, 353 nodes: nodeselector, 354 target: true, 355 handles: ['.' + CSS.EDITINGMOVE], 356 dragConfig: {groups: this.groups} 357 }); 358 del.dd.plug(Y.Plugin.DDProxy, { 359 // Don't move the node at the end of the drag 360 moveOnEnd: false, 361 cloneNode: true 362 }); 363 del.dd.plug(Y.Plugin.DDConstrained, { 364 // Keep it inside the .course-content 365 constrain: '#' + CSS.PAGECONTENT 366 }); 367 del.dd.plug(Y.Plugin.DDWinScroll); 368 369 M.course.coursebase.register_module(this); 370 M.course.dragres = this; 371 } 372 }, 373 374 /** 375 * Apply dragdrop features to the specified selector or node that refers to section(s) 376 * 377 * @method setup_for_section 378 * @param {String} baseselector The CSS selector or node to limit scope to 379 */ 380 setup_for_section: function(baseselector) { 381 Y.Node.all(baseselector).each(function(sectionnode) { 382 var resources = sectionnode.one('.' + CSS.CONTENT + ' ul.' + CSS.SECTION); 383 // See if resources ul exists, if not create one 384 if (!resources) { 385 resources = Y.Node.create('<ul></ul>'); 386 resources.addClass(CSS.SECTION); 387 sectionnode.one('.' + CSS.CONTENT + ' div.' + CSS.SUMMARY).insert(resources, 'after'); 388 } 389 resources.setAttribute('data-draggroups', this.groups.join(' ')); 390 // Define empty ul as droptarget, so that item could be moved to empty list 391 new Y.DD.Drop({ 392 node: resources, 393 groups: this.groups, 394 padding: '20 0 20 0' 395 }); 396 397 // Initialise each resource/activity in this section 398 this.setup_for_resource('#' + sectionnode.get('id') + ' li.' + CSS.ACTIVITY); 399 }, this); 400 }, 401 402 /** 403 * Apply dragdrop features to the specified selector or node that refers to resource(s) 404 * 405 * @method setup_for_resource 406 * @param {String} baseselector The CSS selector or node to limit scope to 407 */ 408 setup_for_resource: function(baseselector) { 409 Y.Node.all(baseselector).each(function(resourcesnode) { 410 var draggroups = resourcesnode.getData('draggroups'); 411 if (!draggroups) { 412 // This Drop Node has not been set up. Configure it now. 413 resourcesnode.setAttribute('data-draggroups', this.groups.join(' ')); 414 // Define empty ul as droptarget, so that item could be moved to empty list 415 new Y.DD.Drop({ 416 node: resourcesnode, 417 groups: this.groups, 418 padding: '20 0 20 0' 419 }); 420 } 421 422 // Replace move icons 423 var move = resourcesnode.one('a.' + CSS.EDITINGMOVE); 424 if (move) { 425 move.replace(this.resourcedraghandle.cloneNode(true)); 426 } 427 }, this); 428 }, 429 430 drag_start: function(e) { 431 // Get our drag object 432 var drag = e.target; 433 drag.get('dragNode').setContent(drag.get('node').get('innerHTML')); 434 drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline'); 435 }, 436 437 drag_dropmiss: function(e) { 438 // Missed the target, but we assume the user intended to drop it 439 // on the last last ghost node location, e.drag and e.drop should be 440 // prepared by global_drag_dropmiss parent so simulate drop_hit(e). 441 this.drop_hit(e); 442 }, 443 444 drop_hit: function(e) { 445 var drag = e.drag; 446 // Get a reference to our drag node 447 var dragnode = drag.get('node'); 448 var dropnode = e.drop.get('node'); 449 450 // Add spinner if it not there 451 var actionarea = dragnode.one(CSS.ACTIONAREA); 452 var spinner = M.util.add_spinner(Y, actionarea); 453 454 var params = {}; 455 456 // Handle any variables which we must pass back through to 457 var pageparams = this.get('config').pageparams; 458 var varname; 459 for (varname in pageparams) { 460 params[varname] = pageparams[varname]; 461 } 462 463 // Prepare request parameters 464 params.sesskey = M.cfg.sesskey; 465 params.courseId = this.get('courseid'); 466 params['class'] = 'resource'; 467 params.field = 'move'; 468 params.id = Number(Y.Moodle.core_course.util.cm.getId(dragnode)); 469 params.sectionId = Y.Moodle.core_course.util.section.getId(dropnode.ancestor(M.course.format.get_section_wrapper(Y), true)); 470 471 if (dragnode.next()) { 472 params.beforeId = Number(Y.Moodle.core_course.util.cm.getId(dragnode.next())); 473 } 474 475 // Do AJAX request 476 var uri = M.cfg.wwwroot + this.get('ajaxurl'); 477 478 Y.io(uri, { 479 method: 'POST', 480 data: params, 481 on: { 482 start: function() { 483 this.lock_drag_handle(drag, CSS.EDITINGMOVE); 484 spinner.show(); 485 }, 486 success: function(tid, response) { 487 var responsetext = Y.JSON.parse(response.responseText); 488 var params = {element: dragnode, visible: responsetext.visible}; 489 M.course.coursebase.invoke_function('set_visibility_resource_ui', params); 490 this.unlock_drag_handle(drag, CSS.EDITINGMOVE); 491 window.setTimeout(function() { 492 spinner.hide(); 493 }, 250); 494 }, 495 failure: function(tid, response) { 496 this.ajax_failure(response); 497 this.unlock_drag_handle(drag, CSS.SECTIONHANDLE); 498 spinner.hide(); 499 // TODO: revert nodes location 500 } 501 }, 502 context: this 503 }); 504 } 505 }, { 506 NAME: 'course-dragdrop-resource', 507 ATTRS: { 508 courseid: { 509 value: null 510 }, 511 ajaxurl: { 512 value: 0 513 }, 514 config: { 515 value: 0 516 } 517 } 518 }); 519 520 M.course = M.course || {}; 521 M.course.init_resource_dragdrop = function(params) { 522 new DRAGRESOURCE(params); 523 }; 524 525 526 }, '@VERSION@', { 527 "requires": [ 528 "base", 529 "node", 530 "io", 531 "dom", 532 "dd", 533 "dd-scroll", 534 "moodle-core-dragdrop", 535 "moodle-core-notification", 536 "moodle-course-coursebase", 537 "moodle-course-util" 538 ] 539 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |