[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
1 /** 2 * Resource and activity toolbox class. 3 * 4 * This class is responsible for managing AJAX interactions with activities and resources 5 * when viewing a quiz in editing mode. 6 * 7 * @module mod_quiz-resource-toolbox 8 * @namespace M.mod_quiz.resource_toolbox 9 */ 10 11 /** 12 * Resource and activity toolbox class. 13 * 14 * This is a class extending TOOLBOX containing code specific to resources 15 * 16 * This class is responsible for managing AJAX interactions with activities and resources 17 * when viewing a quiz in editing mode. 18 * 19 * @class resources 20 * @constructor 21 * @extends M.course.toolboxes.toolbox 22 */ 23 var RESOURCETOOLBOX = function() { 24 RESOURCETOOLBOX.superclass.constructor.apply(this, arguments); 25 }; 26 27 Y.extend(RESOURCETOOLBOX, TOOLBOX, { 28 /** 29 * An Array of events added when editing a max mark field. 30 * These should all be detached when editing is complete. 31 * 32 * @property editmaxmarkevents 33 * @protected 34 * @type Array 35 * @protected 36 */ 37 editmaxmarkevents: [], 38 39 /** 40 * 41 */ 42 NODE_PAGE: 1, 43 NODE_SLOT: 2, 44 NODE_JOIN: 3, 45 46 /** 47 * Initialize the resource toolbox 48 * 49 * For each activity the commands are updated and a reference to the activity is attached. 50 * This way it doesn't matter where the commands are going to called from they have a reference to the 51 * activity that they relate to. 52 * This is essential as some of the actions are displayed in an actionmenu which removes them from the 53 * page flow. 54 * 55 * This function also creates a single event delegate to manage all AJAX actions for all activities on 56 * the page. 57 * 58 * @method initializer 59 * @protected 60 */ 61 initializer: function() { 62 M.mod_quiz.quizbase.register_module(this); 63 BODY.delegate('key', this.handle_data_action, 'down:enter', SELECTOR.ACTIVITYACTION, this); 64 Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this); 65 }, 66 67 /** 68 * Handles the delegation event. When this is fired someone has triggered an action. 69 * 70 * Note not all actions will result in an AJAX enhancement. 71 * 72 * @protected 73 * @method handle_data_action 74 * @param {EventFacade} ev The event that was triggered. 75 * @returns {boolean} 76 */ 77 handle_data_action: function(ev) { 78 // We need to get the anchor element that triggered this event. 79 var node = ev.target; 80 if (!node.test('a')) { 81 node = node.ancestor(SELECTOR.ACTIVITYACTION); 82 } 83 84 // From the anchor we can get both the activity (added during initialisation) and the action being 85 // performed (added by the UI as a data attribute). 86 var action = node.getData('action'), 87 activity = node.ancestor(SELECTOR.ACTIVITYLI); 88 89 if (!node.test('a') || !action || !activity) { 90 // It wasn't a valid action node. 91 return; 92 } 93 94 // Switch based upon the action and do the desired thing. 95 switch (action) { 96 case 'editmaxmark': 97 // The user wishes to edit the maxmark of the resource. 98 this.edit_maxmark(ev, node, activity, action); 99 break; 100 case 'delete': 101 // The user is deleting the activity. 102 this.delete_with_confirmation(ev, node, activity, action); 103 break; 104 case 'addpagebreak': 105 case 'removepagebreak': 106 // The user is adding or removing a page break. 107 this.update_page_break(ev, node, activity, action); 108 break; 109 default: 110 // Nothing to do here! 111 break; 112 } 113 }, 114 115 /** 116 * Add a loading icon to the specified activity. 117 * 118 * The icon is added within the action area. 119 * 120 * @method add_spinner 121 * @param {Node} activity The activity to add a loading icon to 122 * @return {Node|null} The newly created icon, or null if the action area was not found. 123 */ 124 add_spinner: function(activity) { 125 var actionarea = activity.one(SELECTOR.ACTIONAREA); 126 if (actionarea) { 127 return M.util.add_spinner(Y, actionarea); 128 } 129 return null; 130 }, 131 132 /** 133 * Deletes the given activity or resource after confirmation. 134 * 135 * @protected 136 * @method delete_with_confirmation 137 * @param {EventFacade} ev The event that was fired. 138 * @param {Node} button The button that triggered this action. 139 * @param {Node} activity The activity node that this action will be performed on. 140 * @chainable 141 */ 142 delete_with_confirmation: function(ev, button, activity) { 143 // Prevent the default button action. 144 ev.preventDefault(); 145 146 // Get the element we're working on. 147 var element = activity, 148 // Create confirm string (different if element has or does not have name) 149 confirmstring = '', 150 qtypename = M.util.get_string('pluginname', 151 'qtype_' + element.getAttribute('class').match(/qtype_([^\s]*)/)[1]); 152 confirmstring = M.util.get_string('confirmremovequestion', 'quiz', qtypename); 153 154 // Create the confirmation dialogue. 155 var confirm = new M.core.confirm({ 156 question: confirmstring, 157 modal: true 158 }); 159 160 // If it is confirmed. 161 confirm.on('complete-yes', function() { 162 163 var spinner = this.add_spinner(element); 164 var data = { 165 'class': 'resource', 166 'action': 'DELETE', 167 'id': Y.Moodle.mod_quiz.util.slot.getId(element) 168 }; 169 this.send_request(data, spinner, function(response) { 170 if (response.deleted) { 171 // Actually remove the element. 172 Y.Moodle.mod_quiz.util.slot.remove(element); 173 this.reorganise_edit_page(); 174 if (M.core.actionmenu && M.core.actionmenu.instance) { 175 M.core.actionmenu.instance.hideMenu(); 176 } 177 } else { 178 window.location.reload(true); 179 } 180 }); 181 182 }, this); 183 184 return this; 185 }, 186 187 188 /** 189 * Edit the maxmark for the resource 190 * 191 * @protected 192 * @method edit_maxmark 193 * @param {EventFacade} ev The event that was fired. 194 * @param {Node} button The button that triggered this action. 195 * @param {Node} activity The activity node that this action will be performed on. 196 * @param {String} action The action that has been requested. 197 * @return Boolean 198 */ 199 edit_maxmark : function(ev, button, activity) { 200 // Get the element we're working on 201 var activityid = Y.Moodle.mod_quiz.util.slot.getId(activity), 202 instancemaxmark = activity.one(SELECTOR.INSTANCEMAXMARK), 203 instance = activity.one(SELECTOR.ACTIVITYINSTANCE), 204 currentmaxmark = instancemaxmark.get('firstChild'), 205 oldmaxmark = currentmaxmark.get('data'), 206 maxmarktext = oldmaxmark, 207 thisevent, 208 anchor = instancemaxmark,// Grab the anchor so that we can swap it with the edit form. 209 data = { 210 'class' : 'resource', 211 'field' : 'getmaxmark', 212 'id' : activityid 213 }; 214 215 // Prevent the default actions. 216 ev.preventDefault(); 217 218 this.send_request(data, null, function(response) { 219 if (M.core.actionmenu && M.core.actionmenu.instance) { 220 M.core.actionmenu.instance.hideMenu(); 221 } 222 223 // Try to retrieve the existing string from the server. 224 if (response.instancemaxmark) { 225 maxmarktext = response.instancemaxmark; 226 } 227 228 // Create the editor and submit button. 229 var editform = Y.Node.create('<form action="#" />'); 230 var editinstructions = Y.Node.create('<span class="' + CSS.EDITINSTRUCTIONS + '" id="id_editinstructions" />') 231 .set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle')); 232 var editor = Y.Node.create('<input name="maxmark" type="text" class="' + CSS.TITLEEDITOR + '" />').setAttrs({ 233 'value' : maxmarktext, 234 'autocomplete' : 'off', 235 'aria-describedby' : 'id_editinstructions', 236 'maxLength' : '12', 237 'size' : parseInt(this.get('config').questiondecimalpoints, 10) + 2 238 }); 239 240 // Clear the existing content and put the editor in. 241 editform.appendChild(editor); 242 editform.setData('anchor', anchor); 243 instance.insert(editinstructions, 'before'); 244 anchor.replace(editform); 245 246 // Force the editing instruction to match the mod-indent position. 247 var padside = 'left'; 248 if (right_to_left()) { 249 padside = 'right'; 250 } 251 252 // We hide various components whilst editing: 253 activity.addClass(CSS.EDITINGMAXMARK); 254 255 // Focus and select the editor text. 256 editor.focus().select(); 257 258 // Cancel the edit if we lose focus or the escape key is pressed. 259 thisevent = editor.on('blur', this.edit_maxmark_cancel, this, activity, false); 260 this.editmaxmarkevents.push(thisevent); 261 thisevent = editor.on('key', this.edit_maxmark_cancel, 'esc', this, activity, true); 262 this.editmaxmarkevents.push(thisevent); 263 264 // Handle form submission. 265 thisevent = editform.on('submit', this.edit_maxmark_submit, this, activity, oldmaxmark); 266 this.editmaxmarkevents.push(thisevent); 267 }); 268 }, 269 270 /** 271 * Handles the submit event when editing the activity or resources maxmark. 272 * 273 * @protected 274 * @method edit_maxmark_submit 275 * @param {EventFacade} ev The event that triggered this. 276 * @param {Node} activity The activity whose maxmark we are altering. 277 * @param {String} originalmaxmark The original maxmark the activity or resource had. 278 */ 279 edit_maxmark_submit : function(ev, activity, originalmaxmark) { 280 // We don't actually want to submit anything. 281 ev.preventDefault(); 282 var newmaxmark = Y.Lang.trim(activity.one(SELECTOR.ACTIVITYFORM + ' ' + SELECTOR.ACTIVITYMAXMARK).get('value')); 283 var spinner = this.add_spinner(activity); 284 this.edit_maxmark_clear(activity); 285 activity.one(SELECTOR.INSTANCEMAXMARK).setContent(newmaxmark); 286 if (newmaxmark !== null && newmaxmark !== "" && newmaxmark !== originalmaxmark) { 287 var data = { 288 'class' : 'resource', 289 'field' : 'updatemaxmark', 290 'maxmark' : newmaxmark, 291 'id' : Y.Moodle.mod_quiz.util.slot.getId(activity) 292 }; 293 this.send_request(data, spinner, function(response) { 294 if (response.instancemaxmark) { 295 activity.one(SELECTOR.INSTANCEMAXMARK).setContent(response.instancemaxmark); 296 } 297 }); 298 } 299 }, 300 301 /** 302 * Handles the cancel event when editing the activity or resources maxmark. 303 * 304 * @protected 305 * @method edit_maxmark_cancel 306 * @param {EventFacade} ev The event that triggered this. 307 * @param {Node} activity The activity whose maxmark we are altering. 308 * @param {Boolean} preventdefault If true we should prevent the default action from occuring. 309 */ 310 edit_maxmark_cancel : function(ev, activity, preventdefault) { 311 if (preventdefault) { 312 ev.preventDefault(); 313 } 314 this.edit_maxmark_clear(activity); 315 }, 316 317 /** 318 * Handles clearing the editing UI and returning things to the original state they were in. 319 * 320 * @protected 321 * @method edit_maxmark_clear 322 * @param {Node} activity The activity whose maxmark we were altering. 323 */ 324 edit_maxmark_clear : function(activity) { 325 // Detach all listen events to prevent duplicate triggers 326 new Y.EventHandle(this.editmaxmarkevents).detach(); 327 328 var editform = activity.one(SELECTOR.ACTIVITYFORM), 329 instructions = activity.one('#id_editinstructions'); 330 if (editform) { 331 editform.replace(editform.getData('anchor')); 332 } 333 if (instructions) { 334 instructions.remove(); 335 } 336 337 // Remove the editing class again to revert the display. 338 activity.removeClass(CSS.EDITINGMAXMARK); 339 340 // Refocus the link which was clicked originally so the user can continue using keyboard nav. 341 Y.later(100, this, function() { 342 activity.one(SELECTOR.EDITMAXMARK).focus(); 343 }); 344 345 // This hack is to keep Behat happy until they release a version of 346 // MinkSelenium2Driver that fixes 347 // https://github.com/Behat/MinkSelenium2Driver/issues/80. 348 if (!Y.one('input[name=maxmark')) { 349 Y.one('body').append('<input type="text" name="maxmark" style="display: none">'); 350 } 351 }, 352 353 /** 354 * Joins or separates the given slot with the page of the previous slot. Reorders the pages of 355 * the other slots 356 * 357 * @protected 358 * @method update_page_break 359 * @param {EventFacade} ev The event that was fired. 360 * @param {Node} button The button that triggered this action. 361 * @param {Node} activity The activity node that this action will be performed on. 362 * @chainable 363 */ 364 update_page_break: function(ev, button, activity, action) { 365 // Prevent the default button action 366 ev.preventDefault(); 367 368 nextactivity = activity.next('li.activity.slot'); 369 var spinner = this.add_spinner(nextactivity), 370 slotid = 0; 371 var value = action === 'removepagebreak' ? 1 : 2; 372 373 var data = { 374 'class': 'resource', 375 'field': 'updatepagebreak', 376 'id': slotid, 377 'value': value 378 }; 379 380 slotid = Y.Moodle.mod_quiz.util.slot.getId(nextactivity); 381 if (slotid) { 382 data.id = Number(slotid); 383 } 384 this.send_request(data, spinner, function(response) { 385 if (response.slots) { 386 if (action === 'addpagebreak') { 387 Y.Moodle.mod_quiz.util.page.add(activity); 388 } else { 389 var page = activity.next(Y.Moodle.mod_quiz.util.page.SELECTORS.PAGE); 390 Y.Moodle.mod_quiz.util.page.remove(page, true); 391 } 392 this.reorganise_edit_page(); 393 } else { 394 window.location.reload(true); 395 } 396 }); 397 398 return this; 399 }, 400 401 /** 402 * Reorganise the UI after every edit action. 403 * 404 * @protected 405 * @method reorganise_edit_page 406 */ 407 reorganise_edit_page: function() { 408 Y.Moodle.mod_quiz.util.slot.reorderSlots(); 409 Y.Moodle.mod_quiz.util.slot.reorderPageBreaks(); 410 Y.Moodle.mod_quiz.util.page.reorderPages(); 411 }, 412 413 NAME : 'mod_quiz-resource-toolbox', 414 ATTRS : { 415 courseid : { 416 'value' : 0 417 }, 418 quizid : { 419 'value' : 0 420 } 421 } 422 }); 423 424 M.mod_quiz.resource_toolbox = null; 425 M.mod_quiz.init_resource_toolbox = function(config) { 426 M.mod_quiz.resource_toolbox = new RESOURCETOOLBOX(config); 427 return M.mod_quiz.resource_toolbox; 428 };
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 |