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