source: trunk/prototype/app/plugins/zebradialog/javascript/zebra_dialog.src.js @ 5341

Revision 5341, 32.9 KB checked in by wmerlotto, 12 years ago (diff)

Ticket #2434 - Commit inicial do novo módulo de agenda do Expresso - expressoCalendar

Line 
1/**
2 *  Zebra_Dialog
3 *
4 *  A small (4KB minified), compact (one JS file, no dependencies other than jQuery 1.5.2+) and highly configurable
5 *  dialog box plugin for jQuery meant to replace JavaScript's "alert" and "confirmation" dialog boxes.
6 *
7 *  Can also be used as a notification widget - when configured to show no buttons and to close automatically - for updates
8 *  or errors, without distracting users from their browser experience by displaying obtrusive alerts.
9 *
10 *  Features:
11 *
12 *  -   great looks - out of the box
13 *  -   5 types of dialog boxes available: confirmation, information, warning, error and question;
14 *  -   easily customizable appearance by changing the CSS file
15 *  -   create modal or non-modal dialog boxes
16 *  -   easily add custom buttons
17 *  -   position the dialog box wherever you want - not just in the middle of the screen
18 *  -   use callback functions to handle user's choice
19 *  -   works in all major browsers (Firefox, Opera, Safari, Chrome, Internet Explorer 6, 7, 8, 9)
20 *
21 *  Visit {@link http://stefangabos.ro/jquery/zebra-dialog/} for more information.
22 *
23 *  For more resources visit {@link http://stefangabos.ro/}
24 *
25 *  @author     Stefan Gabos <contact@stefangabos.ro>
26 *  @version    1.1 (last revision: August 18, 2011)
27 *  @copyright  (c) 2011 Stefan Gabos
28 *  @license    http://www.gnu.org/licenses/lgpl-3.0.txt GNU LESSER GENERAL PUBLIC LICENSE
29 *  @package    Zebra_Dialog
30 */
31;(function($) {
32
33    $.Zebra_Dialog = function() {
34
35        // default options
36        var defaults = {
37
38            animation_speed:            250,            //  The speed, in milliseconds, by which the overlay and the
39                                                        //  dialog box will be animated when closing.
40                                                        //
41                                                        //  Default is 250
42
43            auto_close:                 false,          //  The number of milliseconds after which to automatically
44                                                        //  close the dialog box or FALSE to not automatically close the
45                                                        //  dialog box.
46                                                        //
47                                                        //  Default is FALSE
48
49            buttons:                    true,           //  Use this for localization and for adding custom buttons.
50                                                        //
51                                                        //  If set to TRUE, the default buttons will be used, depending
52                                                        //  on the type of the dialog box: ['Yes', 'No'] for "question"
53                                                        //  type and ['Ok'] for the other dialog box types.
54                                                        //
55                                                        //  For custom buttons, use an array containing the captions of
56                                                        //  the buttons to display: ['My button 1', 'My button 2'].
57                                                        //
58                                                        //  Set to FALSE if you want no buttons.
59                                                        //
60                                                        //  Note that when the dialog box is closed as a result of clicking
61                                                        //  a button, the "onClose" event is triggered and the callback
62                                                        //  function (if any) receives as argument the caption of the
63                                                        //  clicked button.
64                                                        //
65                                                        //  See the comments for the "onClose" event below for more
66                                                        //  information.
67
68            custom_class:                false,         //  An extra class to add to the dialog box's container. Useful
69                                                        //  for customizing a dialog box elements' styles at run-time.
70                                                        //
71                                                        //  For example, setting this value to "mycustom" and in the
72                                                        //  CSS file having something like
73                                                        //  .mycustom .ZebraDialog_Title { background: red }
74                                                        //  would set the dialog box title's background to red.
75                                                        //
76                                                        //  See the CSS file for what can be changed.
77                                                        //
78                                                        //  Default is FALSE
79
80            keyboard:                   true,           //  When set to TRUE, pressing the ESC key will close the dialog
81                                                        //  box.
82                                                        //
83                                                        //  Default is TRUE.
84
85            message:                    '',             //  The message in the dialog box - this is passed as argument
86                                                        //  when the plugin is called.
87
88            modal:                      true,           //  When set to TRUE there will be a semitransparent overlay
89                                                        //  behind the dialog box, preventing users from clicking
90                                                        //  the page's content.
91                                                        //
92                                                        //  Default is TRUE
93
94            overlay_close:              true,           //  Should the dialog box close when the overlay is clicked?
95                                                        //
96                                                        //  Default is TRUE
97
98            overlay_opacity:            .9,             //  The opacity of the overlay (between 0 and 1)
99                                                        //
100                                                        //  Default is .9
101
102            position:                   'center',       //  Position of the dialog box.
103                                                        //
104                                                        //  Can be either "center" (which would center the dialog box) or
105                                                        //  an array with 2 elements, in the form of
106                                                        //  {'horizontal_position +/- offset', 'vertical_position +/- offset'}
107                                                        //  (notice how everything is enclosed in quotes) where
108                                                        //  "horizontal_position" can be "left", "right" or "center",
109                                                        //  "vertical_position" can be "top", "bottom" or "middle", and
110                                                        //  "offset" represents an optional number of pixels to add/substract
111                                                        //  from the respective horizontal or vertical position.
112                                                        //
113                                                        //  Positions are relative to the viewport (the area of the
114                                                        //  browser that is visible to the user)!
115                                                        //
116                                                        //  Examples:
117                                                        //
118                                                        //  ['left + 20', 'top + 20'] would position the dialog box in the
119                                                        //  top-left corner, shifted 20 pixels inside.
120                                                        //
121                                                        //  ['right - 20', 'bottom - 20'] would position the dialog box
122                                                        //  in the bottom-right corner, shifted 20 pixels inside.
123                                                        //
124                                                        //  ['center', 'top + 20'] would position the dialog box in
125                                                        //  center-top, shifted 20 pixels down.
126                                                        //
127                                                        //  Default is "center".
128
129            title:                      '',             //  Title of the dialog box
130                                                        //
131                                                        //  Default is "" (an empty string - no title)
132
133            type:                       'information',  //  Dialog box type.
134                                                        //
135                                                        //  Can be any of the following:
136                                                        //
137                                                        //  - confirmation
138                                                        //  - error
139                                                        //  - information
140                                                        //  - question
141                                                        //  - warning
142                                                        //
143                                                        //  If you don't want an icon, set the "type" property to FALSE.
144                                                        //
145                                                        //  By default, all types except "question" have a single button
146                                                        //  with the caption "Ok"; type "question" has two buttons, with
147                                                        //  the captions "Ok" and "Cancel" respectively.
148                                                        //
149                                                        //  Default is "information".
150
151            vcenter_short_message:      true,           //  Should short messages be vertically centered?
152                                                        //
153                                                        //  Default is TRUE
154
155            width:                      0,              //  By default, the width of the dialog box is set in the CSS
156                                                        //  file. Use this property to override the default width at
157                                                        //  run-time.
158                                                        //
159                                                        //  Must be an integer, greater than 0. Anything else will instruct
160                                                        //  the script to use the default value, as set in the CSS file.
161                                                        //  Value should be no less than 200 for optimal output.
162                                                        //
163                                                        //  Default is 0 - use the value from the CSS file.
164
165            onClose:                null                //  Event fired when the dialog box is closed.
166                                                        //
167                                                        //  The callback function (if any) receives as argument the
168                                                        //  caption of the clicked button or boolean FALSE if the dialog
169                                                        //  box is closed by pressing the ESC key or by clicking on the
170                                                        //  overlay.
171
172        }
173
174        // to avoid confusions, we use "plugin" to reference the current instance of the object
175        var plugin = this;
176
177        // this will hold the merged default, and user-provided options
178        plugin.settings = {};
179
180        // by default, we assume there are no custom options provided
181        options = {};
182
183        // if plugin is initialized so that first argument is a string
184        // that string is the message to be shown in the dialog box
185        if (typeof arguments[0] == 'string') options.message = arguments[0];
186           
187        // if plugin is initialized so that first or second argument is an object
188        if (typeof arguments[0] == 'object' || typeof arguments[1] == 'object')
189
190            // extend the options object with the user-provided options
191            options = $.extend(options, (typeof arguments[0] == 'object' ? arguments[0] : arguments[1]));
192
193        /**
194         *  Constructor of the method.
195         *
196         *  @return object  Returns a reference to the plugin
197         */
198        plugin.init = function() {
199
200            // the plugin's final properties are the merged default and user-provided options (if any)
201            plugin.settings = $.extend({}, defaults, options);
202
203            // check if browser is Internet Explorer 6 and set a flag accordingly as we need to perform some extra tasks
204            // later on for Internet Explorer 6
205            plugin.isIE6 = ($.browser.msie && parseInt($.browser.version, 10) == 6) || false;
206
207            // if dialog box should be modal
208            if (plugin.settings.modal) {
209
210                // create the overlay
211                plugin.overlay = jQuery('<div>', {
212
213                    'class':    'ZebraDialogOverlay'
214
215                // set some css properties of the overlay
216                }).css({
217
218                    'position': (plugin.isIE6 ? 'absolute' : 'fixed'),  //  for IE6 we emulate the "position:fixed" behaviour
219                    'left':     0,                                      //  the overlay starts at the top-left corner of the
220                    'top':      0,                                      //  browser window (later on we'll stretch it)
221                    'opacity':  plugin.settings.overlay_opacity,        //  set the overlay's opacity
222                    'z-index':  1000                                    //  set a high value for z-index
223
224                });
225
226                // if dialog box can be closed by clicking the overlay
227                if (plugin.settings.overlay_close)
228
229                    // when the overlay is clicked
230                    // remove the overlay and the dialog box from the DOM
231                    plugin.overlay.bind('click', plugin.close);
232
233                // append the overlay to the DOM
234                plugin.overlay.appendTo('body');
235
236            }
237
238            // create the dialog box
239            plugin.dialog = jQuery('<div>', {
240
241                'class':        'ZebraDialog' + (plugin.settings.custom_class ? ' ' + plugin.settings.custom_class : '')
242
243            // set some css properties of the dialog box
244            }).css({
245
246                'position':     (plugin.isIE6 ? 'absolute' : 'fixed'),  //  for IE6 we emulate the "position:fixed" behaviour
247                'left':         0,                                      //  by default, place it in the top-left corner of the
248                'top':          0,                                      //  browser window (we'll position it later)
249                'z-index':      1001,                                   //  the z-index must be higher than the overlay's z-index
250                'visibility':   'hidden'                                //  the dialog box is hidden for now
251
252            });
253
254            // if a notification message
255            if (!plugin.settings.buttons && plugin.settings.auto_close) {
256
257                // generate a unique id so that we can easily find similar notifications in the DOM and stack them properly
258                // (propert stacking is not currently implemented though)
259                // the unique id is made up of the box's position and its auto_close value
260                plugin.settings.uniqueid =  (plugin.settings.position + plugin.settings.auto_close + '').
261                                            replace(/\+/g, 'inc').replace(/\-/g, 'dec').replace(/[^a-z0-9]/gi, '');
262
263                // add the unique id as a class
264                plugin.dialog.addClass(plugin.settings.uniqueid);
265
266            }
267
268            // check to see if the "width" property is given as an integer
269            // try to convert to a integer
270            var tmp = parseInt(plugin.settings.width);
271
272            // if converted value is a valud number
273            if (!isNaN(tmp) && tmp == plugin.settings.width && tmp.toString() == plugin.settings.width.toString() && tmp > 0)
274
275                // set the dialog box's width
276                plugin.dialog.css({'width' : plugin.settings.width});
277
278            // if dialog box has a title
279            if (plugin.settings.title)
280
281                // create the title
282                jQuery('<h3>', {
283
284                    'class':    'ZebraDialog_Title'
285
286                // set the title's text
287                // and append the title to the dialog box
288                }).html(plugin.settings.title).appendTo(plugin.dialog);
289
290            // create the container of the actual message
291            // we save it as a reference because we'll use it later in the "draw" method
292            // if the "vcenter_short_message" property is TRUE
293            plugin.message = jQuery('<div>', {
294
295                // if a known dialog box type is specified, also show the appropriate icon
296                'class':    'ZebraDialog_Body' + (get_type() != '' ? ' ZebraDialog_Icon ZebraDialog_' + get_type() : '')
297
298            });
299
300            // if short messages are to be centered vertically
301            if (plugin.settings.vcenter_short_message)
302
303                // create a secondary container for the message and add everything to the message container
304                // (we'll later align the container vertically)
305                jQuery('<div>').html(plugin.settings.message).appendTo(plugin.message);
306
307            // if short messages are not to be centered vertically
308            else
309
310                // add the message to the message container
311                plugin.message.html(plugin.settings.message);
312
313            // add the message container to the dialog box
314            plugin.message.appendTo(plugin.dialog);
315
316            // get the buttons that are to be added to the dialog box
317            var buttons = get_buttons();
318
319            // if there are any buttons to be added to the dialog box
320            if (buttons) {
321
322                // create the button bar
323                var button_bar = jQuery('<div>', {
324
325                    'class':    'ZebraDialog_Buttons'
326
327                // append it to the dialog box
328                }).appendTo(plugin.dialog);
329
330                // iterate through the buttons that are to be attached to the dialog box
331                $.each(buttons, function(index, value) {
332
333                    // create button
334                    var button = jQuery('<a>', {
335
336                        'href':     'javascript:void(0)',
337                        'class':    'ZebraDialog_Button' + index
338
339                    // set button's caption
340                    }).html(value);
341
342                    // when the button is clicked
343                    button.bind('click', function() {
344
345                        // remove the overlay and the dialog box from the DOM
346                        // also pass the button's label as argument
347                        plugin.close(value)
348
349                    });
350
351                    // append the button to the button bar
352                    button.appendTo(button_bar);
353
354                });
355
356                // since buttons are floated,
357                // we need to clear floats
358                jQuery('<div>', {
359
360                    'style':    'clear:both'
361
362                }).appendTo(button_bar);
363
364            }
365
366            // insert the dialog box in the DOM
367            plugin.dialog.appendTo('body');
368
369            // if the browser window is resized
370            $(window).bind('resize', draw);
371
372            // if dialog box can be closed by pressing the ESC key
373            if (plugin.settings.keyboard)
374
375                // if a key is pressed
376                $(document).bind('keyup', _keyup);
377
378            // if browser is Internet Explorer 6 we attach an event to be fired whenever the window is scrolled
379            // that is because in IE6 we have to simulate "position:fixed"
380            if (plugin.isIE6)
381
382                // if window is scrolled
383                $(window).bind('scroll', _scroll);
384
385            // if plugin is to be closed automatically after a given number of milliseconds
386            if (plugin.settings.auto_close !== false)
387
388                // call the "close" method after the given number of milliseconds
389                setTimeout(plugin.close, plugin.settings.auto_close);
390
391            // draw the overlay and the dialog box
392            draw();
393
394            // return a reference to the object itself
395            return plugin;
396
397        }
398
399        /**
400         *  Close the dialog box
401         *
402         *  @return void
403         */
404        plugin.close = function() {
405
406            // remove all the event listeners
407            if (plugin.settings.keyboard) $(document).unbind('keyup', _keyup);
408
409            if (plugin.isIE6) $(window).unbind('scroll', _scroll);
410
411            $(window).unbind('resize', draw);
412
413            // if an overlay exists
414            if (plugin.settings.modal)
415
416                // animate overlay's css properties
417                plugin.overlay.animate({
418
419                    opacity: 0  // fade out the overlay
420
421                },
422
423                // animation speed
424                plugin.settings.animation_speed,
425
426                // when the animation is complete
427                function() {
428
429                    // remove the overlay from the DOM
430                    plugin.overlay.remove();
431
432                });
433
434            // animate dialog box's css properties
435            plugin.dialog.animate({
436
437                top: 0,     // move the dialog box to the top
438                opacity: 0  // fade out the dialog box
439
440            },
441
442            // animation speed
443            plugin.settings.animation_speed,
444
445            // when the animation is complete
446            function() {
447
448                // remove the dialog box from the DOM
449                plugin.dialog.remove();
450
451            });
452
453            // if a callback function exists for when closing the dialog box
454            if (plugin.settings.onClose && typeof plugin.settings.onClose == 'function')
455
456                // execute the callback function
457                plugin.settings.onClose(undefined != arguments[0] ? arguments[0] : '');
458
459        }
460
461        /**
462         *  Draw the overlay and the dialog box
463         *
464         *  @return void
465         *
466         *  @access private
467         */
468        var draw = function() {
469
470            var
471
472                // get the viewport width and height
473                viewport_width = $(window).width(),
474                viewport_height = $(window).height(),
475
476                // get dialog box's width and height
477                dialog_width = plugin.dialog.width(),
478                dialog_height = plugin.dialog.height(),
479
480                // the numeric representations for some constants that may exist in the "position" property
481                values = {
482
483                    'left':     0,
484                    'top':      0,
485                    'right':    viewport_width - dialog_width,
486                    'bottom':   viewport_height - dialog_height,
487                    'center':   (viewport_width - dialog_width) / 2,
488                    'middle':   (viewport_height - dialog_height) / 2
489
490                };
491
492            // reset these values
493            plugin.dialog_left = undefined;
494            plugin.dialog_top = undefined;
495
496            // if there is an overlay
497            // (the dialog box is modal)
498            if (plugin.settings.modal)
499
500                // stretch the overlay to cover the entire viewport
501                plugin.overlay.css({
502
503                    'width':    viewport_width,
504                    'height':   viewport_height
505
506                });
507
508            // if
509            if (
510
511                // the position is given as an array
512                $.isArray(plugin.settings.position) &&
513
514                // the array has exactly two elements
515                plugin.settings.position.length == 2 &&
516
517                // first element is a string
518                typeof plugin.settings.position[0] == 'string' &&
519
520                // first element contains only "left", "right", "center", numbers, spaces, plus and minus signs
521                plugin.settings.position[0].match(/^(left|right|center)[\s0-9\+\-]*$/) &&
522
523                // second element is a string
524                typeof plugin.settings.position[1] == 'string' &&
525
526                // second element contains only "top", "bottom", "middle", numbers, spaces, plus and minus signs
527                plugin.settings.position[1].match(/^(top|bottom|middle)[\s0-9\+\-]*$/)
528
529            ) {
530
531                // make sure both entries are lowercase
532                plugin.settings.position[0] = plugin.settings.position[0].toLowerCase();
533                plugin.settings.position[1] = plugin.settings.position[1].toLowerCase();
534
535                // iterate through the array of replacements
536                $.each(values, function(index, value) {
537
538                    // we need to check both the horizontal and vertical values
539                    for (var i = 0; i < 2; i++) {
540
541                        // replace if there is anything to be replaced
542                        var tmp = plugin.settings.position[i].replace(index, value);
543
544                        // if anything could be replaced
545                        if (tmp != plugin.settings.position[i])
546
547                            // evaluate string as a mathematical expression and set the appropriate value
548                            if (i == 0) plugin.dialog_left = eval(tmp); else plugin.dialog_top = eval(tmp);
549
550                    }
551
552                });
553
554            }
555
556            // if "dialog_left" and/or "dialog_top" values are still not set
557            if (undefined == plugin.dialog_left || undefined == plugin.dialog_top) {
558
559                // the dialog box will be in its default position, centered
560                plugin.dialog_left = values['center'];
561                plugin.dialog_top = values['middle'];
562
563            }
564
565            // if short messages are to be centered vertically
566            if (plugin.settings.vcenter_short_message) {
567
568                var
569
570                    // the secondary container - the one that containes the message
571                    message = plugin.message.find('div:first'),
572
573                    // the height of the secondary container
574                    message_height = message.height(),
575
576                    // the main container's height
577                    container_height = plugin.message.height();
578
579                // if we need to center the message vertically
580                if (message_height < container_height)
581
582                    // center the message vertically
583                    message.css({
584
585                        'margin-top':   (container_height - message_height) / 2
586
587                    });
588
589            }
590
591            // position the dialog box and make it visible
592            plugin.dialog.css({
593
594                'left':         plugin.dialog_left,
595                'top':          plugin.dialog_top,
596                'visibility':   'visible'
597
598            });
599
600            // move the focus to the first of the dialog box's buttons
601            plugin.dialog.find('a[class^=ZebraDialog_Button]:first').focus();
602
603            // if the browser is Internet Explorer 6, call the "emulate_fixed_position" method
604            // (if we do not apply a short delay, it sometimes does not work as expected)
605            if (plugin.isIE6) setTimeout(emulate_fixed_position, 500);
606
607        }
608
609        /**
610         *  Emulates "position:fixed" for Internet Explorer 6.
611         *
612         *  @return void
613         *
614         *  @access private
615         */
616        var emulate_fixed_position = function() {
617
618            var
619
620                // get how much the window is scrolled both horizontally and vertically
621                scroll_top = $(window).scrollTop(),
622                scroll_left = $(window).scrollLeft();
623
624            // if an overlay exists
625            if (plugin.settings.modal)
626
627                // make sure the overlay is stays positioned at the top of the viewport
628                plugin.overlay.css({
629
630                    'top':  scroll_top,
631                    'left': scroll_left
632
633                });
634
635            // make sure the dialog box always stays in the correct position
636            plugin.dialog.css({
637
638                'left': plugin.dialog_left + scroll_left,
639                'top':  plugin.dialog_top + scroll_top
640
641            });
642
643        }
644
645        /**
646         *  Returns an array containing the buttons that are to be added to the dialog box.
647         *
648         *  If no custom buttons are specified, the returned array depends on the type of the dialog box.
649         *
650         *  @return array       Returns an array containing the buttons that are to be added to the dialog box.
651         *
652         *  @access private
653         */
654        var get_buttons = function() {
655
656            // if plugin.settings.buttons is not TRUE and is not an array either, don't go further
657            if (plugin.settings.buttons !== true && !$.isArray(plugin.settings.buttons)) return false;
658
659            // if default buttons are to be used
660            if (plugin.settings.buttons === true)
661
662                // there are different buttons for different dialog box types
663                switch (plugin.settings.type) {
664
665                    // for "question" type
666                    case 'question':
667
668                        // there are two buttons
669                        plugin.settings.buttons = ['Yes', 'No'];
670
671                        break;
672
673                    // for the other types
674                    default:
675
676                        // there is only one button
677                        plugin.settings.buttons = ['Ok'];
678
679                }
680
681            // return the buttons in reversed order
682            // (buttons need to be added in reverse order because they are floated to the right)
683            return plugin.settings.buttons.reverse();
684
685        }
686
687        /**
688         *  Returns the type of the dialog box, or FALSE if type is not one of the five known types.
689         *
690         *  Values that may be returned by this method are:
691         *  -   Confirmation
692         *  -   Error
693         *  -   Information
694         *  -   Question
695         *  -   Warning
696         *
697         *  @return boolean     Returns the type of the dialog box, or FALSE if type is not one of the five known types.
698         *
699         *  @access private
700         */
701        var get_type = function() {
702
703            // see what is the type of the dialog box
704            switch (plugin.settings.type) {
705
706                // if one of the five supported types
707                case 'confirmation':
708                case 'error':
709                case 'information':
710                case 'question':
711                case 'warning':
712
713                    // return the dialog box's type, first letter capital
714                    return plugin.settings.type.charAt(0).toUpperCase() + plugin.settings.type.slice(1).toLowerCase();
715
716                    break;
717
718                // if unknown type
719                default:
720
721                    // return FALSE
722                    return false;
723
724            }
725
726        }
727
728        /**
729         *  Function to be called when the "onKeyUp" event occurs
730         *
731         *  Why as a sepparate function and not inline when binding the event? Because only this way we can "unbind" it
732         *  when we close the dialog box
733         *
734         *  @return boolean     Returns TRUE
735         *
736         *  @access private
737         */
738        var _keyup = function(e) {
739
740            // if pressed key is ESC
741            // remove the overlay and the dialog box from the DOM
742            if (e.which == 27) plugin.close();
743
744            // let the event bubble up
745            return true;
746
747        }
748
749        /**
750         *  Function to be called when the "onScroll" event occurs in Internet Explorer 6.
751         *
752         *  Why as a sepparate function and not inline when binding the event? Because only this way we can "unbind" it
753         *  when we close the dialog box
754         *
755         *  @return void
756         *
757         *  @access private
758         */
759        var _scroll = function() {
760
761            // make sure the overlay and the dialog box always stay in the correct position
762            emulate_fixed_position();
763
764        }
765
766        // fire up the plugin!
767        // call the "constructor" method
768        return plugin.init();
769
770    }
771
772})(jQuery);
Note: See TracBrowser for help on using the repository browser.