[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 YUI.add('moodle-core-chooserdialogue', function (Y, NAME) { 2 3 /** 4 * A type of dialogue used as for choosing options. 5 * 6 * @module moodle-core-chooserdialogue 7 */ 8 9 /** 10 * A type of dialogue used as for choosing options. 11 * 12 * @constructor 13 * @class M.core.chooserdialogue 14 */ 15 var CHOOSERDIALOGUE = function() { 16 CHOOSERDIALOGUE.superclass.constructor.apply(this, arguments); 17 }; 18 19 Y.extend(CHOOSERDIALOGUE, Y.Base, { 20 // The panel widget 21 panel: null, 22 23 // The submit button - we disable this until an element is set 24 submitbutton: null, 25 26 // The chooserdialogue container 27 container: null, 28 29 // Any event listeners we may need to cancel later 30 listenevents: [], 31 32 bodycontent: null, 33 headercontent: null, 34 instanceconfig: null, 35 36 // The hidden field storing the disabled element values for submission. 37 hiddenRadioValue: null, 38 39 setup_chooser_dialogue: function(bodycontent, headercontent, config) { 40 this.bodycontent = bodycontent; 41 this.headercontent = headercontent; 42 this.instanceconfig = config; 43 }, 44 45 prepare_chooser: function() { 46 if (this.panel) { 47 return; 48 } 49 50 // Ensure that we're showing the JS version of the chooser. 51 Y.one(Y.config.doc.body).addClass('jschooser'); 52 53 // Set Default options 54 var paramkey, 55 params = { 56 bodyContent: this.bodycontent.get('innerHTML'), 57 headerContent: this.headercontent.get('innerHTML'), 58 width: '540px', 59 draggable: true, 60 visible: false, // Hide by default 61 zindex: 100, // Display in front of other items 62 modal: true, // This dialogue should be modal. 63 shim: true, 64 closeButtonTitle: this.get('closeButtonTitle'), 65 focusOnPreviousTargetAfterHide: true, 66 render: false, 67 extraClasses: this._getClassNames() 68 }; 69 70 // Override with additional options 71 for (paramkey in this.instanceconfig) { 72 params[paramkey] = this.instanceconfig[paramkey]; 73 } 74 75 // Create the panel 76 this.panel = new M.core.dialogue(params); 77 78 // Remove the template for the chooser 79 this.bodycontent.remove(); 80 this.headercontent.remove(); 81 82 // Hide and then render the panel 83 this.panel.hide(); 84 this.panel.render(); 85 86 // Set useful links. 87 this.container = this.panel.get('boundingBox').one('.choosercontainer'); 88 this.options = this.container.all('.option input[type=radio]'); 89 90 // The hidden form element we use when submitting. 91 this.hiddenRadioValue = Y.Node.create('<input type="hidden" value="" />'); 92 this.container.one('form').appendChild(this.hiddenRadioValue); 93 94 95 // Add the chooserdialogue class to the container for styling 96 this.panel.get('boundingBox').addClass('chooserdialogue'); 97 }, 98 99 /** 100 * Display the module chooser 101 * 102 * @method display_chooser 103 * @param {EventFacade} e Triggering Event 104 */ 105 display_chooser: function(e) { 106 var bb, dialogue, thisevent; 107 this.prepare_chooser(); 108 109 // Stop the default event actions before we proceed 110 e.preventDefault(); 111 112 bb = this.panel.get('boundingBox'); 113 dialogue = this.container.one('.alloptions'); 114 115 // This will detect a change in orientation and retrigger centering 116 thisevent = Y.one('document').on('orientationchange', function() { 117 this.center_dialogue(dialogue); 118 }, this); 119 this.listenevents.push(thisevent); 120 121 // Detect window resizes (most browsers) 122 thisevent = Y.one('window').on('resize', function() { 123 this.center_dialogue(dialogue); 124 }, this); 125 this.listenevents.push(thisevent); 126 127 // These will trigger a check_options call to display the correct help 128 thisevent = this.container.on('click', this.check_options, this); 129 this.listenevents.push(thisevent); 130 thisevent = this.container.on('key_up', this.check_options, this); 131 this.listenevents.push(thisevent); 132 thisevent = this.container.on('dblclick', function(e) { 133 if (e.target.ancestor('div.option')) { 134 this.check_options(); 135 136 // Prevent duplicate submissions 137 this.submitbutton.setAttribute('disabled', 'disabled'); 138 this.options.setAttribute('disabled', 'disabled'); 139 this.cancel_listenevents(); 140 141 this.container.one('form').submit(); 142 } 143 }, this); 144 this.listenevents.push(thisevent); 145 146 this.container.one('form').on('submit', function() { 147 // Prevent duplicate submissions on submit 148 this.submitbutton.setAttribute('disabled', 'disabled'); 149 this.options.setAttribute('disabled', 'disabled'); 150 this.cancel_listenevents(); 151 }, this); 152 153 // Hook onto the cancel button to hide the form 154 thisevent = this.container.one('.addcancel').on('click', this.cancel_popup, this); 155 this.listenevents.push(thisevent); 156 157 // Hide will be managed by cancel_popup after restoring the body overflow 158 thisevent = bb.one('button.closebutton').on('click', this.cancel_popup, this); 159 this.listenevents.push(thisevent); 160 161 // Grab global keyup events and handle them 162 thisevent = Y.one('document').on('keydown', this.handle_key_press, this); 163 this.listenevents.push(thisevent); 164 165 // Add references to various elements we adjust 166 this.submitbutton = this.container.one('.submitbutton'); 167 168 // Disable the submit element until the user makes a selection 169 this.submitbutton.set('disabled', 'true'); 170 171 // Ensure that the options are shown 172 this.options.removeAttribute('disabled'); 173 174 // Display the panel 175 this.panel.show(e); 176 177 // Re-centre the dialogue after we've shown it. 178 this.center_dialogue(dialogue); 179 180 // Finally, focus the first radio element - this enables form selection via the keyboard 181 this.container.one('.option input[type=radio]').focus(); 182 183 // Trigger check_options to set the initial jumpurl 184 this.check_options(); 185 }, 186 187 /** 188 * Cancel any listen events in the listenevents queue 189 * 190 * Several locations add event handlers which should only be called before the form is submitted. This provides 191 * a way of cancelling those events. 192 * 193 * @method cancel_listenevents 194 */ 195 cancel_listenevents: function() { 196 // Detach all listen events to prevent duplicate triggers 197 var thisevent; 198 while (this.listenevents.length) { 199 thisevent = this.listenevents.shift(); 200 thisevent.detach(); 201 } 202 }, 203 204 /** 205 * Calculate the optimum height of the chooser dialogue 206 * 207 * This tries to set a sensible maximum and minimum to ensure that some options are always shown, and preferably 208 * all, whilst fitting the box within the current viewport. 209 * 210 * @method center_dialogue 211 * @param Node {dialogue} Y.Node The dialogue 212 */ 213 center_dialogue: function(dialogue) { 214 var bb = this.panel.get('boundingBox'), 215 winheight = bb.get('winHeight'), 216 newheight, totalheight; 217 218 if (this.panel.shouldResizeFullscreen()) { 219 // No custom sizing required for a fullscreen dialog. 220 return; 221 } 222 223 // Try and set a sensible max-height -- this must be done before setting the top 224 // Set a default height of 640px 225 newheight = this.get('maxheight'); 226 if (winheight <= newheight) { 227 // Deal with smaller window sizes 228 if (winheight <= this.get('minheight')) { 229 newheight = this.get('minheight'); 230 } else { 231 newheight = winheight; 232 } 233 } 234 235 // If the dialogue is larger than a reasonable minimum height, we 236 // disable the page scrollbars. 237 if (newheight > this.get('minheight')) { 238 // Disable the page scrollbars. 239 if (this.panel.lockScroll && !this.panel.lockScroll.isActive()) { 240 this.panel.lockScroll.enableScrollLock(true); 241 } 242 } else { 243 // Re-enable the page scrollbars. 244 if (this.panel.lockScroll && this.panel.lockScroll.isActive()) { 245 this.panel.lockScroll.disableScrollLock(); 246 } 247 } 248 249 // Take off 15px top and bottom for borders, plus 40px each for the title and button area before setting the 250 // new max-height 251 totalheight = newheight; 252 newheight = newheight - (15 + 15 + 40 + 40); 253 dialogue.setStyle('maxHeight', newheight + 'px'); 254 255 var dialogueheight = bb.getStyle('height'); 256 if (dialogueheight.match(/.*px$/)) { 257 dialogueheight = dialogueheight.replace(/px$/, ''); 258 } else { 259 dialogueheight = totalheight; 260 } 261 262 if (dialogueheight < this.get('baseheight')) { 263 dialogueheight = this.get('baseheight'); 264 dialogue.setStyle('height', dialogueheight + 'px'); 265 } 266 267 this.panel.centerDialogue(); 268 }, 269 270 handle_key_press: function(e) { 271 if (e.keyCode === 27) { 272 this.cancel_popup(e); 273 } 274 }, 275 276 cancel_popup: function(e) { 277 // Prevent normal form submission before hiding 278 e.preventDefault(); 279 this.hide(); 280 }, 281 282 hide: function() { 283 // Cancel all listen events 284 this.cancel_listenevents(); 285 286 this.container.detachAll(); 287 this.panel.hide(); 288 }, 289 290 check_options: function() { 291 // Check which options are set, and change the parent class 292 // to show/hide help as required 293 this.options.each(function(thisoption) { 294 var optiondiv = thisoption.get('parentNode').get('parentNode'); 295 if (thisoption.get('checked')) { 296 optiondiv.addClass('selected'); 297 298 // Trigger any events for this option 299 this.option_selected(thisoption); 300 301 // Ensure that the form may be submitted 302 this.submitbutton.removeAttribute('disabled'); 303 304 // Ensure that the radio remains focus so that keyboard navigation is still possible 305 thisoption.focus(); 306 } else { 307 optiondiv.removeClass('selected'); 308 } 309 }, this); 310 }, 311 312 option_selected: function(e) { 313 // Set a hidden input field with the value and name of the radio button. When we submit the form, we 314 // disable the radios to prevent duplicate submission. This has the result however that the value is never 315 // submitted so we set this value to a hidden field instead 316 this.hiddenRadioValue.setAttrs({ 317 value: e.get('value'), 318 name: e.get('name') 319 }); 320 }, 321 322 /** 323 * Return an array of class names prefixed with 'chooserdialogue-' and 324 * the name of the type of dialogue. 325 * 326 * Note: Class name are converted to lower-case. 327 * 328 * If an array of arguments is supplied, each of these is prefixed and 329 * lower-cased also. 330 * 331 * If no arguments are supplied, then the prefix is returned on it's 332 * own. 333 * 334 * @method _getClassNames 335 * @param {Array} [args] Any additional names to prefix and lower-case. 336 * @return {Array} 337 * @private 338 */ 339 _getClassNames: function(args) { 340 var prefix = 'chooserdialogue-' + this.name, 341 results = []; 342 343 results.push(prefix.toLowerCase()); 344 if (args) { 345 var arg; 346 for (arg in args) { 347 results.push((prefix + '-' + arg).toLowerCase()); 348 } 349 } 350 351 return results; 352 } 353 }, 354 { 355 NAME: 'moodle-core-chooserdialogue', 356 ATTRS: { 357 /** 358 * The minimum height (in pixels) before resizing is prevented and scroll 359 * locking disabled. 360 * 361 * @attribute minheight 362 * @type Number 363 * @default 300 364 */ 365 minheight: { 366 value: 300 367 }, 368 369 /** 370 * The base height?? 371 * 372 * @attribute baseheight 373 * @type Number 374 * @default 400 375 */ 376 baseheight: { 377 value: 400 378 }, 379 380 /** 381 * The maximum height (in pixels) at which we stop resizing. 382 * 383 * @attribute maxheight 384 * @type Number 385 * @default 300 386 */ 387 maxheight: { 388 value: 660 389 }, 390 391 /** 392 * The title of the close button. 393 * 394 * @attribute closeButtonTitle 395 * @type String 396 * @default 'Close' 397 */ 398 closeButtonTitle: { 399 validator: Y.Lang.isString, 400 value: 'Close' 401 } 402 } 403 }); 404 M.core = M.core || {}; 405 M.core.chooserdialogue = CHOOSERDIALOGUE; 406 407 408 }, '@VERSION@', {"requires": ["base", "panel", "moodle-core-notification"]});
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 |