1a499a8ad13ee91e3a41f8ce5b3a8094edf549bc
[moodle.git] / lib / yui / chooserdialogue / chooserdialogue.js
1 YUI.add('moodle-core-chooserdialogue', function(Y) {
3     var CHOOSERDIALOGUE = function() {
4         CHOOSERDIALOGUE.superclass.constructor.apply(this, arguments);
5     }
7     Y.extend(CHOOSERDIALOGUE, Y.Base, {
8         // The overlay widget
9         overlay: null,
11         // The submit button - we disable this until an element is set
12         submitbutton : null,
14         // The chooserdialogue container
15         container : null,
17         // Any event listeners we may need to cancel later
18         listenevents : [],
20         // The initial overflow setting
21         initialoverflow : '',
23         setup_chooser_dialogue : function(bodycontent, headercontent, config) {
24             // Set Default options
25             var params = {
26                 bodyContent : bodycontent.get('innerHTML'),
27                 headerContent : headercontent.get('innerHTML'),
28                 draggable : true,
29                 visible : false, // Hide by default
30                 zindex : 100, // Display in front of other items
31                 lightbox : true, // This dialogue should be modal
32                 shim : true
33             }
35             // Override with additional options
36             for (paramkey in config) {
37               params[paramkey] = config[paramkey];
38             }
40             // Create the overlay
41             this.overlay = new M.core.dialogue(params);
43             // Remove the template for the chooser
44             bodycontent.remove();
45             headercontent.remove();
47             // Hide and then render the overlay
48             this.overlay.hide();
49             this.overlay.render();
51             // Set useful links
52             this.container = this.overlay.get('boundingBox').one('.choosercontainer');
53             this.options = this.container.all('.option input[type=radio]');
55             // Add the chooserdialogue class to the container for styling
56             this.overlay.get('boundingBox').addClass('chooserdialogue');
57         },
59         /**
60          * Display the module chooser
61          *
62          * @param e Event Triggering Event
63          * @return void
64          */
65         display_chooser : function (e) {
66             // Stop the default event actions before we proceed
67             e.preventDefault();
69             var bb = this.overlay.get('boundingBox');
70             var dialogue = this.container.one('.alloptions');
72             // Get the overflow setting when the chooser was opened - we
73             // may need this later
74             if (Y.UA.ie > 0) {
75                 this.initialoverflow = Y.one('html').getStyle('overflow');
76             } else {
77                 this.initialoverflow = Y.one('body').getStyle('overflow');
78             }
80             var thisevent;
82             // This will detect a change in orientation and retrigger centering
83             thisevent = Y.one('document').on('orientationchange', function(e) {
84                 this.center_dialogue(dialogue);
85             }, this);
86             this.listenevents.push(thisevent);
88             // Detect window resizes (most browsers)
89             thisevent = Y.one('window').on('resize', function(e) {
90                 this.center_dialogue(dialogue);
91             }, this);
92             this.listenevents.push(thisevent);
94             // These will trigger a check_options call to display the correct help
95             thisevent = this.container.on('click', this.check_options, this);
96             this.listenevents.push(thisevent);
97             thisevent = this.container.on('key_up', this.check_options, this);
98             this.listenevents.push(thisevent);
99             thisevent = this.container.on('dblclick', function(e) {
100                 if (e.target.ancestor('div.option')) {
101                     this.check_options();
103                     // Prevent duplicate submissions
104                     this.submitbutton.setAttribute('disabled', 'disabled');
105                     this.options.setAttribute('disabled', 'disabled');
106                     this.cancel_listenevents();
108                     this.container.one('form').submit();
109                 }
110             }, this);
111             this.listenevents.push(thisevent);
113             this.container.one('form').on('submit', function(e) {
114                 // Prevent duplicate submissions on submit
115                 this.submitbutton.setAttribute('disabled', 'disabled');
116                 this.options.setAttribute('disabled', 'disabled');
117                 this.cancel_listenevents();
118             }, this);
120             // Hook onto the cancel button to hide the form
121             thisevent = this.container.one('#addcancel').on('click', this.cancel_popup, this);
122             this.listenevents.push(thisevent);
123             thisevent = bb.one('div.closebutton').on('click', this.cancel_popup, this);
124             this.listenevents.push(thisevent);
126             // Grab global keyup events and handle them
127             thisevent = Y.one('document').on('keyup', this.handle_key_press, this);
128             this.listenevents.push(thisevent);
130             // Add references to various elements we adjust
131             this.jumplink     = this.container.one('#jump');
132             this.submitbutton = this.container.one('#submitbutton');
134             // Disable the submit element until the user makes a selection
135             this.submitbutton.set('disabled', 'true');
137             // Ensure that the options are shown
138             this.options.removeAttribute('disabled');
140             // Display the overlay
141             this.overlay.show();
143             // Re-centre the dialogue after we've shown it.
144             this.center_dialogue(dialogue);
146             // Finally, focus the first radio element - this enables form selection via the keyboard
147             this.container.one('.option input[type=radio]').focus();
149             // Trigger check_options to set the initial jumpurl
150             this.check_options();
151         },
153         /**
154          * Cancel any listen events in the listenevents queue
155          *
156          * Several locations add event handlers which should only be called before the form is submitted. This provides
157          * a way of cancelling those events.
158          *
159          * @return void
160          */
161         cancel_listenevents : function () {
162             // Detach all listen events to prevent duplicate triggers
163             var thisevent;
164             while (thisevent = this.listenevents.shift()) {
165                 thisevent.detach();
166             }
167         },
169         /**
170          * Calculate the optimum height of the chooser dialogue
171          *
172          * This tries to set a sensible maximum and minimum to ensure that some options are always shown, and preferably
173          * all, whilst fitting the box within the current viewport.
174          *
175          * @param dialogue Y.Node The dialogue
176          * @return void
177          */
178         center_dialogue : function(dialogue) {
179             var bb = this.overlay.get('boundingBox');
181             var winheight = bb.get('winHeight');
182             var winwidth = bb.get('winWidth');
183             var offsettop = 0;
185             // Try and set a sensible max-height -- this must be done before setting the top
186             // Set a default height of 640px
187             var newheight = this.get('maxheight');
188             if (winheight <= newheight) {
189                 // Deal with smaller window sizes
190                 if (winheight <= this.get('minheight')) {
191                     newheight = this.get('minheight');
192                 } else {
193                     newheight = winheight;
194                 }
195             }
197             // Set a fixed position if the window is large enough
198             if (newheight > this.get('minheight')) {
199                 bb.setStyle('position', 'fixed');
200                 // Disable the page scrollbars
201                 if (Y.UA.ie > 0) {
202                     Y.one('html').setStyle('overflow', 'hidden');
203                 } else {
204                     Y.one('body').setStyle('overflow', 'hidden');
205                 }
206             } else {
207                 bb.setStyle('position', 'absolute');
208                 offsettop = Y.one('window').get('scrollTop');
209                 // Ensure that the page scrollbars are enabled
210                 if (Y.UA.ie > 0) {
211                     Y.one('html').setStyle('overflow', this.initialoverflow);
212                 } else {
213                     Y.one('body').setStyle('overflow', this.initialoverflow);
214                 }
215             }
217             // Take off 15px top and bottom for borders, plus 40px each for the title and button area before setting the
218             // new max-height
219             var totalheight = newheight;
220             newheight = newheight - (15 + 15 + 40 + 40);
221             dialogue.setStyle('max-height', newheight + 'px');
222             dialogue.setStyle('height', newheight + 'px');
224             // Re-calculate the location now that we've changed the size
225             var dialoguetop = Math.max(12, ((winheight - totalheight) / 2)) + offsettop;
227             // We need to set the height for the yui3-widget - can't work
228             // out what we're setting at present -- shoud be the boudingBox
229             bb.setStyle('top', dialoguetop + 'px');
231             // Calculate the left location of the chooser
232             // We don't set a minimum width in the same way as we do height as the width would be far lower than the
233             // optimal width for moodle anyway.
234             var dialoguewidth = bb.get('offsetWidth');
235             var dialogueleft = (winwidth - dialoguewidth) / 2;
236             bb.setStyle('left', dialogueleft + 'px');
237         },
239         handle_key_press : function(e) {
240             if (e.keyCode == 27) {
241                 this.cancel_popup(e);
242             }
243         },
245         cancel_popup : function (e) {
246             // Prevent normal form submission before hiding
247             e.preventDefault();
248             this.hide();
249         },
251         hide : function() {
252             // Detach the global keypress handler before hiding
253             Y.one('document').detach('keyup', this.handle_key_press, this);
255             // Re-enable the page scrollbars
256             if (Y.UA.ie > 0) {
257                 Y.one('html').setStyle('overflow', this.initialoverflow);
258             } else {
259                 Y.one('body').setStyle('overflow', this.initialoverflow);
260             }
262             this.container.detachAll();
263             this.overlay.hide();
264         },
266         check_options : function(e) {
267             // Check which options are set, and change the parent class
268             // to show/hide help as required
269             this.options.each(function(thisoption) {
270                 var optiondiv = thisoption.get('parentNode').get('parentNode');
271                 if (thisoption.get('checked')) {
272                     optiondiv.addClass('selected');
274                     // Trigger any events for this option
275                     this.option_selected(thisoption);
277                     // Ensure that the form may be submitted
278                     this.submitbutton.removeAttribute('disabled');
280                     // Ensure that the radio remains focus so that keyboard navigation is still possible
281                     thisoption.focus();
282                 } else {
283                     optiondiv.removeClass('selected');
284                 }
285             }, this);
286         },
288         option_selected : function(e) {
289         }
290     },
291     {
292         NAME : 'moodle-core-chooserdialogue',
293         ATTRS : {
294             minheight : {
295                 value : 300
296             },
297             maxheight : {
298                 value : 660
299             }
300         }
301     });
302     M.core = M.core || {};
303     M.core.chooserdialogue = CHOOSERDIALOGUE;
304 },
305 '@VERSION@', {
306     requires:['base', 'overlay', 'moodle-enrol-notification']
308 );