[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 YUI.add('moodle-core-blocks', function (Y, NAME) { 2 3 /* global MANAGER */ 4 5 /** 6 * Provides drag and drop functionality for blocks. 7 * 8 * @module moodle-core-blockdraganddrop 9 */ 10 11 var AJAXURL = '/lib/ajax/blocks.php', 12 CSS = { 13 BLOCK: 'block', 14 BLOCKREGION: 'block-region', 15 BLOCKADMINBLOCK: 'block_adminblock', 16 EDITINGMOVE: 'editing_move', 17 HEADER: 'header', 18 LIGHTBOX: 'lightbox', 19 REGIONCONTENT: 'region-content', 20 SKIPBLOCK: 'skip-block', 21 SKIPBLOCKTO: 'skip-block-to', 22 MYINDEX: 'page-my-index', 23 REGIONMAIN: 'region-main', 24 BLOCKSMOVING: 'blocks-moving' 25 }; 26 27 var SELECTOR = { 28 DRAGHANDLE: '.' + CSS.HEADER + ' .commands .moodle-core-dragdrop-draghandle' 29 }; 30 31 /** 32 * Legacy drag and drop manager. 33 * This drag and drop manager is specifically designed for themes using side-pre and side-post 34 * that do not make use of the block output methods introduced by MDL-39824. 35 * 36 * @namespace M.core.blockdraganddrop 37 * @class LegacyManager 38 * @constructor 39 * @extends M.core.dragdrop 40 */ 41 var DRAGBLOCK = function() { 42 DRAGBLOCK.superclass.constructor.apply(this, arguments); 43 }; 44 Y.extend(DRAGBLOCK, M.core.dragdrop, { 45 skipnodetop: null, 46 skipnodebottom: null, 47 dragsourceregion: null, 48 initializer: function() { 49 // Set group for parent class 50 this.groups = ['block']; 51 this.samenodeclass = CSS.BLOCK; 52 this.parentnodeclass = CSS.REGIONCONTENT; 53 54 // Add relevant classes and ID to 'content' block region on Dashboard page. 55 var myhomecontent = Y.Node.all('body#' + CSS.MYINDEX + ' #' + CSS.REGIONMAIN + ' > .' + CSS.REGIONCONTENT); 56 if (myhomecontent.size() > 0) { 57 var contentregion = myhomecontent.item(0); 58 contentregion.addClass(CSS.BLOCKREGION); 59 contentregion.set('id', CSS.REGIONCONTENT); 60 contentregion.one('div').addClass(CSS.REGIONCONTENT); 61 } 62 63 // Initialise blocks dragging 64 // Find all block regions on the page 65 var blockregionlist = Y.Node.all('div.' + CSS.BLOCKREGION); 66 67 if (blockregionlist.size() === 0) { 68 return false; 69 } 70 71 // See if we are missing either of block regions, 72 // if yes we need to add an empty one to use as target 73 if (blockregionlist.size() !== this.get('regions').length) { 74 var blockregion = Y.Node.create('<div></div>') 75 .addClass(CSS.BLOCKREGION); 76 var regioncontent = Y.Node.create('<div></div>') 77 .addClass(CSS.REGIONCONTENT); 78 blockregion.appendChild(regioncontent); 79 var pre = blockregionlist.filter('#region-pre'); 80 var post = blockregionlist.filter('#region-post'); 81 82 if (pre.size() === 0 && post.size() === 1) { 83 // pre block is missing, instert it before post 84 blockregion.setAttrs({id: 'region-pre'}); 85 post.item(0).insert(blockregion, 'before'); 86 blockregionlist.unshift(blockregion); 87 } else if (post.size() === 0 && pre.size() === 1) { 88 // post block is missing, instert it after pre 89 blockregion.setAttrs({id: 'region-post'}); 90 pre.item(0).insert(blockregion, 'after'); 91 blockregionlist.push(blockregion); 92 } 93 } 94 95 blockregionlist.each(function(blockregionnode) { 96 97 // Setting blockregion as droptarget (the case when it is empty) 98 // The region-post (the right one) 99 // is very narrow, so add extra padding on the left to drop block on it. 100 new Y.DD.Drop({ 101 node: blockregionnode.one('div.' + CSS.REGIONCONTENT), 102 groups: this.groups, 103 padding: '40 240 40 240' 104 }); 105 106 // Make each div element in the list of blocks draggable 107 var del = new Y.DD.Delegate({ 108 container: blockregionnode, 109 nodes: '.' + CSS.BLOCK, 110 target: true, 111 handles: [SELECTOR.DRAGHANDLE], 112 invalid: '.block-hider-hide, .block-hider-show, .moveto', 113 dragConfig: {groups: this.groups} 114 }); 115 del.dd.plug(Y.Plugin.DDProxy, { 116 // Don't move the node at the end of the drag 117 moveOnEnd: false 118 }); 119 del.dd.plug(Y.Plugin.DDWinScroll); 120 121 var blocklist = blockregionnode.all('.' + CSS.BLOCK); 122 blocklist.each(function(blocknode) { 123 var move = blocknode.one('a.' + CSS.EDITINGMOVE); 124 if (move) { 125 move.replace(this.get_drag_handle(move.getAttribute('title'), '', 'iconsmall', true)); 126 blocknode.one(SELECTOR.DRAGHANDLE).setStyle('cursor', 'move'); 127 } 128 }, this); 129 }, this); 130 }, 131 132 get_block_id: function(node) { 133 return Number(node.get('id').replace(/inst/i, '')); 134 }, 135 136 get_block_region: function(node) { 137 var region = node.ancestor('div.' + CSS.BLOCKREGION).get('id').replace(/region-/i, ''); 138 if (Y.Array.indexOf(this.get('regions'), region) === -1) { 139 // Must be standard side-X 140 if (window.right_to_left()) { 141 if (region === 'post') { 142 region = 'pre'; 143 } else if (region === 'pre') { 144 region = 'post'; 145 } 146 } 147 return 'side-' + region; 148 } 149 // Perhaps custom region 150 return region; 151 }, 152 153 get_region_id: function(node) { 154 return node.get('id').replace(/region-/i, ''); 155 }, 156 157 drag_start: function(e) { 158 // Get our drag object 159 var drag = e.target; 160 161 // Store the parent node of original drag node (block) 162 // we will need it later for show/hide empty regions 163 this.dragsourceregion = drag.get('node').ancestor('div.' + CSS.BLOCKREGION); 164 165 // Determine skipnodes and store them 166 if (drag.get('node').previous() && drag.get('node').previous().hasClass(CSS.SKIPBLOCK)) { 167 this.skipnodetop = drag.get('node').previous(); 168 } 169 if (drag.get('node').next() && drag.get('node').next().hasClass(CSS.SKIPBLOCKTO)) { 170 this.skipnodebottom = drag.get('node').next(); 171 } 172 173 // Add the blocks-moving class so that the theme can respond if need be. 174 Y.one('body').addClass(CSS.BLOCKSMOVING); 175 }, 176 177 drop_over: function(e) { 178 // Get a reference to our drag and drop nodes 179 var drag = e.drag.get('node'); 180 var drop = e.drop.get('node'); 181 182 // We need to fix the case when parent drop over event has determined 183 // 'goingup' and appended the drag node after admin-block. 184 if (drop.hasClass(this.parentnodeclass) && 185 drop.one('.' + CSS.BLOCKADMINBLOCK) && 186 drop.one('.' + CSS.BLOCKADMINBLOCK).next('.' + CSS.BLOCK)) { 187 drop.prepend(drag); 188 } 189 190 // Block is moved within the same region 191 // stop here, no need to modify anything. 192 if (this.dragsourceregion.contains(drop)) { 193 return false; 194 } 195 196 // TODO: Hiding-displaying block region only works for base theme blocks 197 // (region-pre, region-post) at the moment. It should be improved 198 // to work with custom block regions as well. 199 200 // TODO: Fix this for the case when user drag block towards empty section, 201 // then the section appears, then user chnages his mind and moving back to 202 // original section. The opposite section remains opened and empty. 203 204 var documentbody = Y.one('body'); 205 // Moving block towards hidden region-content, display it 206 var regionname = this.get_region_id(this.dragsourceregion); 207 if (documentbody.hasClass('side-' + regionname + '-only')) { 208 documentbody.removeClass('side-' + regionname + '-only'); 209 } 210 211 // Moving from empty region-content towards the opposite one, 212 // hide empty one (only for region-pre, region-post areas at the moment). 213 regionname = this.get_region_id(drop.ancestor('div.' + CSS.BLOCKREGION)); 214 if (this.dragsourceregion.all('.' + CSS.BLOCK).size() === 0 && 215 this.dragsourceregion.get('id').match(/(region-pre|region-post)/i)) { 216 if (!documentbody.hasClass('side-' + regionname + '-only')) { 217 documentbody.addClass('side-' + regionname + '-only'); 218 } 219 } 220 }, 221 222 drag_end: function() { 223 // clear variables 224 this.skipnodetop = null; 225 this.skipnodebottom = null; 226 this.dragsourceregion = null; 227 // Remove the blocks moving class once the drag-drop is over. 228 Y.one('body').removeClass(CSS.BLOCKSMOVING); 229 }, 230 231 drag_dropmiss: function(e) { 232 // Missed the target, but we assume the user intended to drop it 233 // on the last last ghost node location, e.drag and e.drop should be 234 // prepared by global_drag_dropmiss parent so simulate drop_hit(e). 235 this.drop_hit(e); 236 }, 237 238 drop_hit: function(e) { 239 var drag = e.drag; 240 // Get a reference to our drag node 241 var dragnode = drag.get('node'); 242 var dropnode = e.drop.get('node'); 243 244 // Amend existing skipnodes 245 if (dragnode.previous() && dragnode.previous().hasClass(CSS.SKIPBLOCK)) { 246 // the one that belongs to block below move below 247 dragnode.insert(dragnode.previous(), 'after'); 248 } 249 // Move original skipnodes 250 if (this.skipnodetop) { 251 dragnode.insert(this.skipnodetop, 'before'); 252 } 253 if (this.skipnodebottom) { 254 dragnode.insert(this.skipnodebottom, 'after'); 255 } 256 257 // Add lightbox if it not there 258 var lightbox = M.util.add_lightbox(Y, dragnode); 259 260 // Prepare request parameters 261 var params = { 262 sesskey: M.cfg.sesskey, 263 courseid: this.get('courseid'), 264 pagelayout: this.get('pagelayout'), 265 pagetype: this.get('pagetype'), 266 subpage: this.get('subpage'), 267 contextid: this.get('contextid'), 268 action: 'move', 269 bui_moveid: this.get_block_id(dragnode), 270 bui_newregion: this.get_block_region(dropnode) 271 }; 272 273 if (this.get('cmid')) { 274 params.cmid = this.get('cmid'); 275 } 276 277 if (dragnode.next('.' + this.samenodeclass) && !dragnode.next('.' + this.samenodeclass).hasClass(CSS.BLOCKADMINBLOCK)) { 278 params.bui_beforeid = this.get_block_id(dragnode.next('.' + this.samenodeclass)); 279 } 280 281 // Do AJAX request 282 Y.io(M.cfg.wwwroot + AJAXURL, { 283 method: 'POST', 284 data: params, 285 on: { 286 start: function() { 287 lightbox.show(); 288 }, 289 success: function(tid, response) { 290 window.setTimeout(function() { 291 lightbox.hide(); 292 }, 250); 293 try { 294 var responsetext = Y.JSON.parse(response.responseText); 295 if (responsetext.error) { 296 new M.core.ajaxException(responsetext); 297 } 298 } catch (e) { 299 // Ignore. 300 } 301 }, 302 failure: function(tid, response) { 303 this.ajax_failure(response); 304 lightbox.hide(); 305 } 306 }, 307 context: this 308 }); 309 } 310 }, { 311 NAME: 'core-blocks-dragdrop', 312 ATTRS: { 313 courseid: { 314 value: null 315 }, 316 cmid: { 317 value: null 318 }, 319 contextid: { 320 value: null 321 }, 322 pagelayout: { 323 value: null 324 }, 325 pagetype: { 326 value: null 327 }, 328 subpage: { 329 value: null 330 }, 331 regions: { 332 value: null 333 } 334 } 335 }); 336 337 M.core = M.core || {}; 338 M.core.blockdraganddrop = M.core.blockdraganddrop || {}; 339 340 /** 341 * True if the page is using the new blocks methods. 342 * @private 343 * @static 344 * @property M.core.blockdraganddrop._isusingnewblocksmethod 345 * @type Boolean 346 * @default null 347 */ 348 M.core.blockdraganddrop._isusingnewblocksmethod = null; 349 350 /** 351 * Returns true if the page is using the new blocks methods. 352 * @static 353 * @method M.core.blockdraganddrop.is_using_blocks_render_method 354 * @return Boolean 355 */ 356 M.core.blockdraganddrop.is_using_blocks_render_method = function() { 357 if (this._isusingnewblocksmethod === null) { 358 var goodregions = Y.all('.block-region[data-blockregion]').size(); 359 var allregions = Y.all('.block-region').size(); 360 this._isusingnewblocksmethod = (allregions === goodregions); 361 if (goodregions > 0 && allregions > 0 && goodregions !== allregions) { 362 } 363 } 364 return this._isusingnewblocksmethod; 365 }; 366 367 /** 368 * Initialises a drag and drop manager. 369 * This should only ever be called once for a page. 370 * @static 371 * @method M.core.blockdraganddrop.init 372 * @param {Object} params 373 * @return Manager 374 */ 375 M.core.blockdraganddrop.init = function(params) { 376 if (this.is_using_blocks_render_method()) { 377 new MANAGER(params); 378 } else { 379 new DRAGBLOCK(params); 380 } 381 }; 382 383 /* 384 * Legacy code to keep things working. 385 */ 386 M.core_blocks = M.core_blocks || {}; 387 M.core_blocks.init_dragdrop = function(params) { 388 M.core.blockdraganddrop.init(params); 389 }; 390 /* global BLOCKREGION, SELECTOR, AJAXURL */ 391 392 /** 393 * This file contains the drag and drop manager class. 394 * 395 * Provides drag and drop functionality for blocks. 396 * 397 * @module moodle-core-blockdraganddrop 398 */ 399 400 /** 401 * Constructs a new Block drag and drop manager. 402 * 403 * @namespace M.core.blockdraganddrop 404 * @class Manager 405 * @constructor 406 * @extends M.core.dragdrop 407 */ 408 var MANAGER = function() { 409 MANAGER.superclass.constructor.apply(this, arguments); 410 }; 411 MANAGER.prototype = { 412 413 /** 414 * The skip block link from above the block being dragged while a drag is in progress. 415 * Required by the M.core.dragdrop from whom this class extends. 416 * @private 417 * @property skipnodetop 418 * @type Node 419 * @default null 420 */ 421 skipnodetop: null, 422 423 /** 424 * The skip block link from below the block being dragged while a drag is in progress. 425 * Required by the M.core.dragdrop from whom this class extends. 426 * @private 427 * @property skipnodebottom 428 * @type Node 429 * @default null 430 */ 431 skipnodebottom: null, 432 433 /** 434 * An associative object of regions and the 435 * @property regionobjects 436 * @type {Object} Primitive object mocking an associative array. 437 * @type {BLOCKREGION} [regionname]* Each item uses the region name as the key with the value being 438 * an instance of the BLOCKREGION class. 439 */ 440 regionobjects: {}, 441 442 /** 443 * Called during the initialisation process of the object. 444 * @method initializer 445 */ 446 initializer: function() { 447 var regionnames = this.get('regions'), 448 i = 0, 449 region, 450 regionname, 451 dragdelegation; 452 453 // Evil required by M.core.dragdrop. 454 this.groups = ['block']; 455 this.samenodeclass = CSS.BLOCK; 456 this.parentnodeclass = CSS.BLOCKREGION; 457 458 // Add relevant classes and ID to 'content' block region on Dashboard page. 459 var myhomecontent = Y.Node.all('body#' + CSS.MYINDEX + ' #' + CSS.REGIONMAIN + ' > .' + CSS.REGIONCONTENT); 460 if (myhomecontent.size() > 0) { 461 var contentregion = myhomecontent.item(0); 462 contentregion.addClass(CSS.BLOCKREGION); 463 contentregion.set('id', CSS.REGIONCONTENT); 464 contentregion.one('div').addClass(CSS.REGIONCONTENT); 465 } 466 467 for (i in regionnames) { 468 regionname = regionnames[i]; 469 region = new BLOCKREGION({ 470 manager: this, 471 region: regionname, 472 node: Y.one('#block-region-' + regionname) 473 }); 474 this.regionobjects[regionname] = region; 475 476 // Setting blockregion as droptarget (the case when it is empty) 477 // The region-post (the right one) 478 // is very narrow, so add extra padding on the left to drop block on it. 479 new Y.DD.Drop({ 480 node: region.get_droptarget(), 481 groups: this.groups, 482 padding: '40 240 40 240' 483 }); 484 485 // Make each div element in the list of blocks draggable 486 dragdelegation = new Y.DD.Delegate({ 487 container: region.get_droptarget(), 488 nodes: '.' + CSS.BLOCK, 489 target: true, 490 handles: [SELECTOR.DRAGHANDLE], 491 invalid: '.block-hider-hide, .block-hider-show, .moveto, .block_fake', 492 dragConfig: {groups: this.groups} 493 }); 494 dragdelegation.dd.plug(Y.Plugin.DDProxy, { 495 // Don't move the node at the end of the drag 496 moveOnEnd: false 497 }); 498 dragdelegation.dd.plug(Y.Plugin.DDWinScroll); 499 500 // On the DD Manager start operation, we enable all block regions so that they can be drop targets. This 501 // must be done *before* drag:start but after dragging has been initialised. 502 Y.DD.DDM.on('ddm:start', this.enable_all_regions, this); 503 504 region.change_block_move_icons(this); 505 } 506 }, 507 508 /** 509 * Returns the ID of the block the given node represents. 510 * @method get_block_id 511 * @param {Node} node 512 * @return {int} The blocks ID in the database. 513 */ 514 get_block_id: function(node) { 515 return Number(node.get('id').replace(/inst/i, '')); 516 }, 517 518 /** 519 * Returns the block region that the node is part of or belonging to. 520 * @method get_block_region 521 * @param {Y.Node} node 522 * @return {string} The region name. 523 */ 524 get_block_region: function(node) { 525 if (!node.test('[data-blockregion]')) { 526 node = node.ancestor('[data-blockregion]'); 527 } 528 return node.getData('blockregion'); 529 }, 530 531 /** 532 * Returns the BLOCKREGION instance that represents the block region the given node is part of. 533 * @method get_region_object 534 * @param {Y.Node} node 535 * @return {BLOCKREGION} 536 */ 537 get_region_object: function(node) { 538 return this.regionobjects[this.get_block_region(node)]; 539 }, 540 541 /** 542 * Enables all fo the regions so that they are all visible while dragging is occuring. 543 * 544 * @method enable_all_regions 545 */ 546 enable_all_regions: function() { 547 var groups = Y.DD.DDM.activeDrag.get('groups'); 548 549 // As we're called by Y.DD.DDM, we can't be certain that the call 550 // relates specifically to a block drag/drop operation. Test 551 // whether the relevant group applies here. 552 if (!groups || Y.Array.indexOf(groups, 'block') === -1) { 553 return; 554 } 555 556 var i; 557 for (i in this.regionobjects) { 558 if (!this.regionobjects.hasOwnProperty(i)) { 559 continue; 560 } 561 this.regionobjects[i].enable(); 562 } 563 }, 564 565 /** 566 * Disables enabled regions if they contain no blocks. 567 * @method disable_regions_if_required 568 */ 569 disable_regions_if_required: function() { 570 var i = 0; 571 for (i in this.regionobjects) { 572 this.regionobjects[i].disable_if_required(); 573 } 574 }, 575 576 /** 577 * Called by M.core.dragdrop.global_drag_start when dragging starts. 578 * @method drag_start 579 * @param {Event} e 580 */ 581 drag_start: function(e) { 582 // Get our drag object 583 var drag = e.target; 584 585 // Store the parent node of original drag node (block) 586 // we will need it later for show/hide empty regions 587 588 // Determine skipnodes and store them 589 if (drag.get('node').previous() && drag.get('node').previous().hasClass(CSS.SKIPBLOCK)) { 590 this.skipnodetop = drag.get('node').previous(); 591 } 592 if (drag.get('node').next() && drag.get('node').next().hasClass(CSS.SKIPBLOCKTO)) { 593 this.skipnodebottom = drag.get('node').next(); 594 } 595 }, 596 597 /** 598 * Called by M.core.dragdrop.global_drop_over when something is dragged over a drop target. 599 * @method drop_over 600 * @param {Event} e 601 */ 602 drop_over: function(e) { 603 // Get a reference to our drag and drop nodes 604 var drag = e.drag.get('node'); 605 var drop = e.drop.get('node'); 606 607 // We need to fix the case when parent drop over event has determined 608 // 'goingup' and appended the drag node after admin-block. 609 if (drop.hasClass(CSS.REGIONCONTENT) && 610 drop.one('.' + CSS.BLOCKADMINBLOCK) && 611 drop.one('.' + CSS.BLOCKADMINBLOCK).next('.' + CSS.BLOCK)) { 612 drop.prepend(drag); 613 } 614 }, 615 616 /** 617 * Called by M.core.dragdrop.global_drop_end when a drop has been completed. 618 * @method drop_end 619 */ 620 drop_end: function() { 621 // Clear variables. 622 this.skipnodetop = null; 623 this.skipnodebottom = null; 624 this.disable_regions_if_required(); 625 }, 626 627 /** 628 * Called by M.core.dragdrop.global_drag_dropmiss when something has been dropped on a node that isn't contained by 629 * a drop target. 630 * 631 * @method drag_dropmiss 632 * @param {Event} e 633 */ 634 drag_dropmiss: function(e) { 635 // Missed the target, but we assume the user intended to drop it 636 // on the last ghost node location, e.drag and e.drop should be 637 // prepared by global_drag_dropmiss parent so simulate drop_hit(e). 638 this.drop_hit(e); 639 }, 640 641 /** 642 * Called by M.core.dragdrop.global_drag_hit when something has been dropped on a drop target. 643 * @method drop_hit 644 * @param {Event} e 645 */ 646 drop_hit: function(e) { 647 // Get a reference to our drag node 648 var dragnode = e.drag.get('node'); 649 var dropnode = e.drop.get('node'); 650 651 // Amend existing skipnodes 652 if (dragnode.previous() && dragnode.previous().hasClass(CSS.SKIPBLOCK)) { 653 // the one that belongs to block below move below 654 dragnode.insert(dragnode.previous(), 'after'); 655 } 656 // Move original skipnodes 657 if (this.skipnodetop) { 658 dragnode.insert(this.skipnodetop, 'before'); 659 } 660 if (this.skipnodebottom) { 661 dragnode.insert(this.skipnodebottom, 'after'); 662 } 663 664 // Add lightbox if it not there 665 var lightbox = M.util.add_lightbox(Y, dragnode); 666 667 // Prepare request parameters 668 var params = { 669 sesskey: M.cfg.sesskey, 670 courseid: this.get('courseid'), 671 pagelayout: this.get('pagelayout'), 672 pagetype: this.get('pagetype'), 673 subpage: this.get('subpage'), 674 contextid: this.get('contextid'), 675 action: 'move', 676 bui_moveid: this.get_block_id(dragnode), 677 bui_newregion: this.get_block_region(dropnode) 678 }; 679 680 if (this.get('cmid')) { 681 params.cmid = this.get('cmid'); 682 } 683 684 if (dragnode.next('.' + CSS.BLOCK) && !dragnode.next('.' + CSS.BLOCK).hasClass(CSS.BLOCKADMINBLOCK)) { 685 params.bui_beforeid = this.get_block_id(dragnode.next('.' + CSS.BLOCK)); 686 } 687 688 // Do AJAX request 689 Y.io(M.cfg.wwwroot + AJAXURL, { 690 method: 'POST', 691 data: params, 692 on: { 693 start: function() { 694 lightbox.show(); 695 }, 696 success: function(tid, response) { 697 window.setTimeout(function() { 698 lightbox.hide(); 699 }, 250); 700 try { 701 var responsetext = Y.JSON.parse(response.responseText); 702 if (responsetext.error) { 703 new M.core.ajaxException(responsetext); 704 } 705 } catch (e) { 706 // Ignore. 707 } 708 }, 709 failure: function(tid, response) { 710 this.ajax_failure(response); 711 lightbox.hide(); 712 }, 713 complete: function() { 714 this.disable_regions_if_required(); 715 } 716 }, 717 context: this 718 }); 719 } 720 }; 721 Y.extend(MANAGER, M.core.dragdrop, MANAGER.prototype, { 722 NAME: 'core-blocks-dragdrop-manager', 723 ATTRS: { 724 /** 725 * The Course ID if there is one. 726 * @attribute courseid 727 * @type int|null 728 * @default null 729 */ 730 courseid: { 731 value: null 732 }, 733 734 /** 735 * The Course Module ID if there is one. 736 * @attribute cmid 737 * @type int|null 738 * @default null 739 */ 740 cmid: { 741 value: null 742 }, 743 744 /** 745 * The Context ID. 746 * @attribute contextid 747 * @type int|null 748 * @default null 749 */ 750 contextid: { 751 value: null 752 }, 753 754 /** 755 * The current page layout. 756 * @attribute pagelayout 757 * @type string|null 758 * @default null 759 */ 760 pagelayout: { 761 value: null 762 }, 763 764 /** 765 * The page type string, should be used as the id for the body tag in the theme. 766 * @attribute pagetype 767 * @type string|null 768 * @default null 769 */ 770 pagetype: { 771 value: null 772 }, 773 774 /** 775 * The subpage identifier, if any. 776 * @attribute subpage 777 * @type string|null 778 * @default null 779 */ 780 subpage: { 781 value: null 782 }, 783 784 /** 785 * An array of block regions that are present on the page. 786 * @attribute regions 787 * @type array|null 788 * @default Array[] 789 */ 790 regions: { 791 value: [] 792 } 793 } 794 }); 795 /* global MANAGER */ 796 797 /** 798 * This file contains the Block Region class used by the drag and drop manager. 799 * 800 * Provides drag and drop functionality for blocks. 801 * 802 * @module moodle-core-blockdraganddrop 803 */ 804 805 /** 806 * Constructs a new block region object. 807 * 808 * @namespace M.core.blockdraganddrop 809 * @class BlockRegion 810 * @constructor 811 * @extends Base 812 */ 813 var BLOCKREGION = function() { 814 BLOCKREGION.superclass.constructor.apply(this, arguments); 815 }; 816 BLOCKREGION.prototype = { 817 /** 818 * Called during the initialisation process of the object. 819 * @method initializer 820 */ 821 initializer: function() { 822 var node = this.get('node'); 823 if (!node) { 824 node = this.create_and_add_node(); 825 } 826 var body = Y.one('body'), 827 hasblocks = node.all('.' + CSS.BLOCK).size() > 0, 828 hasregionclass = this.get_has_region_class(); 829 this.set('hasblocks', hasblocks); 830 if (!body.hasClass(hasregionclass)) { 831 body.addClass(hasregionclass); 832 } 833 body.addClass((hasblocks) ? this.get_used_region_class() : this.get_empty_region_class()); 834 body.removeClass((hasblocks) ? this.get_empty_region_class() : this.get_used_region_class()); 835 }, 836 /** 837 * Creates a generic block region node and adds it to the DOM at the best guess location. 838 * Any calling of this method is an unfortunate circumstance. 839 * @method create_and_add_node 840 * @return Node The newly created Node 841 */ 842 create_and_add_node: function() { 843 var c = Y.Node.create, 844 region = this.get('region'), 845 node = c('<div id="block-region-' + region + '" data-droptarget="1"></div>') 846 .addClass(CSS.BLOCKREGION) 847 .setData('blockregion', region), 848 regions = this.get('manager').get('regions'), 849 i, 850 haspre = false, 851 haspost = false, 852 added = false, 853 pre, 854 post; 855 856 for (i in regions) { 857 if (regions[i].match(/(pre|left)/)) { 858 haspre = regions[i]; 859 } else if (regions[i].match(/(post|right)/)) { 860 haspost = regions[i]; 861 } 862 } 863 864 if (haspre !== false && haspost !== false) { 865 if (region === haspre) { 866 post = Y.one('#block-region-' + haspost); 867 if (post) { 868 post.insert(node, 'before'); 869 added = true; 870 } 871 } else { 872 pre = Y.one('#block-region-' + haspre); 873 if (pre) { 874 pre.insert(node, 'after'); 875 added = true; 876 } 877 } 878 } 879 if (added === false) { 880 Y.one('body').append(node); 881 } 882 this.set('node', node); 883 884 return node; 885 }, 886 887 /** 888 * Change the move icons to enhanced drag handles and changes the cursor to a move icon when over the header. 889 * @param M.core.dragdrop the block manager 890 * @method change_block_move_icons 891 */ 892 change_block_move_icons: function(manager) { 893 var handle, icon; 894 this.get('node').all('.' + CSS.BLOCK + ' a.' + CSS.EDITINGMOVE).each(function(moveicon) { 895 moveicon.setStyle('cursor', 'move'); 896 handle = manager.get_drag_handle(moveicon.getAttribute('title'), '', 'icon', true); 897 icon = handle.one('img'); 898 icon.addClass('iconsmall'); 899 icon.removeClass('icon'); 900 moveicon.replace(handle); 901 }); 902 }, 903 904 /** 905 * Returns the class name on the body that signifies the document knows about this region. 906 * @method get_has_region_class 907 * @return String 908 */ 909 get_has_region_class: function() { 910 return 'has-region-' + this.get('region'); 911 }, 912 913 /** 914 * Returns the class name to use on the body if the region contains no blocks. 915 * @method get_empty_region_class 916 * @return String 917 */ 918 get_empty_region_class: function() { 919 return 'empty-region-' + this.get('region'); 920 }, 921 922 /** 923 * Returns the class name to use on the body if the region contains blocks. 924 * @method get_used_region_class 925 * @return String 926 */ 927 get_used_region_class: function() { 928 return 'used-region-' + this.get('region'); 929 }, 930 931 /** 932 * Returns the node to use as the drop target for this region. 933 * @method get_droptarget 934 * @return Node 935 */ 936 get_droptarget: function() { 937 var node = this.get('node'); 938 if (node.test('[data-droptarget="1"]')) { 939 return node; 940 } 941 return node.one('[data-droptarget="1"]'); 942 }, 943 944 /** 945 * Enables the block region so that we can be sure the user can see it. 946 * This is done even if it is empty. 947 * @method enable 948 */ 949 enable: function() { 950 Y.one('body').addClass(this.get_used_region_class()).removeClass(this.get_empty_region_class()); 951 }, 952 953 /** 954 * Disables the region if it contains no blocks, essentially hiding it from the user. 955 * @method disable_if_required 956 */ 957 disable_if_required: function() { 958 if (this.get('node').all('.' + CSS.BLOCK).size() === 0) { 959 Y.one('body').addClass(this.get_empty_region_class()).removeClass(this.get_used_region_class()); 960 } 961 } 962 }; 963 Y.extend(BLOCKREGION, Y.Base, BLOCKREGION.prototype, { 964 NAME: 'core-blocks-dragdrop-blockregion', 965 ATTRS: { 966 967 /** 968 * The drag and drop manager that created this block region instance. 969 * @attribute manager 970 * @type M.core.blockdraganddrop.Manager 971 * @writeOnce 972 */ 973 manager: { 974 // Can only be set during initialisation and must be set then. 975 writeOnce: 'initOnly', 976 validator: function(value) { 977 return Y.Lang.isObject(value) && value instanceof MANAGER; 978 } 979 }, 980 981 /** 982 * The name of the block region this object represents. 983 * @attribute region 984 * @type String 985 * @writeOnce 986 */ 987 region: { 988 // Can only be set during initialisation and must be set then. 989 writeOnce: 'initOnly', 990 validator: function(value) { 991 return Y.Lang.isString(value); 992 } 993 }, 994 995 /** 996 * The node the block region HTML starts at.s 997 * @attribute region 998 * @type Y.Node 999 */ 1000 node: { 1001 validator: function(value) { 1002 return Y.Lang.isObject(value) || Y.Lang.isNull(value); 1003 } 1004 }, 1005 1006 /** 1007 * True if the block region currently contains blocks. 1008 * @attribute hasblocks 1009 * @type Boolean 1010 * @default false 1011 */ 1012 hasblocks: { 1013 value: false, 1014 validator: function(value) { 1015 return Y.Lang.isBoolean(value); 1016 } 1017 } 1018 } 1019 }); 1020 1021 1022 }, '@VERSION@', { 1023 "requires": [ 1024 "base", 1025 "node", 1026 "io", 1027 "dom", 1028 "dd", 1029 "dd-scroll", 1030 "moodle-core-dragdrop", 1031 "moodle-core-notification" 1032 ] 1033 });
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 |