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