[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* global TOOLBOX, BODY, SELECTOR, INDENTLIMITS */ 2 3 /** 4 * Resource and activity toolbox class. 5 * 6 * This class is responsible for managing AJAX interactions with activities and resources 7 * when viewing a course in editing mode. 8 * 9 * @module moodle-course-toolboxes 10 * @namespace M.course.toolboxes 11 */ 12 13 /** 14 * Resource and activity toolbox class. 15 * 16 * This is a class extending TOOLBOX containing code specific to resources 17 * 18 * This class is responsible for managing AJAX interactions with activities and resources 19 * when viewing a course in editing mode. 20 * 21 * @class resources 22 * @constructor 23 * @extends M.course.toolboxes.toolbox 24 */ 25 var RESOURCETOOLBOX = function() { 26 RESOURCETOOLBOX.superclass.constructor.apply(this, arguments); 27 }; 28 29 Y.extend(RESOURCETOOLBOX, TOOLBOX, { 30 /** 31 * No groups are being used. 32 * 33 * @property GROUPS_NONE 34 * @protected 35 * @type Number 36 */ 37 GROUPS_NONE: 0, 38 39 /** 40 * Separate groups are being used. 41 * 42 * @property GROUPS_SEPARATE 43 * @protected 44 * @type Number 45 */ 46 GROUPS_SEPARATE: 1, 47 48 /** 49 * Visible groups are being used. 50 * 51 * @property GROUPS_VISIBLE 52 * @protected 53 * @type Number 54 */ 55 GROUPS_VISIBLE: 2, 56 57 /** 58 * Initialize the resource toolbox 59 * 60 * For each activity the commands are updated and a reference to the activity is attached. 61 * This way it doesn't matter where the commands are going to called from they have a reference to the 62 * activity that they relate to. 63 * This is essential as some of the actions are displayed in an actionmenu which removes them from the 64 * page flow. 65 * 66 * This function also creates a single event delegate to manage all AJAX actions for all activities on 67 * the page. 68 * 69 * @method initializer 70 * @protected 71 */ 72 initializer: function() { 73 M.course.coursebase.register_module(this); 74 BODY.delegate('key', this.handle_data_action, 'down:enter', SELECTOR.ACTIVITYACTION, this); 75 Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this); 76 }, 77 78 /** 79 * Handles the delegation event. When this is fired someone has triggered an action. 80 * 81 * Note not all actions will result in an AJAX enhancement. 82 * 83 * @protected 84 * @method handle_data_action 85 * @param {EventFacade} ev The event that was triggered. 86 * @return {boolean} 87 */ 88 handle_data_action: function(ev) { 89 // We need to get the anchor element that triggered this event. 90 var node = ev.target; 91 if (!node.test('a')) { 92 node = node.ancestor(SELECTOR.ACTIVITYACTION); 93 } 94 95 // From the anchor we can get both the activity (added during initialisation) and the action being 96 // performed (added by the UI as a data attribute). 97 var action = node.getData('action'), 98 activity = node.ancestor(SELECTOR.ACTIVITYLI); 99 100 if (!node.test('a') || !action || !activity) { 101 // It wasn't a valid action node. 102 return; 103 } 104 105 // Switch based upon the action and do the desired thing. 106 switch (action) { 107 case 'moveleft': 108 case 'moveright': 109 // The user changing the indent of the activity. 110 this.change_indent(ev, node, activity, action); 111 break; 112 case 'delete': 113 // The user is deleting the activity. 114 this.delete_with_confirmation(ev, node, activity, action); 115 break; 116 case 'duplicate': 117 // The user is duplicating the activity. 118 this.duplicate(ev, node, activity, action); 119 break; 120 case 'hide': 121 case 'show': 122 // The user is changing the visibility of the activity. 123 this.change_visibility(ev, node, activity, action); 124 break; 125 case 'groupsseparate': 126 case 'groupsvisible': 127 case 'groupsnone': 128 // The user is changing the group mode. 129 this.change_groupmode(ev, node, activity, action); 130 break; 131 case 'move': 132 case 'update': 133 case 'assignroles': 134 break; 135 default: 136 // Nothing to do here! 137 break; 138 } 139 }, 140 141 /** 142 * Add a loading icon to the specified activity. 143 * 144 * The icon is added within the action area. 145 * 146 * @method add_spinner 147 * @param {Node} activity The activity to add a loading icon to 148 * @return {Node|null} The newly created icon, or null if the action area was not found. 149 */ 150 add_spinner: function(activity) { 151 var actionarea = activity.one(SELECTOR.ACTIONAREA); 152 if (actionarea) { 153 return M.util.add_spinner(Y, actionarea); 154 } 155 return null; 156 }, 157 158 /** 159 * Change the indent of the activity or resource. 160 * 161 * @method change_indent 162 * @protected 163 * @param {EventFacade} ev The event that was fired. 164 * @param {Node} button The button that triggered this action. 165 * @param {Node} activity The activity node that this action will be performed on. 166 * @param {String} action The action that has been requested. Will be 'moveleft' or 'moveright'. 167 */ 168 change_indent: function(ev, button, activity, action) { 169 // Prevent the default button action 170 ev.preventDefault(); 171 172 var direction = (action === 'moveleft') ? -1 : 1; 173 174 // And we need to determine the current and new indent level 175 var indentdiv = activity.one(SELECTOR.MODINDENTDIV), 176 indent = indentdiv.getAttribute('class').match(/mod-indent-(\d{1,})/), 177 oldindent = 0, 178 newindent; 179 180 if (indent) { 181 oldindent = parseInt(indent[1], 10); 182 } 183 newindent = oldindent + parseInt(direction, 10); 184 185 if (newindent < INDENTLIMITS.MIN || newindent > INDENTLIMITS.MAX) { 186 return; 187 } 188 189 if (indent) { 190 indentdiv.removeClass(indent[0]); 191 } 192 193 // Perform the move 194 indentdiv.addClass(CSS.MODINDENTCOUNT + newindent); 195 var data = { 196 'class': 'resource', 197 'field': 'indent', 198 'value': newindent, 199 'id': Y.Moodle.core_course.util.cm.getId(activity) 200 }; 201 var spinner = this.add_spinner(activity); 202 this.send_request(data, spinner); 203 204 var remainingmove; 205 206 // Handle removal/addition of the moveleft button. 207 if (newindent === INDENTLIMITS.MIN) { 208 button.addClass('hidden'); 209 remainingmove = activity.one('.editing_moveright'); 210 } else if (newindent > INDENTLIMITS.MIN && oldindent === INDENTLIMITS.MIN) { 211 button.ancestor('.menu').one('[data-action=moveleft]').removeClass('hidden'); 212 } 213 214 if (newindent === INDENTLIMITS.MAX) { 215 button.addClass('hidden'); 216 remainingmove = activity.one('.editing_moveleft'); 217 } else if (newindent < INDENTLIMITS.MAX && oldindent === INDENTLIMITS.MAX) { 218 button.ancestor('.menu').one('[data-action=moveright]').removeClass('hidden'); 219 } 220 221 // Handle massive indentation to match non-ajax display 222 var hashugeclass = indentdiv.hasClass(CSS.MODINDENTHUGE); 223 if (newindent > 15 && !hashugeclass) { 224 indentdiv.addClass(CSS.MODINDENTHUGE); 225 } else if (newindent <= 15 && hashugeclass) { 226 indentdiv.removeClass(CSS.MODINDENTHUGE); 227 } 228 229 if (ev.type && ev.type === "key" && remainingmove) { 230 remainingmove.focus(); 231 } 232 }, 233 234 /** 235 * Deletes the given activity or resource after confirmation. 236 * 237 * @protected 238 * @method delete_with_confirmation 239 * @param {EventFacade} ev The event that was fired. 240 * @param {Node} button The button that triggered this action. 241 * @param {Node} activity The activity node that this action will be performed on. 242 * @chainable 243 */ 244 delete_with_confirmation: function(ev, button, activity) { 245 // Prevent the default button action 246 ev.preventDefault(); 247 248 // Get the element we're working on 249 var element = activity, 250 // Create confirm string (different if element has or does not have name) 251 confirmstring = '', 252 plugindata = { 253 type: M.util.get_string('pluginname', element.getAttribute('class').match(/modtype_([^\s]*)/)[1]) 254 }; 255 if (Y.Moodle.core_course.util.cm.getName(element) !== null) { 256 plugindata.name = Y.Moodle.core_course.util.cm.getName(element); 257 confirmstring = M.util.get_string('deletechecktypename', 'moodle', plugindata); 258 } else { 259 confirmstring = M.util.get_string('deletechecktype', 'moodle', plugindata); 260 } 261 262 // Create the confirmation dialogue. 263 var confirm = new M.core.confirm({ 264 question: confirmstring, 265 modal: true, 266 visible: false 267 }); 268 confirm.show(); 269 270 // If it is confirmed. 271 confirm.on('complete-yes', function() { 272 273 // Actually remove the element. 274 element.remove(); 275 var data = { 276 'class': 'resource', 277 'action': 'DELETE', 278 'id': Y.Moodle.core_course.util.cm.getId(element) 279 }; 280 this.send_request(data); 281 if (M.core.actionmenu && M.core.actionmenu.instance) { 282 M.core.actionmenu.instance.hideMenu(ev); 283 } 284 285 }, this); 286 287 return this; 288 }, 289 290 /** 291 * Duplicates the activity. 292 * 293 * @method duplicate 294 * @protected 295 * @param {EventFacade} ev The event that was fired. 296 * @param {Node} button The button that triggered this action. 297 * @param {Node} activity The activity node that this action will be performed on. 298 * @chainable 299 */ 300 duplicate: function(ev, button, activity) { 301 // Prevent the default button action 302 ev.preventDefault(); 303 304 // Get the element we're working on 305 var element = activity; 306 307 // Add the lightbox. 308 var section = activity.ancestor(M.course.format.get_section_selector(Y)), 309 lightbox = M.util.add_lightbox(Y, section).show(); 310 311 // Build and send the request. 312 var data = { 313 'class': 'resource', 314 'field': 'duplicate', 315 'id': Y.Moodle.core_course.util.cm.getId(element), 316 'sr': button.getData('sr') 317 }; 318 this.send_request(data, lightbox, function(response) { 319 var newcm = Y.Node.create(response.fullcontent); 320 321 // Append to the section? 322 activity.insert(newcm, 'after'); 323 Y.use('moodle-course-coursebase', function() { 324 M.course.coursebase.invoke_function('setup_for_resource', newcm); 325 }); 326 if (M.core.actionmenu && M.core.actionmenu.newDOMNode) { 327 M.core.actionmenu.newDOMNode(newcm); 328 } 329 }); 330 return this; 331 }, 332 333 /** 334 * Changes the visibility of this activity or resource. 335 * 336 * @method change_visibility 337 * @protected 338 * @param {EventFacade} ev The event that was fired. 339 * @param {Node} button The button that triggered this action. 340 * @param {Node} activity The activity node that this action will be performed on. 341 * @param {String} action The action that has been requested. 342 * @chainable 343 */ 344 change_visibility: function(ev, button, activity, action) { 345 // Prevent the default button action 346 ev.preventDefault(); 347 348 // Get the element we're working on 349 var element = activity; 350 var value = this.handle_resource_dim(button, activity, action); 351 352 // Send the request 353 var data = { 354 'class': 'resource', 355 'field': 'visible', 356 'value': value, 357 'id': Y.Moodle.core_course.util.cm.getId(element) 358 }; 359 var spinner = this.add_spinner(element); 360 this.send_request(data, spinner); 361 362 return this; 363 }, 364 365 /** 366 * Handles the UI aspect of dimming the activity or resource. 367 * 368 * @method handle_resource_dim 369 * @protected 370 * @param {Node} button The button that triggered the action. 371 * @param {Node} activity The activity node that this action will be performed on. 372 * @param {String} action 'show' or 'hide'. 373 * @return {Number} 1 if we changed to visible, 0 if we were hiding. 374 */ 375 handle_resource_dim: function(button, activity, action) { 376 var toggleclass = CSS.DIMCLASS, 377 dimarea = activity.one([ 378 SELECTOR.ACTIVITYLINK, 379 SELECTOR.CONTENTWITHOUTLINK 380 ].join(', ')), 381 availabilityinfo = activity.one(CSS.AVAILABILITYINFODIV), 382 nextaction = (action === 'hide') ? 'show' : 'hide', 383 buttontext = button.one('span'), 384 newstring = M.util.get_string(nextaction, 'moodle'), 385 buttonimg = button.one('img'); 386 387 // Update button info. 388 buttonimg.setAttrs({ 389 'src': M.util.image_url('t/' + nextaction) 390 }); 391 392 if (Y.Lang.trim(button.getAttribute('title'))) { 393 button.setAttribute('title', newstring); 394 } 395 396 if (Y.Lang.trim(buttonimg.getAttribute('alt'))) { 397 buttonimg.setAttribute('alt', newstring); 398 } 399 400 button.replaceClass('editing_' + action, 'editing_' + nextaction); 401 button.setData('action', nextaction); 402 if (buttontext) { 403 buttontext.set('text', newstring); 404 } 405 406 if (activity.one(SELECTOR.CONTENTWITHOUTLINK)) { 407 dimarea = activity.one(SELECTOR.CONTENTWITHOUTLINK); 408 toggleclass = CSS.DIMMEDTEXT; 409 } 410 411 // If activity is conditionally hidden, then don't toggle. 412 if (!dimarea.hasClass(CSS.CONDITIONALHIDDEN)) { 413 if (action === 'hide') { 414 // Change the UI. 415 dimarea.addClass(toggleclass); 416 // We need to toggle dimming on the description too. 417 activity.all(SELECTOR.CONTENTAFTERLINK).addClass(CSS.DIMMEDTEXT); 418 activity.all(SELECTOR.GROUPINGLABEL).addClass(CSS.DIMMEDTEXT); 419 } else { 420 // Change the UI. 421 dimarea.removeClass(toggleclass); 422 // We need to toggle dimming on the description too. 423 activity.all(SELECTOR.CONTENTAFTERLINK).removeClass(CSS.DIMMEDTEXT); 424 activity.all(SELECTOR.GROUPINGLABEL).removeClass(CSS.DIMMEDTEXT); 425 } 426 } 427 // Toggle availablity info for conditional activities. 428 if (availabilityinfo) { 429 availabilityinfo.toggleClass(CSS.HIDE); 430 } 431 return (action === 'hide') ? 0 : 1; 432 }, 433 434 /** 435 * Changes the groupmode of the activity to the next groupmode in the sequence. 436 * 437 * @method change_groupmode 438 * @protected 439 * @param {EventFacade} ev The event that was fired. 440 * @param {Node} button The button that triggered this action. 441 * @param {Node} activity The activity node that this action will be performed on. 442 * @chainable 443 */ 444 change_groupmode: function(ev, button, activity) { 445 // Prevent the default button action. 446 ev.preventDefault(); 447 448 // Current Mode 449 var groupmode = parseInt(button.getData('nextgroupmode'), 10), 450 newtitle = '', 451 iconsrc = '', 452 newtitlestr, 453 data, 454 spinner, 455 nextgroupmode = groupmode + 1, 456 buttonimg = button.one('img'); 457 458 if (nextgroupmode > 2) { 459 nextgroupmode = 0; 460 } 461 462 if (groupmode === this.GROUPS_NONE) { 463 newtitle = 'groupsnone'; 464 iconsrc = M.util.image_url('i/groupn', 'moodle'); 465 } else if (groupmode === this.GROUPS_SEPARATE) { 466 newtitle = 'groupsseparate'; 467 iconsrc = M.util.image_url('i/groups', 'moodle'); 468 } else if (groupmode === this.GROUPS_VISIBLE) { 469 newtitle = 'groupsvisible'; 470 iconsrc = M.util.image_url('i/groupv', 'moodle'); 471 } 472 newtitlestr = M.util.get_string('clicktochangeinbrackets', 'moodle', M.util.get_string(newtitle, 'moodle')); 473 474 // Change the UI 475 var oldAction = button.getData('action'); 476 button.replaceClass('editing_' + oldAction, 'editing_' + newtitle); 477 buttonimg.setAttrs({ 478 'src': iconsrc 479 }); 480 if (Y.Lang.trim(button.getAttribute('title'))) { 481 button.setAttribute('title', newtitlestr).setData('action', newtitle).setData('nextgroupmode', nextgroupmode); 482 } 483 484 if (Y.Lang.trim(buttonimg.getAttribute('alt'))) { 485 buttonimg.setAttribute('alt', newtitlestr); 486 } 487 488 // And send the request 489 data = { 490 'class': 'resource', 491 'field': 'groupmode', 492 'value': groupmode, 493 'id': Y.Moodle.core_course.util.cm.getId(activity) 494 }; 495 496 spinner = this.add_spinner(activity); 497 this.send_request(data, spinner); 498 return this; 499 }, 500 501 /** 502 * Set the visibility of the specified resource to match the visible parameter. 503 * 504 * Note: This is not a toggle function and only changes the visibility 505 * in the browser (no ajax update is performed). 506 * 507 * @method set_visibility_resource_ui 508 * @param {object} args An object containing the required information to trigger a change. 509 * @param {Node} args.element The resource to toggle 510 * @param {Boolean} args.visible The target visibility 511 */ 512 set_visibility_resource_ui: function(args) { 513 var element = args.element, 514 buttonnode = element.one(SELECTOR.HIDE), 515 // By default we assume that the item is visible and we're going to hide it. 516 currentVisibility = true, 517 targetVisibility = false; 518 519 if (!buttonnode) { 520 // If the buttonnode was not found, try to find the HIDE button 521 // and change the target visibility setting to false. 522 buttonnode = element.one(SELECTOR.SHOW); 523 currentVisibility = false; 524 targetVisibility = true; 525 } 526 527 if (typeof args.visible !== 'undefined') { 528 // If we were provided with a visibility argument, use that instead. 529 targetVisibility = args.visible; 530 } 531 532 // Only trigger a change if necessary. 533 if (currentVisibility !== targetVisibility) { 534 var action = 'hide'; 535 if (targetVisibility) { 536 action = 'show'; 537 } 538 539 this.handle_resource_dim(buttonnode, element, action); 540 } 541 } 542 }, { 543 NAME: 'course-resource-toolbox', 544 ATTRS: { 545 } 546 }); 547 548 M.course.resource_toolbox = null; 549 M.course.init_resource_toolbox = function(config) { 550 M.course.resource_toolbox = new RESOURCETOOLBOX(config); 551 return M.course.resource_toolbox; 552 };
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 |