      1 /*
      2  * jQuery Pines Notify (pnotify) Plugin 1.0.2-richfaces
      3  *
      4  * Copyright (c) 2009-2011 Hunter Perrin
      5  *
      6  * Triple license under the GPL, LGPL, and MPL:
      7  *    http://www.gnu.org/licenses/gpl.html
      8  *    http://www.gnu.org/licenses/lgpl.html
      9  *    http://www.mozilla.org/MPL/MPL-1.1.html
     10  *
     11  * Modified by: RichFaces team
     12  */
     13
     14 (function($) {
     15     var history_handle_top, timer;
     16     var body;
     17     var jwindow;
     18     $.extend({
     19         pnotify_remove_all: function () {
     20             var body_data = body.data("pnotify");
     21             /* POA: Added null-check */
     22             if (body_data && body_data.length) {
     23                 $.each(body_data, function() {
     24                     if (this.pnotify_remove)
     25                         this.pnotify_remove();
     26                 });
     27             }
     28         },
     29         pnotify_position_all: function () {
     30             if (timer)
     31                 clearTimeout(timer);
     32             timer = null;
     33             var body_data = body.data("pnotify");
     34             if (!body_data || !body_data.length)
     35                 return;
     36             $.each(body_data, function() {
     37                 var s = this.opts.pnotify_stack;
     38                 if (!s) return;
     39                 if (!s.nextpos1)
     40                     s.nextpos1 = s.firstpos1;
     41                 if (!s.nextpos2)
     42                     s.nextpos2 = s.firstpos2;
     43                 if (!s.addpos2)
     44                     s.addpos2 = 0;
     45                 if (this.css("display") != "none") {
     46                     var curpos1, curpos2;
     47                     var animate = {};
     48                     // Calculate the current pos1 value.
     49                     var csspos1;
     50                     switch (s.dir1) {
     51                         case "down":
     52                             csspos1 = "top";
     53                             break;
     54                         case "up":
     55                             csspos1 = "bottom";
     56                             break;
     57                         case "left":
     58                             csspos1 = "right";
     59                             break;
     60                         case "right":
     61                             csspos1 = "left";
     62                             break;
     63                     }
     64                     curpos1 = parseInt(this.css(csspos1));
     65                     if (isNaN(curpos1))
     66                         curpos1 = 0;
     67                     // Remember the first pos1, so the first visible notice goes there.
     68                     if (typeof s.firstpos1 == "undefined") {
     69                         s.firstpos1 = curpos1;
     70                         s.nextpos1 = s.firstpos1;
     71                     }
     72                     // Calculate the current pos2 value.
     73                     var csspos2;
     74                     switch (s.dir2) {
     75                         case "down":
     76                             csspos2 = "top";
     77                             break;
     78                         case "up":
     79                             csspos2 = "bottom";
     80                             break;
     81                         case "left":
     82                             csspos2 = "right";
     83                             break;
     84                         case "right":
     85                             csspos2 = "left";
     86                             break;
     87                     }
     88                     curpos2 = parseInt(this.css(csspos2));
     89                     if (isNaN(curpos2))
     90                         curpos2 = 0;
     91                     // Remember the first pos2, so the first visible notice goes there.
     92                     if (typeof s.firstpos2 == "undefined") {
     93                         s.firstpos2 = curpos2;
     94                         s.nextpos2 = s.firstpos2;
     95                     }
     96                     // Check that it's not beyond the viewport edge.
     97                     if ((s.dir1 == "down" && s.nextpos1 + this.height() > jwindow.height()) ||
     98                             (s.dir1 == "up" && s.nextpos1 + this.height() > jwindow.height()) ||
     99                             (s.dir1 == "left" && s.nextpos1 + this.width() > jwindow.width()) ||
    100                             (s.dir1 == "right" && s.nextpos1 + this.width() > jwindow.width())) {
    101                         // If it is, it needs to go back to the first pos1, and over on pos2.
    102                         s.nextpos1 = s.firstpos1;
    103                         s.nextpos2 += s.addpos2 + 10;
    104                         s.addpos2 = 0;
    105                     }
    106                     // Animate if we're moving on dir2.
    107                     if (s.animation && s.nextpos2 < curpos2) {
    108                         switch (s.dir2) {
    109                             case "down":
    110                                 animate.top = s.nextpos2 + "px";
    111                                 break;
    112                             case "up":
    113                                 animate.bottom = s.nextpos2 + "px";
    114                                 break;
    115                             case "left":
    116                                 animate.right = s.nextpos2 + "px";
    117                                 break;
    118                             case "right":
    119                                 animate.left = s.nextpos2 + "px";
    120                                 break;
    121                         }
    122                     } else
    123                         this.css(csspos2, s.nextpos2 + "px");
    124                     // Keep track of the widest/tallest notice in the column/row, so we can push the next column/row.
    125                     switch (s.dir2) {
    126                         case "down":
    127                         case "up":
    128                             if (this.outerHeight(true) > s.addpos2)
    129                                 s.addpos2 = this.height();
    130                             break;
    131                         case "left":
    132                         case "right":
    133                             if (this.outerWidth(true) > s.addpos2)
    134                                 s.addpos2 = this.width();
    135                             break;
    136                     }
    137                     // Move the notice on dir1.
    138                     if (s.nextpos1) {
    139                         // Animate if we're moving toward the first pos.
    140                         if (s.animation && (curpos1 > s.nextpos1 || animate.top || animate.bottom || animate.right || animate.left)) {
    141                             switch (s.dir1) {
    142                                 case "down":
    143                                     animate.top = s.nextpos1 + "px";
    144                                     break;
    145                                 case "up":
    146                                     animate.bottom = s.nextpos1 + "px";
    147                                     break;
    148                                 case "left":
    149                                     animate.right = s.nextpos1 + "px";
    150                                     break;
    151                                 case "right":
    152                                     animate.left = s.nextpos1 + "px";
    153                                     break;
    154                             }
    155                         } else
    156                             this.css(csspos1, s.nextpos1 + "px");
    157                     }
    158                     if (animate.top || animate.bottom || animate.right || animate.left)
    159                         this.animate(animate, {duration: 500, queue: false});
    160                     // Calculate the next dir1 position.
    161                     switch (s.dir1) {
    162                         case "down":
    163                         case "up":
    164                             s.nextpos1 += this.height() + 10;
    165                             break;
    166                         case "left":
    167                         case "right":
    168                             s.nextpos1 += this.width() + 10;
    169                             break;
    170                     }
    171                 }
    172             });
    173             // Reset the next position data.
    174             $.each(body_data, function() {
    175                 var s = this.opts.pnotify_stack;
    176                 if (!s) return;
    177                 s.nextpos1 = s.firstpos1;
    178                 s.nextpos2 = s.firstpos2;
    179                 s.addpos2 = 0;
    180                 s.animation = true;
    181             });
    182         },
    183         pnotify: function(options) {
    184             if (!body)
    185                 body = $("body");
    186             if (!jwindow)
    187                 jwindow = $(window);
    188
    189             var animating;
    190
    191             // Build main options.
    192             var opts;
    193             if (typeof options != "object") {
    194                 opts = $.extend({}, $.pnotify.defaults);
    195                 opts.pnotify_text = options;
    196             } else {
    197                 opts = $.extend({}, $.pnotify.defaults, options);
    198                 if (opts['pnotify_animation'] instanceof Object) {
    199                     opts['pnotify_animation'] = $.extend({
    200                         effect_in:$.pnotify.defaults.pnotify_animation,
    201                         effect_out:$.pnotify.defaults.pnotify_animation
    202                     }, opts['pnotify_animation']);
    203                 }
    204             }
    205
    206             if (opts.pnotify_before_init) {
    207                 if (opts.pnotify_before_init(opts) === false)
    208                     return null;
    209             }
    210
    211             // This keeps track of the last element the mouse was over, so
    212             // mouseleave, mouseenter, etc can be called.
    213             var nonblock_last_elem;
    214             // This is used to pass events through the notice if it is non-blocking.
    215             var nonblock_pass = function(e, e_name) {
    216                 pnotify.css("display", "none");
    217                 var element_below = document.elementFromPoint(e.clientX, e.clientY);
    218                 pnotify.css("display", "block");
    219                 var jelement_below = $(element_below);
    220                 var cursor_style = jelement_below.css("cursor");
    221                 pnotify.css("cursor", cursor_style != "auto" ? cursor_style : "default");
    222                 // If the element changed, call mouseenter, mouseleave, etc.
    223                 if (!nonblock_last_elem || nonblock_last_elem.get(0) != element_below) {
    224                     if (nonblock_last_elem) {
    225                         dom_event.call(nonblock_last_elem.get(0), "mouseleave", e.originalEvent);
    226                         dom_event.call(nonblock_last_elem.get(0), "mouseout", e.originalEvent);
    227                     }
    228                     dom_event.call(element_below, "mouseenter", e.originalEvent);
    229                     dom_event.call(element_below, "mouseover", e.originalEvent);
    230                 }
    231                 dom_event.call(element_below, e_name, e.originalEvent);
    232                 // Remember the latest element the mouse was over.
    233                 nonblock_last_elem = jelement_below;
    234             };
    235
    236             // Create our widget.
    237             // Stop animation, reset the removal timer, and show the close
    238             // button when the user mouses over.
    239             var pnotify = $("<div />", {
    240                 "class": "rf-ntf " + opts.pnotify_addclass,
    241                 "css": {"display": "none"},
    242                 "mouseenter": function(e) {
    243                     if (opts.pnotify_nonblock) e.stopPropagation();
    244                     if (opts.pnotify_mouse_reset && animating == "out") {
    245                         // If it's animating out, animate back in really quick.
    246                         pnotify.stop(true);
    247                         animating = "in";
    248                         pnotify.css("height", "auto").animate({"width": opts.pnotify_width, "opacity": opts.pnotify_nonblock ? opts.pnotify_nonblock_opacity : opts.pnotify_opacity}, "fast");
    249                     }
    250                     if (opts.pnotify_nonblock) {
    251                         // If it's non-blocking, animate to the other opacity.
    252                         pnotify.animate({"opacity": opts.pnotify_nonblock_opacity}, "fast");
    253                     }
    254                     if (opts.pnotify_hide && opts.pnotify_mouse_reset) pnotify.pnotify_cancel_remove();
    255                     //                    Do not update
    256                     if (opts.pnotify_closer && !opts.pnotify_nonblock) pnotify.closer.css("visibility", "visible");
    257                 },
    258                 "mouseleave": function(e) {
    259                     if (opts.pnotify_nonblock) e.stopPropagation();
    260                     nonblock_last_elem = null;
    261                     pnotify.css("cursor", "auto");
    262                     if (opts.pnotify_nonblock && animating != "out")
    263                         pnotify.animate({"opacity": opts.pnotify_opacity}, "fast");
    264                     if (opts.pnotify_hide && opts.pnotify_mouse_reset) pnotify.pnotify_queue_remove();
    265                     //                    Do not update
    266                     pnotify.closer.css("visibility", "hidden");
    267                     $.pnotify_position_all();
    268                 },
    269                 "mouseover": function(e) {
    270                     if (opts.pnotify_nonblock) e.stopPropagation();
    271                 },
    272                 "mouseout": function(e) {
    273                     if (opts.pnotify_nonblock) e.stopPropagation();
    274                 },
    275                 "mousemove": function(e) {
    276                     if (opts.pnotify_nonblock) {
    277                         e.stopPropagation();
    278                         nonblock_pass(e, "onmousemove");
    279                     }
    280                 },
    281                 "mousedown": function(e) {
    282                     if (opts.pnotify_nonblock) {
    283                         e.stopPropagation();
    284                         e.preventDefault();
    285                         nonblock_pass(e, "onmousedown");
    286                     }
    287                 },
    288                 "mouseup": function(e) {
    289                     if (opts.pnotify_nonblock) {
    290                         e.stopPropagation();
    291                         e.preventDefault();
    292                         nonblock_pass(e, "onmouseup");
    293                     }
    294                 },
    295                 "click": function(e) {
    296                     if (opts.pnotify_nonblock) {
    297                         e.stopPropagation();
    298                         nonblock_pass(e, "onclick");
    299                     }
    300                 },
    301                 "dblclick": function(e) {
    302                     if (opts.pnotify_nonblock) {
    303                         e.stopPropagation();
    304                         nonblock_pass(e, "ondblclick");
    305                     }
    306                 }
    307             });
    308             pnotify.opts = opts;
    309             // Create a drop shadow.
    310             if (opts.pnotify_shadow && !$.browser.msie)
    311                 pnotify.shadow_container = $("<div />", {"class": "rf-ntf-shdw"}).prependTo(pnotify);
    312             // Create a container for the notice contents.
    313             pnotify.container = $("<div />", {"class": "rf-ntf-cnt"})
    314                     .appendTo(pnotify);
    315
    316             pnotify.pnotify_version = "1.0.2";
    317
    318             // This function is for updating the notice.
    319             pnotify.pnotify = function(options) {
    320                 // Update the notice.
    321                 var old_opts = opts;
    322                 if (typeof options == "string")
    323                     opts.pnotify_text = options;
    324                 else
    325                     opts = $.extend({}, opts, options);
    326                 pnotify.opts = opts;
    327                 // Update the shadow.
    328                 if (opts.pnotify_shadow != old_opts.pnotify_shadow) {
    329                     if (opts.pnotify_shadow && !$.browser.msie)
    330                         pnotify.shadow_container = $("<div />", {"class": "rf-ntf-shdw"}).prependTo(pnotify);
    331                     else
    332                         pnotify.children(".rf-ntf-shdw").remove();
    333                 }
    334                 // Update the additional classes.
    335                 if (opts.pnotify_addclass === false)
    336                     pnotify.removeClass(old_opts.pnotify_addclass);
    337                 else if (opts.pnotify_addclass !== old_opts.pnotify_addclass)
    338                     pnotify.removeClass(old_opts.pnotify_addclass).addClass(opts.pnotify_addclass);
    339                 // Update the title.
    340                 if (opts.pnotify_title === false)
    341                     pnotify.title_container.hide("fast");
    342                 else if (opts.pnotify_title !== old_opts.pnotify_title)
    343                     pnotify.title_container.html(opts.pnotify_title).show(200);
    344                 // Update the text.
    345                 if (opts.pnotify_text === false) {
    346                     pnotify.text_container.hide("fast");
    347                 } else if (opts.pnotify_text !== old_opts.pnotify_text) {
    348                     if (opts.pnotify_insert_brs)
    349                         opts.pnotify_text = opts.pnotify_text.replace(/\n/g, "<br />");
    350                     pnotify.text_container.html(opts.pnotify_text).show(200);
    351                 }
    352                 pnotify.pnotify_history = opts.pnotify_history;
    353                 // Change the notice type.
    354                 if (opts.pnotify_type != old_opts.pnotify_type)
    355                     pnotify.container.toggleClass("rf-ntf-cnt rf-ntf-cnt-hov");
    356                 if ((opts.pnotify_notice_icon != old_opts.pnotify_notice_icon && opts.pnotify_type == "notice") ||
    357                         (opts.pnotify_error_icon != old_opts.pnotify_error_icon && opts.pnotify_type == "error") ||
    358                         (opts.pnotify_type != old_opts.pnotify_type)) {
    359                     // Remove any old icon.
    360                     pnotify.container.find("div.rf-ntf-ico").remove();
    361                     //					if ((opts.pnotify_error_icon && opts.pnotify_type == "error") || (opts.pnotify_notice_icon)) {
    362                     // Build the new icon.
    363                     $("<div />", {"class": "rf-ntf-ico"})
    364                             .append($("<span />", {"class": opts.pnotify_type == "error" ? opts.pnotify_error_icon : opts.pnotify_notice_icon}))
    365                             .prependTo(pnotify.container);
    366                     //					}
    367                 }
    368                 // Update the width.
    369                 if (opts.pnotify_width !== old_opts.pnotify_width)
    370                     pnotify.animate({width: opts.pnotify_width});
    371                 // Update the minimum height.
    372                 if (opts.pnotify_min_height !== old_opts.pnotify_min_height)
    373                     pnotify.container.animate({minHeight: opts.pnotify_min_height});
    374                 // Update the opacity.
    375                 if (opts.pnotify_opacity !== old_opts.pnotify_opacity)
    376                     pnotify.fadeTo(opts.pnotify_animate_speed, opts.pnotify_opacity);
    377                 if (!opts.pnotify_hide)
    378                     pnotify.pnotify_cancel_remove();
    379                 else if (!old_opts.pnotify_hide)
    380                     pnotify.pnotify_queue_remove();
    381                 pnotify.pnotify_queue_position();
    382                 return pnotify;
    383             };
    384
    385             // Queue the position function so it doesn't run repeatedly and use
    386             // up resources.
    387             pnotify.pnotify_queue_position = function() {
    388                 if (timer)
    389                     clearTimeout(timer);
    390                 timer = setTimeout($.pnotify_position_all, 10);
    391             };
    392
    393             // Display the notice.
    394             pnotify.pnotify_display = function() {
    395                 // If the notice is not in the DOM, append it.
    396                 if (!pnotify.parent().length)
    397                     pnotify.appendTo(body);
    398                 // Run callback.
    399                 if (opts.pnotify_before_open) {
    400                     if (opts.pnotify_before_open(pnotify) === false)
    401                         return;
    402                 }
    403                 pnotify.pnotify_queue_position();
    404                 // First show it, then set its opacity, then hide it.
    405                 if (opts.pnotify_animation == "fade" || opts.pnotify_animation.effect_in == "fade") {
    406                     // If it's fading in, it should start at 0.
    407                     pnotify.show().fadeTo(0, 0).hide();
    408                 } else {
    409                     // Or else it should be set to the opacity.
    410                     if (opts.pnotify_opacity != 1)
    411                         pnotify.show().fadeTo(0, opts.pnotify_opacity).hide();
    412                 }
    413                 pnotify.animate_in(function() {
    414                     if (opts.pnotify_after_open)
    415                         opts.pnotify_after_open(pnotify);
    416
    417                     pnotify.pnotify_queue_position();
    418
    419                     // Now set it to hide.
    420                     if (opts.pnotify_hide)
    421                         pnotify.pnotify_queue_remove();
    422                 });
    423             };
    424
    425             // Remove the notice.
    426             pnotify.pnotify_remove = function() {
    427                 if (pnotify.timer) {
    428                     window.clearTimeout(pnotify.timer);
    429                     pnotify.timer = null;
    430                 }
    431                 // Run callback.
    432                 if (opts.pnotify_before_close) {
    433                     if (opts.pnotify_before_close(pnotify) === false)
    434                         return;
    435                 }
    436                 pnotify.animate_out(function() {
    437                     if (opts.pnotify_after_close) {
    438                         if (opts.pnotify_after_close(pnotify) === false)
    439                             return;
    440                     }
    441                     pnotify.pnotify_queue_position();
    442                     // If we're supposed to remove the notice from the DOM, do it.
    443                     if (opts.pnotify_remove)
    444                         pnotify.detach();
    445                 });
    446             };
    447
    448             // Animate the notice in.
    449             pnotify.animate_in = function(callback) {
    450                 // Declare that the notice is animating in. (Or has completed animating in.)
    451                 animating = "in";
    452                 var animation;
    453                 if (typeof opts.pnotify_animation.effect_in != "undefined")
    454                     animation = opts.pnotify_animation.effect_in;
    455                 else
    456                     animation = opts.pnotify_animation;
    457                 if (animation == "none") {
    458                     pnotify.show();
    459                     callback();
    460                 } else if (animation == "show")
    461                     pnotify.show(opts.pnotify_animate_speed, callback);
    462                 else if (animation == "fade")
    463                     pnotify.show().fadeTo(opts.pnotify_animate_speed, opts.pnotify_opacity, callback);
    464                 else if (animation == "slide")
    465                     pnotify.slideDown(opts.pnotify_animate_speed, callback);
    466                 else if (typeof animation == "function")
    467                     animation("in", callback, pnotify);
    468                 else if (pnotify.effect)
    469                     pnotify.effect(animation, {}, opts.pnotify_animate_speed, callback);
    470             };
    471
    472             // Animate the notice out.
    473             pnotify.animate_out = function(callback) {
    474                 // Declare that the notice is animating out. (Or has completed animating out.)
    475                 animating = "out";
    476                 var animation;
    477                 if (typeof opts.pnotify_animation.effect_out != "undefined")
    478                     animation = opts.pnotify_animation.effect_out;
    479                 else
    480                     animation = opts.pnotify_animation;
    481                 if (animation == "none") {
    482                     pnotify.hide();
    483                     callback();
    484                 } else if (animation == "show")
    485                     pnotify.hide(opts.pnotify_animate_speed, callback);
    486                 else if (animation == "fade")
    487                     pnotify.fadeOut(opts.pnotify_animate_speed, callback);
    488                 else if (animation == "slide")
    489                     pnotify.slideUp(opts.pnotify_animate_speed, callback);
    490                 else if (typeof animation == "function")
    491                     animation("out", callback, pnotify);
    492                 else if (pnotify.effect)
    493                     pnotify.effect(animation, {}, opts.pnotify_animate_speed, callback);
    494             };
    495
    496             // Cancel any pending removal timer.
    497             pnotify.pnotify_cancel_remove = function() {
    498                 if (pnotify.timer)
    499                     window.clearTimeout(pnotify.timer);
    500             };
    501
    502             // Queue a removal timer.
    503             pnotify.pnotify_queue_remove = function() {
    504                 // Cancel any current removal timer.
    505                 pnotify.pnotify_cancel_remove();
    506                 pnotify.timer = window.setTimeout(function() {
    507                     pnotify.pnotify_remove();
    508                 }, (isNaN(opts.pnotify_delay) ? 0 : opts.pnotify_delay));
    509             };
    510
    511             // Provide a button to close the notice.
    512             pnotify.closer = $("<div />", {
    513                 "class": "rf-ntf-cls",
    514                 "css": {"cursor": "pointer", "visibility": "hidden"},
    515                 "click": function() {
    516                     pnotify.pnotify_remove();
    517                     //                    Do not update
    518                     pnotify.closer.css("visibility", "hidden");
    519                 }
    520             })
    521                     .append($("<span />", {"class": "rf-ntf-cls-ico"}))
    522                     .appendTo(pnotify.container);
    523
    524             // Add the appropriate icon.
    525             //			if ((opts.pnotify_error_icon && opts.pnotify_type == "error") || (opts.pnotify_notice_icon)) {
    526             $("<div />", {"class": "rf-ntf-ico"})
    527                     .append($("<span />", {"class": opts.pnotify_type == "error" ? opts.pnotify_error_icon : opts.pnotify_notice_icon}))
    528                     .appendTo(pnotify.container);
    529             //			}
    530
    531             // Add a title.
    532             pnotify.title_container = $("<div />", {
    533                 "class": "rf-ntf-sum",
    534                 "html": opts.pnotify_title
    535             })
    536                     .appendTo(pnotify.container);
    537             if (opts.pnotify_title === false)
    538                 pnotify.title_container.hide();
    539
    540             // Replace new lines with HTML line breaks.
    541             if (opts.pnotify_insert_brs && typeof opts.pnotify_text == "string")
    542                 opts.pnotify_text = opts.pnotify_text.replace(/\n/g, "<br />");
    543             // Add text.
    544             pnotify.text_container = $("<div />", {
    545                 "class": "rf-ntf-det",
    546                 "html": opts.pnotify_text
    547             })
    548                     .appendTo(pnotify.container);
    549             if (opts.pnotify_text === false)
    550                 pnotify.text_container.hide();
    551
    552             //Append div with clear:both class
    553             $("<div />", {"class":"rf-ntf-clr"}).appendTo(pnotify.container);
    554
    555             // Set width and min height.
    556             if (typeof opts.pnotify_width == "string")
    557                 pnotify.css("width", opts.pnotify_width);
    558             if (typeof opts.pnotify_min_height == "string")
    559                 pnotify.container.css("min-height", opts.pnotify_min_height);
    560
    561             // The history variable controls whether the notice gets redisplayed
    562             // by the history pull down.
    563             pnotify.pnotify_history = opts.pnotify_history;
    564
    565             // Add the notice to the notice array.
    566             var body_data = body.data("pnotify");
    567             if (body_data == null || typeof body_data != "object")
    568                 body_data = [];
    569             if (opts.pnotify_stack.push == "top")
    570                 body_data = $.merge([pnotify], body_data);
    571             else
    572                 body_data = $.merge(body_data, [pnotify]);
    573             body.data("pnotify", body_data);
    574
    575             // Run callback.
    576             if (opts.pnotify_after_init)
    577                 opts.pnotify_after_init(pnotify);
    578
    579             if (opts.pnotify_history) {
    580                 // If there isn't a history pull down, create one.
    581                 var body_history = body.data("pnotify_history");
    582                 if (typeof body_history == "undefined") {
    583                     body_history = $("<div />", {
    584                         "class": "rf-ntf-hstr",
    585                         "mouseleave": function() {
    586                             body_history.animate({top: "-" + history_handle_top + "px"}, {duration: 100, queue: false});
    587                         }
    588                     })
    589                             .append($("<div />", {"class": "rf-ntf-hstr-hdr", "text": "Redisplay"}))
    590                             .append($("<button />", {
    591                         "class": "rf-ntf-hstr-all",
    592                         "text": "All",
    593                         //							"mouseenter": function(){
    594                         //								$(this).addClass("ui-state-hover");
    595                         //							},
    596                         //							"mouseleave": function(){
    597                         //								$(this).removeClass("ui-state-hover");
    598                         //							},
    599                         "click": function() {
    600                             // Display all notices. (Disregarding non-history notices.)
    601                             //                            Don't change it to pnotify's new version, cause using body_data here is a bug
    602                             $.each(body.data("pnotify"), function() {
    603                                 if (this.pnotify_history && this.pnotify_display)
    604                                     this.pnotify_display();
    605                             });
    606                             return false;
    607                         }
    608                     }))
    609                             .append($("<button />", {
    610                         "class": "rf-ntf-hstr-last",
    611                         "text": "Last",
    612                         //							"mouseenter": function(){
    613                         //								$(this).addClass("ui-state-hover");
    614                         //							},
    615                         //							"mouseleave": function(){
    616                         //								$(this).removeClass("ui-state-hover");
    617                         //							},
    618                         "click": function() {
    619                             // Look up the last history notice, and display it.
    620                             var i = 1;
    621                             var body_data = body.data("pnotify");
    622                             while (!body_data[body_data.length - i] || !body_data[body_data.length - i].pnotify_history || body_data[body_data.length - i].is(":visible")) {
    623                                 if (body_data.length - i === 0)
    624                                     return false;
    625                                 i++;
    626                             }
    627                             var n = body_data[body_data.length - i];
    628                             if (n.pnotify_display)
    629                                 n.pnotify_display();
    630                             return false;
    631                         }
    632                     }))
    633                             .appendTo(body);
    634
    635                     // Make a handle so the user can pull down the history pull down.
    636                     var handle = $("<span />", {
    637                         "class": "rf-ntf-hstr-hndl",
    638                         "mouseenter": function() {
    639                             body_history.animate({top: "0"}, {duration: 100, queue: false});
    640                         }
    641                     })
    642                             .appendTo(body_history);
    643
    644                     // Get the top of the handle.
    645                     history_handle_top = handle.offset().top + 2;
    646                     // Hide the history pull down up to the top of the handle.
    647                     body_history.css({top: "-" + history_handle_top + "px"});
    648                     // Save the history pull down.
    649                     body.data("pnotify_history", body_history);
    650                 }
    651             }
    652
    653             // Mark the stack so it won't animate the new notice.
    654             opts.pnotify_stack.animation = false;
    655
    656             // Display the notice.
    657             pnotify.pnotify_display();
    658
    659             return pnotify;
    660         }
    661     });
    662
    663     // Some useful regexes.
    664     var re_on = /^on/;
    665     var re_mouse_events = /^(dbl)?click$|^mouse(move|down|up|over|out|enter|leave)$|^contextmenu$/;
    666     var re_ui_events = /^(focus|blur|select|change|reset)$|^key(press|down|up)$/;
    667     var re_html_events = /^(scroll|resize|(un)?load|abort|error)$/;
    668     // Fire a DOM event.
    669     var dom_event = function(e, orig_e) {
    670         var event_object;
    671         e = e.toLowerCase();
    672         if (document.createEvent && this.dispatchEvent) {
    673             // FireFox, Opera, Safari, Chrome
    674             e = e.replace(re_on, '');
    675             if (e.match(re_mouse_events)) {
    676                 // This allows the click event to fire on the notice. There is
    677                 // probably a much better way to do it.
    678                 $(this).offset();
    679                 event_object = document.createEvent("MouseEvents");
    680                 event_object.initMouseEvent(
    681                         e, orig_e.bubbles, orig_e.cancelable, orig_e.view, orig_e.detail,
    682                         orig_e.screenX, orig_e.screenY, orig_e.clientX, orig_e.clientY,
    683                         orig_e.ctrlKey, orig_e.altKey, orig_e.shiftKey, orig_e.metaKey, orig_e.button, orig_e.relatedTarget
    684                         );
    685             } else if (e.match(re_ui_events)) {
    686                 event_object = document.createEvent("UIEvents");
    687                 event_object.initUIEvent(e, orig_e.bubbles, orig_e.cancelable, orig_e.view, orig_e.detail);
    688             } else if (e.match(re_html_events)) {
    689                 event_object = document.createEvent("HTMLEvents");
    690                 event_object.initEvent(e, orig_e.bubbles, orig_e.cancelable);
    691             }
    692             if (!event_object) return;
    693             this.dispatchEvent(event_object);
    694         } else {
    695             // Internet Explorer
    696             if (!e.match(re_on)) e = "on" + e;
    697             event_object = document.createEventObject(orig_e);
    698             this.fireEvent(e, event_object);
    699         }
    700     };
    701
    702     $.pnotify.defaults = {
    703         // The notice's title.
    704         pnotify_title: false,
    705         // The notice's text.
    706         pnotify_text: false,
    707         // Additional classes to be added to the notice. (For custom styling.)
    708         pnotify_addclass: "",
    709         // Create a non-blocking notice. It lets the user click elements underneath it.
    710         pnotify_nonblock: false,
    711         // The opacity of the notice (if it's non-blocking) when the mouse is over it.
    712         pnotify_nonblock_opacity: .2,
    713         // Display a pull down menu to redisplay previous notices, and place the notice in the history.
    714         pnotify_history: true,
    715         // Width of the notice.
    716         pnotify_width: "300px",
    717         // Minimum height of the notice. It will expand to fit content.
    718         pnotify_min_height: "16px",
    719         // Type of the notice. "notice" or "error".
    720         pnotify_type: "notice",
    721         // The icon class to use if type is notice.
    722         pnotify_notice_icon: "",
    723         // The icon class to use if type is error.
    724         pnotify_error_icon: "",
    725         // The animation to use when displaying and hiding the notice. "none", "show", "fade", and "slide" are built in to jQuery. Others require jQuery UI. Use an object with effect_in and effect_out to use different effects.
    726         pnotify_animation: "fade",
    727         // Speed at which the notice animates in and out. "slow", "def" or "normal", "fast" or number of milliseconds.
    728         pnotify_animate_speed: "slow",
    729         // Opacity of the notice.
    730         pnotify_opacity: 1,
    731         // Display a drop shadow.
    732         pnotify_shadow: false,
    733         // Provide a button for the user to manually close the notice.
    734         pnotify_closer: true,
    735         // After a delay, remove the notice.
    736         pnotify_hide: true,
    737         // Delay in milliseconds before the notice is removed.
    738         pnotify_delay: 8000,
    739         // Reset the hide timer if the mouse moves over the notice.
    740         pnotify_mouse_reset: true,
    741         // Remove the notice's elements from the DOM after it is removed.
    742         pnotify_remove: true,
    743         // Change new lines to br tags.
    744         pnotify_insert_brs: true,
    745         // The stack on which the notices will be placed. Also controls the direction the notices stack.
    746         pnotify_stack: {"dir1": "down", "dir2": "left", "push": "bottom"}
    747     };
    748 })(jQuery);