[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 YUI.add('moodle-course-toolboxes', function (Y, NAME) { 2 3 /* eslint-disable no-unused-vars */ 4 /** 5 * Resource and activity toolbox class. 6 * 7 * This class is responsible for managing AJAX interactions with activities and resources 8 * when viewing a course in editing mode. 9 * 10 * @module moodle-course-toolboxes 11 * @namespace M.course.toolboxes 12 */ 13 14 // The CSS classes we use. 15 var CSS = { 16 ACTIVITYINSTANCE: 'activityinstance', 17 AVAILABILITYINFODIV: 'div.availabilityinfo', 18 CONTENTWITHOUTLINK: 'contentwithoutlink', 19 CONDITIONALHIDDEN: 'conditionalhidden', 20 DIMCLASS: 'dimmed', 21 DIMMEDTEXT: 'dimmed_text', 22 EDITINSTRUCTIONS: 'editinstructions', 23 HIDE: 'hide', 24 MODINDENTCOUNT: 'mod-indent-', 25 MODINDENTHUGE: 'mod-indent-huge', 26 MODULEIDPREFIX: 'module-', 27 SECTIONHIDDENCLASS: 'hidden', 28 SECTIONIDPREFIX: 'section-', 29 SHOW: 'editing_show' 30 }, 31 // The CSS selectors we use. 32 SELECTOR = { 33 ACTIONAREA: '.actions', 34 ACTIONLINKTEXT: '.actionlinktext', 35 ACTIVITYACTION: 'a.cm-edit-action[data-action]', 36 ACTIVITYICON: 'img.activityicon', 37 ACTIVITYINSTANCE: '.' + CSS.ACTIVITYINSTANCE, 38 ACTIVITYLINK: '.' + CSS.ACTIVITYINSTANCE + ' > a, .' + CSS.ACTIVITYINSTANCE + 39 ' > span[data-inplaceeditable] > a:not([data-inplaceeditablelink])', 40 ACTIVITYLI: 'li.activity', 41 COMMANDSPAN: '.commands', 42 CONTENTAFTERLINK: 'div.contentafterlink', 43 CONTENTWITHOUTLINK: 'div.contentwithoutlink', 44 GROUPINGLABEL: '.' + CSS.ACTIVITYINSTANCE + ' .groupinglabel', 45 HIDE: 'a.editing_hide', 46 HIGHLIGHT: 'a.editing_highlight', 47 INSTANCENAME: 'span.instancename', 48 MODINDENTDIV: '.mod-indent', 49 MODINDENTOUTER: '.mod-indent-outer', 50 PAGECONTENT: 'body', 51 SECTIONLI: 'li.section', 52 SHOW: 'a.' + CSS.SHOW, 53 SHOWHIDE: 'a.editing_showhide' 54 }, 55 INDENTLIMITS = { 56 MIN: 0, 57 MAX: 16 58 }, 59 BODY = Y.one(document.body); 60 61 // Setup the basic namespace. 62 M.course = M.course || {}; 63 64 /** 65 * The toolbox class is a generic class which should never be directly 66 * instantiated. Please extend it instead. 67 * 68 * @class toolbox 69 * @constructor 70 * @protected 71 * @extends Base 72 */ 73 var TOOLBOX = function() { 74 TOOLBOX.superclass.constructor.apply(this, arguments); 75 }; 76 77 Y.extend(TOOLBOX, Y.Base, { 78 /** 79 * Send a request using the REST API 80 * 81 * @method send_request 82 * @param {Object} data The data to submit with the AJAX request 83 * @param {Node} [statusspinner] A statusspinner which may contain a section loader 84 * @param {Function} success_callback The callback to use on success 85 * @param {Object} [optionalconfig] Any additional configuration to submit 86 * @chainable 87 */ 88 send_request: function(data, statusspinner, success_callback, optionalconfig) { 89 // Default data structure 90 if (!data) { 91 data = {}; 92 } 93 // Handle any variables which we must pass back through to 94 var pageparams = this.get('config').pageparams, 95 varname; 96 for (varname in pageparams) { 97 data[varname] = pageparams[varname]; 98 } 99 100 data.sesskey = M.cfg.sesskey; 101 data.courseId = this.get('courseid'); 102 103 var uri = M.cfg.wwwroot + this.get('ajaxurl'); 104 105 // Define the configuration to send with the request 106 var responsetext = []; 107 var config = { 108 method: 'POST', 109 data: data, 110 on: { 111 success: function(tid, response) { 112 try { 113 responsetext = Y.JSON.parse(response.responseText); 114 if (responsetext.error) { 115 new M.core.ajaxException(responsetext); 116 } 117 } catch (e) { 118 // Ignore. 119 } 120 121 // Run the callback if we have one. 122 if (success_callback) { 123 Y.bind(success_callback, this, responsetext)(); 124 } 125 126 if (statusspinner) { 127 window.setTimeout(function() { 128 statusspinner.hide(); 129 }, 400); 130 } 131 }, 132 failure: function(tid, response) { 133 if (statusspinner) { 134 statusspinner.hide(); 135 } 136 new M.core.ajaxException(response); 137 } 138 }, 139 context: this 140 }; 141 142 // Apply optional config 143 if (optionalconfig) { 144 for (varname in optionalconfig) { 145 config[varname] = optionalconfig[varname]; 146 } 147 } 148 149 if (statusspinner) { 150 statusspinner.show(); 151 } 152 153 // Send the request 154 Y.io(uri, config); 155 return this; 156 } 157 }, 158 { 159 NAME: 'course-toolbox', 160 ATTRS: { 161 /** 162 * The ID of the Moodle Course being edited. 163 * 164 * @attribute courseid 165 * @default 0 166 * @type Number 167 */ 168 courseid: { 169 'value': 0 170 }, 171 172 /** 173 * The Moodle course format. 174 * 175 * @attribute format 176 * @default 'topics' 177 * @type String 178 */ 179 format: { 180 'value': 'topics' 181 }, 182 /** 183 * The URL to use when submitting requests. 184 * @attribute ajaxurl 185 * @default null 186 * @type String 187 */ 188 ajaxurl: { 189 'value': null 190 }, 191 /** 192 * Any additional configuration passed when creating the instance. 193 * 194 * @attribute config 195 * @default {} 196 * @type Object 197 */ 198 config: { 199 'value': {} 200 } 201 } 202 } 203 ); 204 /* global TOOLBOX, BODY, SELECTOR, INDENTLIMITS */ 205 206 /** 207 * Resource and activity toolbox class. 208 * 209 * This class is responsible for managing AJAX interactions with activities and resources 210 * when viewing a course in editing mode. 211 * 212 * @module moodle-course-toolboxes 213 * @namespace M.course.toolboxes 214 */ 215 216 /** 217 * Resource and activity toolbox class. 218 * 219 * This is a class extending TOOLBOX containing code specific to resources 220 * 221 * This class is responsible for managing AJAX interactions with activities and resources 222 * when viewing a course in editing mode. 223 * 224 * @class resources 225 * @constructor 226 * @extends M.course.toolboxes.toolbox 227 */ 228 var RESOURCETOOLBOX = function() { 229 RESOURCETOOLBOX.superclass.constructor.apply(this, arguments); 230 }; 231 232 Y.extend(RESOURCETOOLBOX, TOOLBOX, { 233 /** 234 * No groups are being used. 235 * 236 * @property GROUPS_NONE 237 * @protected 238 * @type Number 239 */ 240 GROUPS_NONE: 0, 241 242 /** 243 * Separate groups are being used. 244 * 245 * @property GROUPS_SEPARATE 246 * @protected 247 * @type Number 248 */ 249 GROUPS_SEPARATE: 1, 250 251 /** 252 * Visible groups are being used. 253 * 254 * @property GROUPS_VISIBLE 255 * @protected 256 * @type Number 257 */ 258 GROUPS_VISIBLE: 2, 259 260 /** 261 * Initialize the resource toolbox 262 * 263 * For each activity the commands are updated and a reference to the activity is attached. 264 * This way it doesn't matter where the commands are going to called from they have a reference to the 265 * activity that they relate to. 266 * This is essential as some of the actions are displayed in an actionmenu which removes them from the 267 * page flow. 268 * 269 * This function also creates a single event delegate to manage all AJAX actions for all activities on 270 * the page. 271 * 272 * @method initializer 273 * @protected 274 */ 275 initializer: function() { 276 M.course.coursebase.register_module(this); 277 BODY.delegate('key', this.handle_data_action, 'down:enter', SELECTOR.ACTIVITYACTION, this); 278 Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this); 279 }, 280 281 /** 282 * Handles the delegation event. When this is fired someone has triggered an action. 283 * 284 * Note not all actions will result in an AJAX enhancement. 285 * 286 * @protected 287 * @method handle_data_action 288 * @param {EventFacade} ev The event that was triggered. 289 * @return {boolean} 290 */ 291 handle_data_action: function(ev) { 292 // We need to get the anchor element that triggered this event. 293 var node = ev.target; 294 if (!node.test('a')) { 295 node = node.ancestor(SELECTOR.ACTIVITYACTION); 296 } 297 298 // From the anchor we can get both the activity (added during initialisation) and the action being 299 // performed (added by the UI as a data attribute). 300 var action = node.getData('action'), 301 activity = node.ancestor(SELECTOR.ACTIVITYLI); 302 303 if (!node.test('a') || !action || !activity) { 304 // It wasn't a valid action node. 305 return; 306 } 307 308 // Switch based upon the action and do the desired thing. 309 switch (action) { 310 case 'moveleft': 311 case 'moveright': 312 // The user changing the indent of the activity. 313 this.change_indent(ev, node, activity, action); 314 break; 315 case 'delete': 316 // The user is deleting the activity. 317 this.delete_with_confirmation(ev, node, activity, action); 318 break; 319 case 'duplicate': 320 // The user is duplicating the activity. 321 this.duplicate(ev, node, activity, action); 322 break; 323 case 'hide': 324 case 'show': 325 // The user is changing the visibility of the activity. 326 this.change_visibility(ev, node, activity, action); 327 break; 328 case 'groupsseparate': 329 case 'groupsvisible': 330 case 'groupsnone': 331 // The user is changing the group mode. 332 this.change_groupmode(ev, node, activity, action); 333 break; 334 case 'move': 335 case 'update': 336 case 'assignroles': 337 break; 338 default: 339 // Nothing to do here! 340 break; 341 } 342 }, 343 344 /** 345 * Add a loading icon to the specified activity. 346 * 347 * The icon is added within the action area. 348 * 349 * @method add_spinner 350 * @param {Node} activity The activity to add a loading icon to 351 * @return {Node|null} The newly created icon, or null if the action area was not found. 352 */ 353 add_spinner: function(activity) { 354 var actionarea = activity.one(SELECTOR.ACTIONAREA); 355 if (actionarea) { 356 return M.util.add_spinner(Y, actionarea); 357 } 358 return null; 359 }, 360 361 /** 362 * Change the indent of the activity or resource. 363 * 364 * @method change_indent 365 * @protected 366 * @param {EventFacade} ev The event that was fired. 367 * @param {Node} button The button that triggered this action. 368 * @param {Node} activity The activity node that this action will be performed on. 369 * @param {String} action The action that has been requested. Will be 'moveleft' or 'moveright'. 370 */ 371 change_indent: function(ev, button, activity, action) { 372 // Prevent the default button action 373 ev.preventDefault(); 374 375 var direction = (action === 'moveleft') ? -1 : 1; 376 377 // And we need to determine the current and new indent level 378 var indentdiv = activity.one(SELECTOR.MODINDENTDIV), 379 indent = indentdiv.getAttribute('class').match(/mod-indent-(\d{1,})/), 380 oldindent = 0, 381 newindent; 382 383 if (indent) { 384 oldindent = parseInt(indent[1], 10); 385 } 386 newindent = oldindent + parseInt(direction, 10); 387 388 if (newindent < INDENTLIMITS.MIN || newindent > INDENTLIMITS.MAX) { 389 return; 390 } 391 392 if (indent) { 393 indentdiv.removeClass(indent[0]); 394 } 395 396 // Perform the move 397 indentdiv.addClass(CSS.MODINDENTCOUNT + newindent); 398 var data = { 399 'class': 'resource', 400 'field': 'indent', 401 'value': newindent, 402 'id': Y.Moodle.core_course.util.cm.getId(activity) 403 }; 404 var spinner = this.add_spinner(activity); 405 this.send_request(data, spinner); 406 407 var remainingmove; 408 409 // Handle removal/addition of the moveleft button. 410 if (newindent === INDENTLIMITS.MIN) { 411 button.addClass('hidden'); 412 remainingmove = activity.one('.editing_moveright'); 413 } else if (newindent > INDENTLIMITS.MIN && oldindent === INDENTLIMITS.MIN) { 414 button.ancestor('.menu').one('[data-action=moveleft]').removeClass('hidden'); 415 } 416 417 if (newindent === INDENTLIMITS.MAX) { 418 button.addClass('hidden'); 419 remainingmove = activity.one('.editing_moveleft'); 420 } else if (newindent < INDENTLIMITS.MAX && oldindent === INDENTLIMITS.MAX) { 421 button.ancestor('.menu').one('[data-action=moveright]').removeClass('hidden'); 422 } 423 424 // Handle massive indentation to match non-ajax display 425 var hashugeclass = indentdiv.hasClass(CSS.MODINDENTHUGE); 426 if (newindent > 15 && !hashugeclass) { 427 indentdiv.addClass(CSS.MODINDENTHUGE); 428 } else if (newindent <= 15 && hashugeclass) { 429 indentdiv.removeClass(CSS.MODINDENTHUGE); 430 } 431 432 if (ev.type && ev.type === "key" && remainingmove) { 433 remainingmove.focus(); 434 } 435 }, 436 437 /** 438 * Deletes the given activity or resource after confirmation. 439 * 440 * @protected 441 * @method delete_with_confirmation 442 * @param {EventFacade} ev The event that was fired. 443 * @param {Node} button The button that triggered this action. 444 * @param {Node} activity The activity node that this action will be performed on. 445 * @chainable 446 */ 447 delete_with_confirmation: function(ev, button, activity) { 448 // Prevent the default button action 449 ev.preventDefault(); 450 451 // Get the element we're working on 452 var element = activity, 453 // Create confirm string (different if element has or does not have name) 454 confirmstring = '', 455 plugindata = { 456 type: M.util.get_string('pluginname', element.getAttribute('class').match(/modtype_([^\s]*)/)[1]) 457 }; 458 if (Y.Moodle.core_course.util.cm.getName(element) !== null) { 459 plugindata.name = Y.Moodle.core_course.util.cm.getName(element); 460 confirmstring = M.util.get_string('deletechecktypename', 'moodle', plugindata); 461 } else { 462 confirmstring = M.util.get_string('deletechecktype', 'moodle', plugindata); 463 } 464 465 // Create the confirmation dialogue. 466 var confirm = new M.core.confirm({ 467 question: confirmstring, 468 modal: true, 469 visible: false 470 }); 471 confirm.show(); 472 473 // If it is confirmed. 474 confirm.on('complete-yes', function() { 475 476 // Actually remove the element. 477 element.remove(); 478 var data = { 479 'class': 'resource', 480 'action': 'DELETE', 481 'id': Y.Moodle.core_course.util.cm.getId(element) 482 }; 483 this.send_request(data); 484 if (M.core.actionmenu && M.core.actionmenu.instance) { 485 M.core.actionmenu.instance.hideMenu(ev); 486 } 487 488 }, this); 489 490 return this; 491 }, 492 493 /** 494 * Duplicates the activity. 495 * 496 * @method duplicate 497 * @protected 498 * @param {EventFacade} ev The event that was fired. 499 * @param {Node} button The button that triggered this action. 500 * @param {Node} activity The activity node that this action will be performed on. 501 * @chainable 502 */ 503 duplicate: function(ev, button, activity) { 504 // Prevent the default button action 505 ev.preventDefault(); 506 507 // Get the element we're working on 508 var element = activity; 509 510 // Add the lightbox. 511 var section = activity.ancestor(M.course.format.get_section_selector(Y)), 512 lightbox = M.util.add_lightbox(Y, section).show(); 513 514 // Build and send the request. 515 var data = { 516 'class': 'resource', 517 'field': 'duplicate', 518 'id': Y.Moodle.core_course.util.cm.getId(element), 519 'sr': button.getData('sr') 520 }; 521 this.send_request(data, lightbox, function(response) { 522 var newcm = Y.Node.create(response.fullcontent); 523 524 // Append to the section? 525 activity.insert(newcm, 'after'); 526 Y.use('moodle-course-coursebase', function() { 527 M.course.coursebase.invoke_function('setup_for_resource', newcm); 528 }); 529 if (M.core.actionmenu && M.core.actionmenu.newDOMNode) { 530 M.core.actionmenu.newDOMNode(newcm); 531 } 532 }); 533 return this; 534 }, 535 536 /** 537 * Changes the visibility of this activity or resource. 538 * 539 * @method change_visibility 540 * @protected 541 * @param {EventFacade} ev The event that was fired. 542 * @param {Node} button The button that triggered this action. 543 * @param {Node} activity The activity node that this action will be performed on. 544 * @param {String} action The action that has been requested. 545 * @chainable 546 */ 547 change_visibility: function(ev, button, activity, action) { 548 // Prevent the default button action 549 ev.preventDefault(); 550 551 // Get the element we're working on 552 var element = activity; 553 var value = this.handle_resource_dim(button, activity, action); 554 555 // Send the request 556 var data = { 557 'class': 'resource', 558 'field': 'visible', 559 'value': value, 560 'id': Y.Moodle.core_course.util.cm.getId(element) 561 }; 562 var spinner = this.add_spinner(element); 563 this.send_request(data, spinner); 564 565 return this; 566 }, 567 568 /** 569 * Handles the UI aspect of dimming the activity or resource. 570 * 571 * @method handle_resource_dim 572 * @protected 573 * @param {Node} button The button that triggered the action. 574 * @param {Node} activity The activity node that this action will be performed on. 575 * @param {String} action 'show' or 'hide'. 576 * @return {Number} 1 if we changed to visible, 0 if we were hiding. 577 */ 578 handle_resource_dim: function(button, activity, action) { 579 var toggleclass = CSS.DIMCLASS, 580 dimarea = activity.one([ 581 SELECTOR.ACTIVITYLINK, 582 SELECTOR.CONTENTWITHOUTLINK 583 ].join(', ')), 584 availabilityinfo = activity.one(CSS.AVAILABILITYINFODIV), 585 nextaction = (action === 'hide') ? 'show' : 'hide', 586 buttontext = button.one('span'), 587 newstring = M.util.get_string(nextaction, 'moodle'), 588 buttonimg = button.one('img'); 589 590 // Update button info. 591 buttonimg.setAttrs({ 592 'src': M.util.image_url('t/' + nextaction) 593 }); 594 595 if (Y.Lang.trim(button.getAttribute('title'))) { 596 button.setAttribute('title', newstring); 597 } 598 599 if (Y.Lang.trim(buttonimg.getAttribute('alt'))) { 600 buttonimg.setAttribute('alt', newstring); 601 } 602 603 button.replaceClass('editing_' + action, 'editing_' + nextaction); 604 button.setData('action', nextaction); 605 if (buttontext) { 606 buttontext.set('text', newstring); 607 } 608 609 if (activity.one(SELECTOR.CONTENTWITHOUTLINK)) { 610 dimarea = activity.one(SELECTOR.CONTENTWITHOUTLINK); 611 toggleclass = CSS.DIMMEDTEXT; 612 } 613 614 // If activity is conditionally hidden, then don't toggle. 615 if (!dimarea.hasClass(CSS.CONDITIONALHIDDEN)) { 616 if (action === 'hide') { 617 // Change the UI. 618 dimarea.addClass(toggleclass); 619 // We need to toggle dimming on the description too. 620 activity.all(SELECTOR.CONTENTAFTERLINK).addClass(CSS.DIMMEDTEXT); 621 activity.all(SELECTOR.GROUPINGLABEL).addClass(CSS.DIMMEDTEXT); 622 } else { 623 // Change the UI. 624 dimarea.removeClass(toggleclass); 625 // We need to toggle dimming on the description too. 626 activity.all(SELECTOR.CONTENTAFTERLINK).removeClass(CSS.DIMMEDTEXT); 627 activity.all(SELECTOR.GROUPINGLABEL).removeClass(CSS.DIMMEDTEXT); 628 } 629 } 630 // Toggle availablity info for conditional activities. 631 if (availabilityinfo) { 632 availabilityinfo.toggleClass(CSS.HIDE); 633 } 634 return (action === 'hide') ? 0 : 1; 635 }, 636 637 /** 638 * Changes the groupmode of the activity to the next groupmode in the sequence. 639 * 640 * @method change_groupmode 641 * @protected 642 * @param {EventFacade} ev The event that was fired. 643 * @param {Node} button The button that triggered this action. 644 * @param {Node} activity The activity node that this action will be performed on. 645 * @chainable 646 */ 647 change_groupmode: function(ev, button, activity) { 648 // Prevent the default button action. 649 ev.preventDefault(); 650 651 // Current Mode 652 var groupmode = parseInt(button.getData('nextgroupmode'), 10), 653 newtitle = '', 654 iconsrc = '', 655 newtitlestr, 656 data, 657 spinner, 658 nextgroupmode = groupmode + 1, 659 buttonimg = button.one('img'); 660 661 if (nextgroupmode > 2) { 662 nextgroupmode = 0; 663 } 664 665 if (groupmode === this.GROUPS_NONE) { 666 newtitle = 'groupsnone'; 667 iconsrc = M.util.image_url('i/groupn', 'moodle'); 668 } else if (groupmode === this.GROUPS_SEPARATE) { 669 newtitle = 'groupsseparate'; 670 iconsrc = M.util.image_url('i/groups', 'moodle'); 671 } else if (groupmode === this.GROUPS_VISIBLE) { 672 newtitle = 'groupsvisible'; 673 iconsrc = M.util.image_url('i/groupv', 'moodle'); 674 } 675 newtitlestr = M.util.get_string('clicktochangeinbrackets', 'moodle', M.util.get_string(newtitle, 'moodle')); 676 677 // Change the UI 678 var oldAction = button.getData('action'); 679 button.replaceClass('editing_' + oldAction, 'editing_' + newtitle); 680 buttonimg.setAttrs({ 681 'src': iconsrc 682 }); 683 if (Y.Lang.trim(button.getAttribute('title'))) { 684 button.setAttribute('title', newtitlestr).setData('action', newtitle).setData('nextgroupmode', nextgroupmode); 685 } 686 687 if (Y.Lang.trim(buttonimg.getAttribute('alt'))) { 688 buttonimg.setAttribute('alt', newtitlestr); 689 } 690 691 // And send the request 692 data = { 693 'class': 'resource', 694 'field': 'groupmode', 695 'value': groupmode, 696 'id': Y.Moodle.core_course.util.cm.getId(activity) 697 }; 698 699 spinner = this.add_spinner(activity); 700 this.send_request(data, spinner); 701 return this; 702 }, 703 704 /** 705 * Set the visibility of the specified resource to match the visible parameter. 706 * 707 * Note: This is not a toggle function and only changes the visibility 708 * in the browser (no ajax update is performed). 709 * 710 * @method set_visibility_resource_ui 711 * @param {object} args An object containing the required information to trigger a change. 712 * @param {Node} args.element The resource to toggle 713 * @param {Boolean} args.visible The target visibility 714 */ 715 set_visibility_resource_ui: function(args) { 716 var element = args.element, 717 buttonnode = element.one(SELECTOR.HIDE), 718 // By default we assume that the item is visible and we're going to hide it. 719 currentVisibility = true, 720 targetVisibility = false; 721 722 if (!buttonnode) { 723 // If the buttonnode was not found, try to find the HIDE button 724 // and change the target visibility setting to false. 725 buttonnode = element.one(SELECTOR.SHOW); 726 currentVisibility = false; 727 targetVisibility = true; 728 } 729 730 if (typeof args.visible !== 'undefined') { 731 // If we were provided with a visibility argument, use that instead. 732 targetVisibility = args.visible; 733 } 734 735 // Only trigger a change if necessary. 736 if (currentVisibility !== targetVisibility) { 737 var action = 'hide'; 738 if (targetVisibility) { 739 action = 'show'; 740 } 741 742 this.handle_resource_dim(buttonnode, element, action); 743 } 744 } 745 }, { 746 NAME: 'course-resource-toolbox', 747 ATTRS: { 748 } 749 }); 750 751 M.course.resource_toolbox = null; 752 M.course.init_resource_toolbox = function(config) { 753 M.course.resource_toolbox = new RESOURCETOOLBOX(config); 754 return M.course.resource_toolbox; 755 }; 756 /* global SELECTOR, TOOLBOX */ 757 758 /** 759 * Resource and activity toolbox class. 760 * 761 * This class is responsible for managing AJAX interactions with activities and resources 762 * when viewing a course in editing mode. 763 * 764 * @module moodle-course-toolboxes 765 * @namespace M.course.toolboxes 766 */ 767 768 /** 769 * Section toolbox class. 770 * 771 * This class is responsible for managing AJAX interactions with sections 772 * when viewing a course in editing mode. 773 * 774 * @class section 775 * @constructor 776 * @extends M.course.toolboxes.toolbox 777 */ 778 var SECTIONTOOLBOX = function() { 779 SECTIONTOOLBOX.superclass.constructor.apply(this, arguments); 780 }; 781 782 Y.extend(SECTIONTOOLBOX, TOOLBOX, { 783 /** 784 * Initialize the section toolboxes module. 785 * 786 * Updates all span.commands with relevant handlers and other required changes. 787 * 788 * @method initializer 789 * @protected 790 */ 791 initializer: function() { 792 M.course.coursebase.register_module(this); 793 794 // Section Highlighting. 795 Y.delegate('click', this.toggle_highlight, SELECTOR.PAGECONTENT, SELECTOR.SECTIONLI + ' ' + SELECTOR.HIGHLIGHT, this); 796 797 // Section Visibility. 798 Y.delegate('click', this.toggle_hide_section, SELECTOR.PAGECONTENT, SELECTOR.SECTIONLI + ' ' + SELECTOR.SHOWHIDE, this); 799 }, 800 801 toggle_hide_section: function(e) { 802 // Prevent the default button action. 803 e.preventDefault(); 804 805 // Get the section we're working on. 806 var section = e.target.ancestor(M.course.format.get_section_selector(Y)), 807 button = e.target.ancestor('a', true), 808 hideicon = button.one('img'), 809 buttontext = button.one('span'), 810 811 // The value to submit 812 value, 813 814 // The text for strings and images. Also determines the icon to display. 815 action, 816 nextaction; 817 818 if (!section.hasClass(CSS.SECTIONHIDDENCLASS)) { 819 section.addClass(CSS.SECTIONHIDDENCLASS); 820 value = 0; 821 action = 'hide'; 822 nextaction = 'show'; 823 } else { 824 section.removeClass(CSS.SECTIONHIDDENCLASS); 825 value = 1; 826 action = 'show'; 827 nextaction = 'hide'; 828 } 829 830 var newstring = M.util.get_string(nextaction + 'fromothers', 'format_' + this.get('format')); 831 hideicon.setAttrs({ 832 'alt': newstring, 833 'src': M.util.image_url('i/' + nextaction) 834 }); 835 button.set('title', newstring); 836 if (buttontext) { 837 buttontext.set('text', newstring); 838 } 839 840 // Change the show/hide status 841 var data = { 842 'class': 'section', 843 'field': 'visible', 844 'id': Y.Moodle.core_course.util.section.getId(section.ancestor(M.course.format.get_section_wrapper(Y), true)), 845 'value': value 846 }; 847 848 var lightbox = M.util.add_lightbox(Y, section); 849 lightbox.show(); 850 851 this.send_request(data, lightbox, function(response) { 852 var activities = section.all(SELECTOR.ACTIVITYLI); 853 activities.each(function(node) { 854 var button; 855 if (node.one(SELECTOR.SHOW)) { 856 button = node.one(SELECTOR.SHOW); 857 } else { 858 button = node.one(SELECTOR.HIDE); 859 } 860 var activityid = Y.Moodle.core_course.util.cm.getId(node); 861 862 // NOTE: resourcestotoggle is returned as a string instead 863 // of a Number so we must cast our activityid to a String. 864 if (Y.Array.indexOf(response.resourcestotoggle, "" + activityid) !== -1) { 865 M.course.resource_toolbox.handle_resource_dim(button, node, action); 866 } 867 }, this); 868 }); 869 }, 870 871 /** 872 * Toggle highlighting the current section. 873 * 874 * @method toggle_highlight 875 * @param {EventFacade} e 876 */ 877 toggle_highlight: function(e) { 878 // Prevent the default button action. 879 e.preventDefault(); 880 881 // Get the section we're working on. 882 var section = e.target.ancestor(M.course.format.get_section_selector(Y)); 883 var button = e.target.ancestor('a', true); 884 var buttonicon = button.one('img'); 885 var buttontext = button.one('span'); 886 887 // Determine whether the marker is currently set. 888 var togglestatus = section.hasClass('current'); 889 var value = 0; 890 891 // Set the current highlighted item text. 892 var old_string = M.util.get_string('markthistopic', 'moodle'); 893 894 var selectedpage = Y.one(SELECTOR.PAGECONTENT); 895 selectedpage 896 .all(M.course.format.get_section_selector(Y) + '.current ' + SELECTOR.HIGHLIGHT) 897 .set('title', old_string); 898 selectedpage 899 .all(M.course.format.get_section_selector(Y) + '.current ' + SELECTOR.HIGHLIGHT + ' span') 900 .set('text', M.util.get_string('highlight', 'moodle')); 901 selectedpage 902 .all(M.course.format.get_section_selector(Y) + '.current ' + SELECTOR.HIGHLIGHT + ' img') 903 .set('alt', old_string) 904 .set('src', M.util.image_url('i/marker')); 905 906 // Remove the highlighting from all sections. 907 selectedpage.all(M.course.format.get_section_selector(Y)) 908 .removeClass('current'); 909 910 // Then add it if required to the selected section. 911 if (!togglestatus) { 912 section.addClass('current'); 913 value = Y.Moodle.core_course.util.section.getId(section.ancestor(M.course.format.get_section_wrapper(Y), true)); 914 var new_string = M.util.get_string('markedthistopic', 'moodle'); 915 button 916 .set('title', new_string); 917 buttonicon 918 .set('alt', new_string) 919 .set('src', M.util.image_url('i/marked')); 920 if (buttontext) { 921 buttontext 922 .set('text', M.util.get_string('highlightoff', 'moodle')); 923 } 924 } 925 926 // Change the highlight status. 927 var data = { 928 'class': 'course', 929 'field': 'marker', 930 'value': value 931 }; 932 var lightbox = M.util.add_lightbox(Y, section); 933 lightbox.show(); 934 this.send_request(data, lightbox); 935 } 936 }, { 937 NAME: 'course-section-toolbox', 938 ATTRS: { 939 } 940 }); 941 942 M.course.init_section_toolbox = function(config) { 943 return new SECTIONTOOLBOX(config); 944 }; 945 946 947 }, '@VERSION@', {"requires": ["node", "base", "event-key", "node", "io", "moodle-course-coursebase", "moodle-course-util"]});
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 |