[ 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 return; 173 } 174 175 176 if (loopstart > loopend) { 177 // If we're going up, we need to swap the loop order 178 // because loops can't go backwards. 179 loopstart = dropnodeindex; 180 loopend = dragnodeid; 181 } 182 183 // Get the list of nodes. 184 drag.get('dragNode').removeClass(CSS.COURSECONTENT); 185 var sectionlist = Y.Node.all(this.sectionlistselector); 186 187 // Add a lightbox if it's not there. 188 var lightbox = M.util.add_lightbox(Y, dragnode); 189 190 // Handle any variables which we must pass via AJAX. 191 var params = {}, 192 pageparams = this.get('config').pageparams, 193 varname; 194 195 for (varname in pageparams) { 196 if (!pageparams.hasOwnProperty(varname)) { 197 continue; 198 } 199 params[varname] = pageparams[varname]; 200 } 201 202 // Prepare request parameters 203 params.sesskey = M.cfg.sesskey; 204 params.courseid = this.get('courseid'); 205 params.quizid = this.get('quizid'); 206 params['class'] = 'section'; 207 params.field = 'move'; 208 params.id = dragnodeid; 209 params.value = dropnodeindex; 210 211 // Perform the AJAX request. 212 var uri = M.cfg.wwwroot + this.get('ajaxurl'); 213 Y.io(uri, { 214 method: 'POST', 215 data: params, 216 on: { 217 start: function() { 218 lightbox.show(); 219 }, 220 success: function(tid, response) { 221 // Update section titles, we can't simply swap them as 222 // they might have custom title 223 try { 224 var responsetext = Y.JSON.parse(response.responseText); 225 if (responsetext.error) { 226 new M.core.ajaxException(responsetext); 227 } 228 M.mod_quiz.edit.process_sections(Y, sectionlist, responsetext, loopstart, loopend); 229 } catch (e) { 230 // Ignore. 231 } 232 233 // Update all of the section IDs - first unset them, then set them 234 // to avoid duplicates in the DOM. 235 var index; 236 237 // Classic bubble sort algorithm is applied to the section 238 // nodes between original drag node location and the new one. 239 var swapped = false; 240 do { 241 swapped = false; 242 for (index = loopstart; index <= loopend; index++) { 243 if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) > 244 Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) { 245 // Swap section id. 246 var sectionid = sectionlist.item(index - 1).get('id'); 247 sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id')); 248 sectionlist.item(index).set('id', sectionid); 249 250 // See what format needs to swap. 251 M.mod_quiz.edit.swap_sections(Y, index - 1, index); 252 253 // Update flag. 254 swapped = true; 255 } 256 } 257 loopend = loopend - 1; 258 } while (swapped); 259 260 window.setTimeout(function() { 261 lightbox.hide(); 262 }, 250); 263 }, 264 265 failure: function(tid, response) { 266 this.ajax_failure(response); 267 lightbox.hide(); 268 } 269 }, 270 context: this 271 }); 272 } 273 274 }, { 275 NAME: 'mod_quiz-dragdrop-section', 276 ATTRS: { 277 courseid: { 278 value: null 279 }, 280 quizid: { 281 value: null 282 }, 283 ajaxurl: { 284 value: 0 285 }, 286 config: { 287 value: 0 288 } 289 } 290 }); 291 292 M.mod_quiz = M.mod_quiz || {}; 293 M.mod_quiz.init_section_dragdrop = function(params) { 294 new DRAGSECTION(params); 295 }; 296 /* global SELECTOR */ 297 /** 298 * Resource drag and drop. 299 * 300 * @class M.course.dragdrop.resource 301 * @constructor 302 * @extends M.core.dragdrop 303 */ 304 var DRAGRESOURCE = function() { 305 DRAGRESOURCE.superclass.constructor.apply(this, arguments); 306 }; 307 Y.extend(DRAGRESOURCE, M.core.dragdrop, { 308 initializer: function() { 309 // Set group for parent class 310 this.groups = ['resource']; 311 this.samenodeclass = CSS.ACTIVITY; 312 this.parentnodeclass = CSS.SECTION; 313 this.resourcedraghandle = this.get_drag_handle(M.util.get_string('move', 'moodle'), CSS.EDITINGMOVE, CSS.ICONCLASS, true); 314 315 this.samenodelabel = { 316 identifier: 'dragtoafter', 317 component: 'quiz' 318 }; 319 this.parentnodelabel = { 320 identifier: 'dragtostart', 321 component: 'quiz' 322 }; 323 324 // Go through all sections 325 this.setup_for_section(); 326 327 // Initialise drag & drop for all resources/activities 328 var nodeselector = 'li.' + CSS.ACTIVITY; 329 var del = new Y.DD.Delegate({ 330 container: '.' + CSS.COURSECONTENT, 331 nodes: nodeselector, 332 target: true, 333 handles: ['.' + CSS.EDITINGMOVE], 334 dragConfig: {groups: this.groups} 335 }); 336 del.dd.plug(Y.Plugin.DDProxy, { 337 // Don't move the node at the end of the drag 338 moveOnEnd: false, 339 cloneNode: true 340 }); 341 del.dd.plug(Y.Plugin.DDConstrained, { 342 // Keep it inside the .mod-quiz-edit-content 343 constrain: '#' + CSS.SLOTS 344 }); 345 del.dd.plug(Y.Plugin.DDWinScroll); 346 347 M.mod_quiz.quizbase.register_module(this); 348 M.mod_quiz.dragres = this; 349 }, 350 351 /** 352 * Apply dragdrop features to the specified selector or node that refers to section(s) 353 * 354 * @method setup_for_section 355 * @param {String} baseselector The CSS selector or node to limit scope to 356 */ 357 setup_for_section: function() { 358 Y.Node.all('.mod-quiz-edit-content ul.slots ul.section').each(function(resources) { 359 resources.setAttribute('data-draggroups', this.groups.join(' ')); 360 // Define empty ul as droptarget, so that item could be moved to empty list 361 new Y.DD.Drop({ 362 node: resources, 363 groups: this.groups, 364 padding: '20 0 20 0' 365 }); 366 367 // Initialise each resource/activity in this section 368 this.setup_for_resource('li.activity'); 369 }, this); 370 }, 371 372 /** 373 * Apply dragdrop features to the specified selector or node that refers to resource(s) 374 * 375 * @method setup_for_resource 376 * @param {String} baseselector The CSS selector or node to limit scope to 377 */ 378 setup_for_resource: function(baseselector) { 379 Y.Node.all(baseselector).each(function(resourcesnode) { 380 // Replace move icons 381 var move = resourcesnode.one('a.' + CSS.EDITINGMOVE); 382 if (move) { 383 move.replace(this.resourcedraghandle.cloneNode(true)); 384 } 385 }, this); 386 }, 387 388 drag_start: function(e) { 389 // Get our drag object 390 var drag = e.target; 391 drag.get('dragNode').setContent(drag.get('node').get('innerHTML')); 392 drag.get('dragNode').all('img.iconsmall').setStyle('vertical-align', 'baseline'); 393 }, 394 395 drag_dropmiss: function(e) { 396 // Missed the target, but we assume the user intended to drop it 397 // on the last ghost node location, e.drag and e.drop should be 398 // prepared by global_drag_dropmiss parent so simulate drop_hit(e). 399 this.drop_hit(e); 400 }, 401 402 drop_hit: function(e) { 403 var drag = e.drag; 404 // Get a reference to our drag node 405 var dragnode = drag.get('node'); 406 var dropnode = e.drop.get('node'); 407 408 // Add spinner if it not there 409 var actionarea = dragnode.one(CSS.ACTIONAREA); 410 var spinner = M.util.add_spinner(Y, actionarea); 411 412 var params = {}; 413 414 // Handle any variables which we must pass back through to 415 var pageparams = this.get('config').pageparams; 416 var varname; 417 for (varname in pageparams) { 418 params[varname] = pageparams[varname]; 419 } 420 421 // Prepare request parameters 422 params.sesskey = M.cfg.sesskey; 423 params.courseid = this.get('courseid'); 424 params.quizid = this.get('quizid'); 425 params['class'] = 'resource'; 426 params.field = 'move'; 427 params.id = Number(Y.Moodle.mod_quiz.util.slot.getId(dragnode)); 428 params.sectionId = Y.Moodle.core_course.util.section.getId(dropnode.ancestor('li.section', true)); 429 430 var previousslot = dragnode.previous(SELECTOR.SLOT); 431 if (previousslot) { 432 params.previousid = Number(Y.Moodle.mod_quiz.util.slot.getId(previousslot)); 433 } 434 435 var previouspage = dragnode.previous(SELECTOR.PAGE); 436 if (previouspage) { 437 params.page = Number(Y.Moodle.mod_quiz.util.page.getId(previouspage)); 438 } 439 440 // Do AJAX request 441 var uri = M.cfg.wwwroot + this.get('ajaxurl'); 442 443 Y.io(uri, { 444 method: 'POST', 445 data: params, 446 on: { 447 start: function() { 448 this.lock_drag_handle(drag, CSS.EDITINGMOVE); 449 spinner.show(); 450 }, 451 success: function(tid, response) { 452 var responsetext = Y.JSON.parse(response.responseText); 453 var params = {element: dragnode, visible: responsetext.visible}; 454 M.mod_quiz.quizbase.invoke_function('set_visibility_resource_ui', params); 455 this.unlock_drag_handle(drag, CSS.EDITINGMOVE); 456 window.setTimeout(function() { 457 spinner.hide(); 458 }, 250); 459 M.mod_quiz.resource_toolbox.reorganise_edit_page(); 460 }, 461 failure: function(tid, response) { 462 this.ajax_failure(response); 463 this.unlock_drag_handle(drag, CSS.SECTIONHANDLE); 464 spinner.hide(); 465 window.location.reload(true); 466 } 467 }, 468 context: this 469 }); 470 }, 471 472 global_drop_over: function(e) { 473 // Overriding parent method so we can stop the slots being dragged before the first page node. 474 475 // Check that drop object belong to correct group. 476 if (!e.drop || !e.drop.inGroup(this.groups)) { 477 return; 478 } 479 480 // Get a reference to our drag and drop nodes. 481 var drag = e.drag.get('node'), 482 drop = e.drop.get('node'); 483 484 // Save last drop target for the case of missed target processing. 485 this.lastdroptarget = e.drop; 486 487 // Are we dropping within the same parent node? 488 if (drop.hasClass(this.samenodeclass)) { 489 var where; 490 491 if (this.goingup) { 492 where = "before"; 493 } else { 494 where = "after"; 495 } 496 497 drop.insert(drag, where); 498 } else if ((drop.hasClass(this.parentnodeclass) || drop.test('[data-droptarget="1"]')) && !drop.contains(drag)) { 499 // We are dropping on parent node and it is empty 500 if (this.goingup) { 501 drop.append(drag); 502 } else { 503 drop.prepend(drag); 504 } 505 } 506 this.drop_over(e); 507 } 508 }, { 509 NAME: 'mod_quiz-dragdrop-resource', 510 ATTRS: { 511 courseid: { 512 value: null 513 }, 514 quizid: { 515 value: null 516 }, 517 ajaxurl: { 518 value: 0 519 }, 520 config: { 521 value: 0 522 } 523 } 524 }); 525 526 M.mod_quiz = M.mod_quiz || {}; 527 M.mod_quiz.init_resource_dragdrop = function(params) { 528 new DRAGRESOURCE(params); 529 }; 530 531 532 }, '@VERSION@', { 533 "requires": [ 534 "base", 535 "node", 536 "io", 537 "dom", 538 "dd", 539 "dd-scroll", 540 "moodle-core-dragdrop", 541 "moodle-core-notification", 542 "moodle-mod_quiz-quizbase", 543 "moodle-mod_quiz-util-base", 544 "moodle-mod_quiz-util-page", 545 "moodle-mod_quiz-util-slot", 546 "moodle-course-util" 547 ] 548 });
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 |