weekly release 2.6dev
[moodle.git] / lib / yui / build / moodle-core-notification-dialogue / moodle-core-notification-dialogue-debug.js
CommitLineData
78686995
AN
1YUI.add('moodle-core-notification-dialogue', function (Y, NAME) {
2
3var DIALOGUE_PREFIX,
4 BASE,
5 COUNT,
6 CONFIRMYES,
7 CONFIRMNO,
8 TITLE,
9 QUESTION,
10 CSS;
11
12DIALOGUE_PREFIX = 'moodle-dialogue',
13BASE = 'notificationBase',
14COUNT = 0,
15CONFIRMYES = 'yesLabel',
16CONFIRMNO = 'noLabel',
17TITLE = 'title',
18QUESTION = 'question',
19CSS = {
20 BASE : 'moodle-dialogue-base',
21 WRAP : 'moodle-dialogue-wrap',
22 HEADER : 'moodle-dialogue-hd',
23 BODY : 'moodle-dialogue-bd',
24 CONTENT : 'moodle-dialogue-content',
25 FOOTER : 'moodle-dialogue-ft',
26 HIDDEN : 'hidden',
27 LIGHTBOX : 'moodle-dialogue-lightbox'
28};
29
30// Set up the namespace once.
31M.core = M.core || {};
32/**
33 * The generic dialogue class for use in Moodle.
34 *
35 * @module moodle-core-notification
36 * @submodule moodle-core-notification-dialogue
37 */
38
39var DIALOGUE_NAME = 'Moodle dialogue',
bf7c86cf
DW
40 DIALOGUE,
41 DIALOGUE_FULLSCREEN_CLASS,
42 DIALOGUE_HIDDEN_CLASS,
43 EXISTING_WINDOW_SELECTOR,
44 NOSCROLLING_CLASS;
45
46DIALOGUE_MODAL_CLASS = 'yui3-widget-modal';
47DIALOGUE_FULLSCREEN_CLASS = DIALOGUE_PREFIX+'-fullscreen';
48DIALOGUE_HIDDEN_CLASS = DIALOGUE_PREFIX+'-hidden';
49EXISTING_WINDOW_SELECTOR = '[role=dialog]';
50NOSCROLLING_CLASS = 'no-scrolling';
78686995
AN
51
52/**
53 * A re-usable dialogue box with Moodle classes applied.
54 *
55 * @param {Object} config Object literal specifying the dialogue configuration properties.
56 * @constructor
57 * @class M.core.dialogue
58 * @extends Y.Panel
59 */
60DIALOGUE = function(config) {
61 COUNT++;
bf7c86cf 62 var id = 'moodle-dialogue-'+COUNT;
78686995
AN
63 config.notificationBase =
64 Y.Node.create('<div class="'+CSS.BASE+'">')
65 .append(Y.Node.create('<div id="'+id+'" role="dialog" aria-labelledby="'+id+'-header-text" class="'+CSS.WRAP+'"></div>')
baffb422 66 .append(Y.Node.create('<div id="'+id+'-header-text" class="'+CSS.HEADER+' yui3-widget-hd"></div>'))
78686995
AN
67 .append(Y.Node.create('<div class="'+CSS.BODY+' yui3-widget-bd"></div>'))
68 .append(Y.Node.create('<div class="'+CSS.FOOTER+' yui3-widget-ft"></div>')));
69 Y.one(document.body).append(config.notificationBase);
b59f2e3b
SH
70
71 if (config.additionalBaseClass) {
72 config.notificationBase.addClass(config.additionalBaseClass);
73 }
74
78686995
AN
75 config.srcNode = '#'+id;
76 config.width = config.width || '400px';
77 config.visible = config.visible || false;
4fd8adab 78 config.center = config.centered && true;
78686995
AN
79 config.centered = false;
80 config.COUNT = COUNT;
81
b59f2e3b
SH
82 if (config.width === 'auto') {
83 delete config.width;
84 }
85
78686995
AN
86 // lightbox param to keep the stable versions API.
87 if (config.lightbox !== false) {
88 config.modal = true;
89 }
90 delete config.lightbox;
91
92 // closeButton param to keep the stable versions API.
93 if (config.closeButton === false) {
94 config.buttons = null;
95 } else {
96 config.buttons = [
97 {
98 section: Y.WidgetStdMod.HEADER,
99 classNames: 'closebutton',
100 action: function () {
101 this.hide();
102 }
103 }
104 ];
105 }
106 DIALOGUE.superclass.constructor.apply(this, [config]);
107
108 if (config.closeButton !== false) {
109 // The buttons constructor does not allow custom attributes
110 this.get('buttons').header[0].setAttribute('title', this.get('closeButtonTitle'));
111 }
112};
113Y.extend(DIALOGUE, Y.Panel, {
d61c96b6
DW
114 // Window resize event listener.
115 _resizeevent : null,
116 // Orientation change event listener.
117 _orientationevent : null,
118
bf7c86cf
DW
119 /**
120 * Initialise the dialogue.
121 *
122 * @method initializer
123 * @return void
124 */
125 initializer : function(config) {
126 var bb;
127
78686995
AN
128 this.render();
129 this.show();
bf7c86cf 130 this.after('visibleChange', this.visibilityChanged, this);
dd66b6ab
DW
131 if (config.center) {
132 this.centerDialogue();
133 }
bf7c86cf
DW
134 if (!config.visible) {
135 this.hide();
136 }
78686995
AN
137 this.set('COUNT', COUNT);
138
139 // Workaround upstream YUI bug http://yuilibrary.com/projects/yui3/ticket/2532507
140 // and allow setting of z-index in theme.
d61c96b6 141 bb = this.get('boundingBox');
bf7c86cf
DW
142
143 if (config.extraClasses) {
144 Y.Array.each(config.extraClasses, bb.addClass, bb);
145 }
146 if (config.visible) {
147 this.applyZIndex();
148 }
149 },
150
151 /**
152 * Either set the zindex to the supplied value, or set it to one more than the highest existing
153 * dialog in the page.
154 *
155 * @method visibilityChanged
156 * @return void
157 */
158 applyZIndex : function() {
159 var highestzindex = 0,
160 zindex,
161 bb;
162
163 bb = this.get('boundingBox');
164 if (this.get('zIndex')) {
165 // The zindex was specified so we should use that.
166 bb.setStyle('zIndex', this.get('zIndex'));
167 } else {
168 // Determine the correct zindex by looking at all existing dialogs in the page.
169 // Get the zindex of the parent of each wrapper node.
170 Y.all(EXISTING_WINDOW_SELECTOR).each(function (node) {
171 zindex = node.getStyle('zIndex');
172
173 // In most cases the zindex is set on the parent of the dialog.
174 if (!zindex) {
175 zindex = node.get('parentNode').getStyle('zIndex');
176 }
177
178 if (zindex) {
179 zindex = parseInt(zindex, 10);
180
181 if (zindex > highestzindex) {
182 highestzindex = zindex;
183 }
184 }
185 });
186 // Only set the zindex if we found a wrapper.
187 if (highestzindex > 0) {
188 bb.setStyle('zIndex', highestzindex + 1);
189 }
190 }
191 },
192
193 /**
194 * Enable or disable document scrolling (see if there are any modal or fullscreen popups).
195 *
196 * @method toggleDocumentScrolling
197 * @param Boolean scroll - If true, allow document scrolling.
198 * @return void
199 */
200 toggleDocumentScrolling : function() {
201 var windowroot = Y.one(Y.config.doc.body),
202 scroll = true,
203 search;
204
205 search = '.' + DIALOGUE_FULLSCREEN_CLASS + ', .' + DIALOGUE_MODAL_CLASS;
206 Y.all(search).each(function (node) {
207 if (!node.hasClass(DIALOGUE_HIDDEN_CLASS)) {
208 scroll = false;
209 }
210 });
211
212 if (Y.UA.ie > 0) {
213 // Remember the previous value:
214 windowroot = Y.one('html');
215 }
216 if (scroll) {
217 if (windowroot.hasClass(NOSCROLLING_CLASS)) {
218 windowroot.removeClass(NOSCROLLING_CLASS);
219 }
220 } else {
221 windowroot.addClass(NOSCROLLING_CLASS);
d61c96b6 222 }
78686995 223 },
bf7c86cf
DW
224
225 /**
226 * Event listener for the visibility changed event.
227 *
228 * @method visibilityChanged
229 * @return void
230 */
78686995
AN
231 visibilityChanged : function(e) {
232 var titlebar;
233 if (e.attrName === 'visible') {
234 this.get('maskNode').addClass(CSS.LIGHTBOX);
d61c96b6
DW
235 if (e.prevVal && !e.newVal) {
236 if (this._resizeevent) {
237 this._resizeevent.detach();
238 this._resizeevent = null;
239 }
240 if (this._orientationevent) {
241 this._orientationevent.detach();
242 this._orientationevent = null;
243 }
244 }
bf7c86cf
DW
245 if (!e.prevVal && e.newVal) {
246 // This needs to be done each time the dialog is shown as new dialogs may have been opened.
247 this.applyZIndex();
248 // This needs to be done each time the dialog is shown as the window may have been resized.
249 this.makeResponsive();
250 if (!this.shouldResizeFullscreen()) {
251 if (this.get('draggable')) {
252 titlebar = '#' + this.get('id') + ' .' + CSS.HEADER;
253 this.plug(Y.Plugin.Drag, {handles : [titlebar]});
254 Y.one(titlebar).setStyle('cursor', 'move');
255 }
256 }
257 }
78686995
AN
258 if (this.get('center') && !e.prevVal && e.newVal) {
259 this.centerDialogue();
260 }
bf7c86cf 261 this.toggleDocumentScrolling();
78686995
AN
262 }
263 },
bf7c86cf
DW
264 /**
265 * If the responsive attribute is set on the dialog, and the window size is
266 * smaller than the responsive width - make the dialog fullscreen.
267 *
268 * @method makeResponsive
269 * @return void
270 */
271 makeResponsive : function() {
78686995 272 var bb = this.get('boundingBox'),
bf7c86cf
DW
273 content;
274
275 if (this.shouldResizeFullscreen()) {
d61c96b6
DW
276 // Make this dialogue fullscreen on a small screen.
277 // Disable the page scrollbars.
bf7c86cf 278
d61c96b6
DW
279 // Size and position the fullscreen dialog.
280
281 bb.addClass(DIALOGUE_PREFIX+'-fullscreen');
bf7c86cf 282 bb.setStyles({'left' : null, 'top' : null, 'width' : null, 'height' : null});
d61c96b6
DW
283
284 content = Y.one('#' + this.get('id') + ' .' + CSS.BODY);
285 content.setStyle('overflow', 'auto');
d61c96b6
DW
286 } else {
287 if (this.get('responsive')) {
288 // We must reset any of the fullscreen changes.
289 bb.removeClass(DIALOGUE_PREFIX+'-fullscreen')
bf7c86cf
DW
290 .setStyles({'overflow' : 'inherit',
291 'width' : this.get('width'),
292 'height' : this.get('height')});
d61c96b6
DW
293 content = Y.one('#' + this.get('id') + ' .' + CSS.BODY);
294 content.setStyle('overflow', 'inherit');
295
d61c96b6 296 }
d61c96b6 297 }
bf7c86cf
DW
298 },
299 /**
300 * Center the dialog on the screen.
301 *
302 * @method centerDialogue
303 * @return void
304 */
305 centerDialogue : function() {
306 var bb = this.get('boundingBox'),
307 hidden = bb.hasClass(DIALOGUE_HIDDEN_CLASS),
308 x,
309 y;
310
311 // Don't adjust the position if we are in full screen mode.
312 if (this.shouldResizeFullscreen()) {
313 return;
314 }
315 if (hidden) {
316 bb.setStyle('top', '-1000px').removeClass(DIALOGUE_HIDDEN_CLASS);
317 }
318 x = Math.max(Math.round((bb.get('winWidth') - bb.get('offsetWidth'))/2), 15);
319 y = Math.max(Math.round((bb.get('winHeight') - bb.get('offsetHeight'))/2), 15) + Y.one(window).get('scrollTop');
320 bb.setStyles({ 'left' : x, 'top' : y});
78686995
AN
321
322 if (hidden) {
bf7c86cf 323 bb.addClass(DIALOGUE_HIDDEN_CLASS);
78686995 324 }
d61c96b6 325 },
bf7c86cf
DW
326 /**
327 * Return if this dialogue should be fullscreen or not.
328 * Responsive attribute must be true and we should not be in an iframe and the screen width should
329 * be less than the responsive width.
330 *
331 * @method shouldResizeFullscreen
332 * @return Boolean
333 */
334 shouldResizeFullscreen : function() {
335 return (window === window.parent) && this.get('responsive') &&
336 Math.floor(Y.one(document.body).get('winWidth')) < this.get('responsiveWidth');
78686995
AN
337 }
338}, {
339 NAME : DIALOGUE_NAME,
340 CSS_PREFIX : DIALOGUE_PREFIX,
341 ATTRS : {
342 notificationBase : {
343
344 },
345
346 /**
347 * Whether to display the dialogue modally and with a
348 * lightbox style.
349 *
350 * @attribute lightbox
351 * @type Boolean
352 * @default true
353 */
354 lightbox : {
355 validator : Y.Lang.isBoolean,
356 value : true
357 },
358
359 /**
360 * Whether to display a close button on the dialogue.
361 *
362 * Note, we do not recommend hiding the close button as this has
363 * potential accessibility concerns.
364 *
365 * @attribute closeButton
366 * @type Boolean
367 * @default true
368 */
369 closeButton : {
370 validator : Y.Lang.isBoolean,
371 value : true
372 },
373
374 /**
375 * The title for the close button if one is to be shown.
376 *
377 * @attribute closeButtonTitle
378 * @type String
379 * @default 'Close'
380 */
381 closeButtonTitle : {
382 validator : Y.Lang.isString,
383 value : 'Close'
384 },
385
386 /**
387 * Whether to display the dialogue centrally on the screen.
388 *
389 * @attribute center
390 * @type Boolean
391 * @default true
392 */
393 center : {
394 validator : Y.Lang.isBoolean,
395 value : true
396 },
397
398 /**
399 * Whether to make the dialogue movable around the page.
400 *
401 * @attribute draggable
402 * @type Boolean
403 * @default false
404 */
405 draggable : {
406 validator : Y.Lang.isBoolean,
407 value : false
408 },
bf7c86cf
DW
409
410 /**
411 * Used to generate a unique id for the dialogue.
412 *
413 * @attribute COUNT
414 * @type Integer
415 * @default 0
416 */
78686995
AN
417 COUNT: {
418 value: 0
d61c96b6 419 },
bf7c86cf
DW
420
421 /**
422 * Used to disable the fullscreen resizing behaviour if required.
423 *
424 * @attribute responsive
425 * @type Boolean
426 * @default true
427 */
d61c96b6
DW
428 responsive : {
429 validator : Y.Lang.isBoolean,
430 value : true
431 },
bf7c86cf
DW
432
433 /**
434 * The width that this dialogue should be resized to fullscreen.
435 *
436 * @attribute responsiveWidth
437 * @type Integer
438 * @default 768
439 */
d61c96b6
DW
440 responsiveWidth : {
441 value : 768
78686995
AN
442 }
443 }
444});
445
446M.core.dialogue = DIALOGUE;
447
448
449}, '@VERSION@', {"requires": ["base", "node", "panel", "event-key", "dd-plugin"]});