1/* 2 * jQuery Pines Notify (pnotify) Plugin 1.2.0 3 * 4 * http://pinesframework.org/pnotify/ 5 * Copyright (c) 2009-2012 Hunter Perrin 6 * 7 * Triple license under the GPL, LGPL, and MPL: 8 * http://www.gnu.org/licenses/gpl.html 9 * http://www.gnu.org/licenses/lgpl.html 10 * http://www.mozilla.org/MPL/MPL-1.1.html 11 */ 12 13(function($) { 14 var history_handle_top, 15 timer, 16 body, 17 jwindow = $(window), 18 styling = { 19 jqueryui: { 20 container: "ui-widget ui-widget-content ui-corner-all", 21 notice: "ui-state-highlight", 22 // (The actual jQUI notice icon looks terrible.) 23 notice_icon: "ui-icon ui-icon-info", 24 info: "", 25 info_icon: "ui-icon ui-icon-info", 26 success: "ui-state-default", 27 success_icon: "ui-icon ui-icon-circle-check", 28 error: "ui-state-error", 29 error_icon: "ui-icon ui-icon-alert", 30 closer: "ui-icon ui-icon-close", 31 pin_up: "ui-icon ui-icon-pin-w", 32 pin_down: "ui-icon ui-icon-pin-s", 33 hi_menu: "ui-state-default ui-corner-bottom", 34 hi_btn: "ui-state-default ui-corner-all", 35 hi_btnhov: "ui-state-hover", 36 hi_hnd: "ui-icon ui-icon-grip-dotted-horizontal" 37 }, 38 bootstrap: { 39 container: "alert", 40 notice: "", 41 notice_icon: "icon-exclamation-sign", 42 info: "alert-info", 43 info_icon: "icon-info-sign", 44 success: "alert-success", 45 success_icon: "icon-ok-sign", 46 error: "alert-error", 47 error_icon: "icon-warning-sign", 48 closer: "icon-remove", 49 pin_up: "icon-pause", 50 pin_down: "icon-play", 51 hi_menu: "well", 52 hi_btn: "btn", 53 hi_btnhov: "", 54 hi_hnd: "icon-chevron-down" 55 } 56 }; 57 // Set global variables. 58 var do_when_ready = function(){ 59 body = $("body"); 60 jwindow = $(window); 61 // Reposition the notices when the window resizes. 62 jwindow.bind('resize', function(){ 63 if (timer) 64 clearTimeout(timer); 65 timer = setTimeout($.pnotify_position_all, 10); 66 }); 67 }; 68 if (document.body) 69 do_when_ready(); 70 else 71 $(do_when_ready); 72 $.extend({ 73 pnotify_remove_all: function () { 74 var notices_data = jwindow.data("pnotify"); 75 /* POA: Added null-check */ 76 if (notices_data && notices_data.length) { 77 $.each(notices_data, function(){ 78 if (this.pnotify_remove) 79 this.pnotify_remove(); 80 }); 81 } 82 }, 83 pnotify_position_all: function () { 84 // This timer is used for queueing this function so it doesn't run 85 // repeatedly. 86 if (timer) 87 clearTimeout(timer); 88 timer = null; 89 // Get all the notices. 90 var notices_data = jwindow.data("pnotify"); 91 if (!notices_data || !notices_data.length) 92 return; 93 // Reset the next position data. 94 $.each(notices_data, function(){ 95 var s = this.opts.stack; 96 if (!s) return; 97 s.nextpos1 = s.firstpos1; 98 s.nextpos2 = s.firstpos2; 99 s.addpos2 = 0; 100 s.animation = true; 101 }); 102 $.each(notices_data, function(){ 103 this.pnotify_position(); 104 }); 105 }, 106 pnotify: function(options) { 107 // Stores what is currently being animated (in or out). 108 var animating; 109 110 // Build main options. 111 var opts; 112 if (typeof options != "object") { 113 opts = $.extend({}, $.pnotify.defaults); 114 opts.text = options; 115 } else { 116 opts = $.extend({}, $.pnotify.defaults, options); 117 } 118 // Translate old pnotify_ style options. 119 for (var i in opts) { 120 if (typeof i == "string" && i.match(/^pnotify_/)) 121 opts[i.replace(/^pnotify_/, "")] = opts[i]; 122 } 123 124 if (opts.before_init) { 125 if (opts.before_init(opts) === false) 126 return null; 127 } 128 129 // This keeps track of the last element the mouse was over, so 130 // mouseleave, mouseenter, etc can be called. 131 var nonblock_last_elem; 132 // This is used to pass events through the notice if it is non-blocking. 133 var nonblock_pass = function(e, e_name){ 134 pnotify.css("display", "none"); 135 var element_below = document.elementFromPoint(e.clientX, e.clientY); 136 pnotify.css("display", "block"); 137 var jelement_below = $(element_below); 138 var cursor_style = jelement_below.css("cursor"); 139 pnotify.css("cursor", cursor_style != "auto" ? cursor_style : "default"); 140 // If the element changed, call mouseenter, mouseleave, etc. 141 if (!nonblock_last_elem || nonblock_last_elem.get(0) != element_below) { 142 if (nonblock_last_elem) { 143 dom_event.call(nonblock_last_elem.get(0), "mouseleave", e.originalEvent); 144 dom_event.call(nonblock_last_elem.get(0), "mouseout", e.originalEvent); 145 } 146 dom_event.call(element_below, "mouseenter", e.originalEvent); 147 dom_event.call(element_below, "mouseover", e.originalEvent); 148 } 149 dom_event.call(element_below, e_name, e.originalEvent); 150 // Remember the latest element the mouse was over. 151 nonblock_last_elem = jelement_below; 152 }; 153 154 // Get our styling object. 155 var styles = styling[opts.styling]; 156 157 // Create our widget. 158 // Stop animation, reset the removal timer, and show the close 159 // button when the user mouses over. 160 var pnotify = $("<div />", { 161 "class": "ui-pnotify "+opts.addclass, 162 "css": {"display": "none"}, 163 "mouseenter": function(e){ 164 if (opts.nonblock) e.stopPropagation(); 165 if (opts.mouse_reset && animating == "out") { 166 // If it's animating out, animate back in really quickly. 167 pnotify.stop(true); 168 animating = "in"; 169 pnotify.css("height", "auto").animate({"width": opts.width, "opacity": opts.nonblock ? opts.nonblock_opacity : opts.opacity}, "fast"); 170 } 171 if (opts.nonblock) { 172 // If it's non-blocking, animate to the other opacity. 173 pnotify.animate({"opacity": opts.nonblock_opacity}, "fast"); 174 } 175 // Stop the close timer. 176 if (opts.hide && opts.mouse_reset) pnotify.pnotify_cancel_remove(); 177 // Show the buttons. 178 if (opts.sticker && !opts.nonblock) pnotify.sticker.trigger("pnotify_icon").css("visibility", "visible"); 179 if (opts.closer && !opts.nonblock) pnotify.closer.css("visibility", "visible"); 180 }, 181 "mouseleave": function(e){ 182 if (opts.nonblock) e.stopPropagation(); 183 nonblock_last_elem = null; 184 pnotify.css("cursor", "auto"); 185 // Animate back to the normal opacity. 186 if (opts.nonblock && animating != "out") 187 pnotify.animate({"opacity": opts.opacity}, "fast"); 188 // Start the close timer. 189 if (opts.hide && opts.mouse_reset) pnotify.pnotify_queue_remove(); 190 // Hide the buttons. 191 if (opts.sticker_hover) 192 pnotify.sticker.css("visibility", "hidden"); 193 if (opts.closer_hover) 194 pnotify.closer.css("visibility", "hidden"); 195 $.pnotify_position_all(); 196 }, 197 "mouseover": function(e){ 198 if (opts.nonblock) e.stopPropagation(); 199 }, 200 "mouseout": function(e){ 201 if (opts.nonblock) e.stopPropagation(); 202 }, 203 "mousemove": function(e){ 204 if (opts.nonblock) { 205 e.stopPropagation(); 206 nonblock_pass(e, "onmousemove"); 207 } 208 }, 209 "mousedown": function(e){ 210 if (opts.nonblock) { 211 e.stopPropagation(); 212 e.preventDefault(); 213 nonblock_pass(e, "onmousedown"); 214 } 215 }, 216 "mouseup": function(e){ 217 if (opts.nonblock) { 218 e.stopPropagation(); 219 e.preventDefault(); 220 nonblock_pass(e, "onmouseup"); 221 } 222 }, 223 "click": function(e){ 224 if (opts.nonblock) { 225 e.stopPropagation(); 226 nonblock_pass(e, "onclick"); 227 } 228 }, 229 "dblclick": function(e){ 230 if (opts.nonblock) { 231 e.stopPropagation(); 232 nonblock_pass(e, "ondblclick"); 233 } 234 } 235 }); 236 pnotify.opts = opts; 237 // Create a container for the notice contents. 238 pnotify.container = $("<div />", {"class": styles.container+" ui-pnotify-container "+(opts.type == "error" ? styles.error : (opts.type == "info" ? styles.info : (opts.type == "success" ? styles.success : styles.notice)))}) 239 .appendTo(pnotify); 240 if (opts.cornerclass != "") 241 pnotify.container.removeClass("ui-corner-all").addClass(opts.cornerclass); 242 // Create a drop shadow. 243 if (opts.shadow) 244 pnotify.container.addClass("ui-pnotify-shadow"); 245 246 // The current version of Pines Notify. 247 pnotify.pnotify_version = "1.2.0"; 248 249 // This function is for updating the notice. 250 pnotify.pnotify = function(options) { 251 // Update the notice. 252 var old_opts = opts; 253 if (typeof options == "string") 254 opts.text = options; 255 else 256 opts = $.extend({}, opts, options); 257 // Translate old pnotify_ style options. 258 for (var i in opts) { 259 if (typeof i == "string" && i.match(/^pnotify_/)) 260 opts[i.replace(/^pnotify_/, "")] = opts[i]; 261 } 262 pnotify.opts = opts; 263 // Update the corner class. 264 if (opts.cornerclass != old_opts.cornerclass) 265 pnotify.container.removeClass("ui-corner-all").addClass(opts.cornerclass); 266 // Update the shadow. 267 if (opts.shadow != old_opts.shadow) { 268 if (opts.shadow) 269 pnotify.container.addClass("ui-pnotify-shadow"); 270 else 271 pnotify.container.removeClass("ui-pnotify-shadow"); 272 } 273 // Update the additional classes. 274 if (opts.addclass === false) 275 pnotify.removeClass(old_opts.addclass); 276 else if (opts.addclass !== old_opts.addclass) 277 pnotify.removeClass(old_opts.addclass).addClass(opts.addclass); 278 // Update the title. 279 if (opts.title === false) 280 pnotify.title_container.slideUp("fast"); 281 else if (opts.title !== old_opts.title) { 282 if (opts.title_escape) 283 pnotify.title_container.text(opts.title).slideDown(200); 284 else 285 pnotify.title_container.html(opts.title).slideDown(200); 286 } 287 // Update the text. 288 if (opts.text === false) { 289 pnotify.text_container.slideUp("fast"); 290 } else if (opts.text !== old_opts.text) { 291 if (opts.text_escape) 292 pnotify.text_container.text(opts.text).slideDown(200); 293 else 294 pnotify.text_container.html(opts.insert_brs ? String(opts.text).replace(/\n/g, "<br />") : opts.text).slideDown(200); 295 } 296 // Update values for history menu access. 297 pnotify.pnotify_history = opts.history; 298 pnotify.pnotify_hide = opts.hide; 299 // Change the notice type. 300 if (opts.type != old_opts.type) 301 pnotify.container.removeClass(styles.error+" "+styles.notice+" "+styles.success+" "+styles.info).addClass(opts.type == "error" ? styles.error : (opts.type == "info" ? styles.info : (opts.type == "success" ? styles.success : styles.notice))); 302 if (opts.icon !== old_opts.icon || (opts.icon === true && opts.type != old_opts.type)) { 303 // Remove any old icon. 304 pnotify.container.find("div.ui-pnotify-icon").remove(); 305 if (opts.icon !== false) { 306 // Build the new icon. 307 $("<div />", {"class": "ui-pnotify-icon"}) 308 .append($("<span />", {"class": opts.icon === true ? (opts.type == "error" ? styles.error_icon : (opts.type == "info" ? styles.info_icon : (opts.type == "success" ? styles.success_icon : styles.notice_icon))) : opts.icon})) 309 .prependTo(pnotify.container); 310 } 311 } 312 // Update the width. 313 if (opts.width !== old_opts.width) 314 pnotify.animate({width: opts.width}); 315 // Update the minimum height. 316 if (opts.min_height !== old_opts.min_height) 317 pnotify.container.animate({minHeight: opts.min_height}); 318 // Update the opacity. 319 if (opts.opacity !== old_opts.opacity) 320 pnotify.fadeTo(opts.animate_speed, opts.opacity); 321 // Update the sticker and closer buttons. 322 if (!opts.closer || opts.nonblock) 323 pnotify.closer.css("display", "none"); 324 else 325 pnotify.closer.css("display", "block"); 326 if (!opts.sticker || opts.nonblock) 327 pnotify.sticker.css("display", "none"); 328 else 329 pnotify.sticker.css("display", "block"); 330 // Update the sticker icon. 331 pnotify.sticker.trigger("pnotify_icon"); 332 // Update the hover status of the buttons. 333 if (opts.sticker_hover) 334 pnotify.sticker.css("visibility", "hidden"); 335 else if (!opts.nonblock) 336 pnotify.sticker.css("visibility", "visible"); 337 if (opts.closer_hover) 338 pnotify.closer.css("visibility", "hidden"); 339 else if (!opts.nonblock) 340 pnotify.closer.css("visibility", "visible"); 341 // Update the timed hiding. 342 if (!opts.hide) 343 pnotify.pnotify_cancel_remove(); 344 else if (!old_opts.hide) 345 pnotify.pnotify_queue_remove(); 346 pnotify.pnotify_queue_position(); 347 return pnotify; 348 }; 349 350 // Position the notice. dont_skip_hidden causes the notice to 351 // position even if it's not visible. 352 pnotify.pnotify_position = function(dont_skip_hidden){ 353 // Get the notice's stack. 354 var s = pnotify.opts.stack; 355 if (!s) return; 356 if (!s.nextpos1) 357 s.nextpos1 = s.firstpos1; 358 if (!s.nextpos2) 359 s.nextpos2 = s.firstpos2; 360 if (!s.addpos2) 361 s.addpos2 = 0; 362 var hidden = pnotify.css("display") == "none"; 363 // Skip this notice if it's not shown. 364 if (!hidden || dont_skip_hidden) { 365 var curpos1, curpos2; 366 // Store what will need to be animated. 367 var animate = {}; 368 // Calculate the current pos1 value. 369 var csspos1; 370 switch (s.dir1) { 371 case "down": 372 csspos1 = "top"; 373 break; 374 case "up": 375 csspos1 = "bottom"; 376 break; 377 case "left": 378 csspos1 = "right"; 379 break; 380 case "right": 381 csspos1 = "left"; 382 break; 383 } 384 curpos1 = parseInt(pnotify.css(csspos1)); 385 if (isNaN(curpos1)) 386 curpos1 = 0; 387 // Remember the first pos1, so the first visible notice goes there. 388 if (typeof s.firstpos1 == "undefined" && !hidden) { 389 s.firstpos1 = curpos1; 390 s.nextpos1 = s.firstpos1; 391 } 392 // Calculate the current pos2 value. 393 var csspos2; 394 switch (s.dir2) { 395 case "down": 396 csspos2 = "top"; 397 break; 398 case "up": 399 csspos2 = "bottom"; 400 break; 401 case "left": 402 csspos2 = "right"; 403 break; 404 case "right": 405 csspos2 = "left"; 406 break; 407 } 408 curpos2 = parseInt(pnotify.css(csspos2)); 409 if (isNaN(curpos2)) 410 curpos2 = 0; 411 // Remember the first pos2, so the first visible notice goes there. 412 if (typeof s.firstpos2 == "undefined" && !hidden) { 413 s.firstpos2 = curpos2; 414 s.nextpos2 = s.firstpos2; 415 } 416 // Check that it's not beyond the viewport edge. 417 if ((s.dir1 == "down" && s.nextpos1 + pnotify.height() > jwindow.height()) || 418 (s.dir1 == "up" && s.nextpos1 + pnotify.height() > jwindow.height()) || 419 (s.dir1 == "left" && s.nextpos1 + pnotify.width() > jwindow.width()) || 420 (s.dir1 == "right" && s.nextpos1 + pnotify.width() > jwindow.width()) ) { 421 // If it is, it needs to go back to the first pos1, and over on pos2. 422 s.nextpos1 = s.firstpos1; 423 s.nextpos2 += s.addpos2 + (typeof s.spacing2 == "undefined" ? 25 : s.spacing2); 424 s.addpos2 = 0; 425 } 426 // Animate if we're moving on dir2. 427 if (s.animation && s.nextpos2 < curpos2) { 428 switch (s.dir2) { 429 case "down": 430 animate.top = s.nextpos2+"px"; 431 break; 432 case "up": 433 animate.bottom = s.nextpos2+"px"; 434 break; 435 case "left": 436 animate.right = s.nextpos2+"px"; 437 break; 438 case "right": 439 animate.left = s.nextpos2+"px"; 440 break; 441 } 442 } else 443 pnotify.css(csspos2, s.nextpos2+"px"); 444 // Keep track of the widest/tallest notice in the column/row, so we can push the next column/row. 445 switch (s.dir2) { 446 case "down": 447 case "up": 448 if (pnotify.outerHeight(true) > s.addpos2) 449 s.addpos2 = pnotify.height(); 450 break; 451 case "left": 452 case "right": 453 if (pnotify.outerWidth(true) > s.addpos2) 454 s.addpos2 = pnotify.width(); 455 break; 456 } 457 // Move the notice on dir1. 458 if (s.nextpos1) { 459 // Animate if we're moving toward the first pos. 460 if (s.animation && (curpos1 > s.nextpos1 || animate.top || animate.bottom || animate.right || animate.left)) { 461 switch (s.dir1) { 462 case "down": 463 animate.top = s.nextpos1+"px"; 464 break; 465 case "up": 466 animate.bottom = s.nextpos1+"px"; 467 break; 468 case "left": 469 animate.right = s.nextpos1+"px"; 470 break; 471 case "right": 472 animate.left = s.nextpos1+"px"; 473 break; 474 } 475 } else 476 pnotify.css(csspos1, s.nextpos1+"px"); 477 } 478 // Run the animation. 479 if (animate.top || animate.bottom || animate.right || animate.left) 480 pnotify.animate(animate, {duration: 500, queue: false}); 481 // Calculate the next dir1 position. 482 switch (s.dir1) { 483 case "down": 484 case "up": 485 s.nextpos1 += pnotify.height() + (typeof s.spacing1 == "undefined" ? 25 : s.spacing1); 486 break; 487 case "left": 488 case "right": 489 s.nextpos1 += pnotify.width() + (typeof s.spacing1 == "undefined" ? 25 : s.spacing1); 490 break; 491 } 492 } 493 }; 494 495 // Queue the positiona all function so it doesn't run repeatedly and 496 // use up resources. 497 pnotify.pnotify_queue_position = function(milliseconds){ 498 if (timer) 499 clearTimeout(timer); 500 if (!milliseconds) 501 milliseconds = 10; 502 timer = setTimeout($.pnotify_position_all, milliseconds); 503 }; 504 505 // Display the notice. 506 pnotify.pnotify_display = function() { 507 // If the notice is not in the DOM, append it. 508 if (!pnotify.parent().length) 509 pnotify.appendTo(body); 510 // Run callback. 511 if (opts.before_open) { 512 if (opts.before_open(pnotify) === false) 513 return; 514 } 515 // Try to put it in the right position. 516 if (opts.stack.push != "top") 517 pnotify.pnotify_position(true); 518 // First show it, then set its opacity, then hide it. 519 if (opts.animation == "fade" || opts.animation.effect_in == "fade") { 520 // If it's fading in, it should start at 0. 521 pnotify.show().fadeTo(0, 0).hide(); 522 } else { 523 // Or else it should be set to the opacity. 524 if (opts.opacity != 1) 525 pnotify.show().fadeTo(0, opts.opacity).hide(); 526 } 527 pnotify.animate_in(function(){ 528 if (opts.after_open) 529 opts.after_open(pnotify); 530 531 pnotify.pnotify_queue_position(); 532 533 // Now set it to hide. 534 if (opts.hide) 535 pnotify.pnotify_queue_remove(); 536 }); 537 }; 538 539 // Remove the notice. 540 pnotify.pnotify_remove = function() { 541 if (pnotify.timer) { 542 window.clearTimeout(pnotify.timer); 543 pnotify.timer = null; 544 } 545 // Run callback. 546 if (opts.before_close) { 547 if (opts.before_close(pnotify) === false) 548 return; 549 } 550 pnotify.animate_out(function(){ 551 if (opts.after_close) { 552 if (opts.after_close(pnotify) === false) 553 return; 554 } 555 pnotify.pnotify_queue_position(); 556 // If we're supposed to remove the notice from the DOM, do it. 557 if (opts.remove) 558 pnotify.detach(); 559 }); 560 }; 561 562 // Animate the notice in. 563 pnotify.animate_in = function(callback){ 564 // Declare that the notice is animating in. (Or has completed animating in.) 565 animating = "in"; 566 var animation; 567 if (typeof opts.animation.effect_in != "undefined") 568 animation = opts.animation.effect_in; 569 else 570 animation = opts.animation; 571 if (animation == "none") { 572 pnotify.show(); 573 callback(); 574 } else if (animation == "show") 575 pnotify.show(opts.animate_speed, callback); 576 else if (animation == "fade") 577 pnotify.show().fadeTo(opts.animate_speed, opts.opacity, callback); 578 else if (animation == "slide") 579 pnotify.slideDown(opts.animate_speed, callback); 580 else if (typeof animation == "function") 581 animation("in", callback, pnotify); 582 else 583 pnotify.show(animation, (typeof opts.animation.options_in == "object" ? opts.animation.options_in : {}), opts.animate_speed, callback); 584 }; 585 586 // Animate the notice out. 587 pnotify.animate_out = function(callback){ 588 // Declare that the notice is animating out. (Or has completed animating out.) 589 animating = "out"; 590 var animation; 591 if (typeof opts.animation.effect_out != "undefined") 592 animation = opts.animation.effect_out; 593 else 594 animation = opts.animation; 595 if (animation == "none") { 596 pnotify.hide(); 597 callback(); 598 } else if (animation == "show") 599 pnotify.hide(opts.animate_speed, callback); 600 else if (animation == "fade") 601 pnotify.fadeOut(opts.animate_speed, callback); 602 else if (animation == "slide") 603 pnotify.slideUp(opts.animate_speed, callback); 604 else if (typeof animation == "function") 605 animation("out", callback, pnotify); 606 else 607 pnotify.hide(animation, (typeof opts.animation.options_out == "object" ? opts.animation.options_out : {}), opts.animate_speed, callback); 608 }; 609 610 // Cancel any pending removal timer. 611 pnotify.pnotify_cancel_remove = function() { 612 if (pnotify.timer) 613 window.clearTimeout(pnotify.timer); 614 }; 615 616 // Queue a removal timer. 617 pnotify.pnotify_queue_remove = function() { 618 // Cancel any current removal timer. 619 pnotify.pnotify_cancel_remove(); 620 pnotify.timer = window.setTimeout(function(){ 621 pnotify.pnotify_remove(); 622 }, (isNaN(opts.delay) ? 0 : opts.delay)); 623 }; 624 625 // Provide a button to close the notice. 626 pnotify.closer = $("<div />", { 627 "class": "ui-pnotify-closer", 628 "css": {"cursor": "pointer", "visibility": opts.closer_hover ? "hidden" : "visible"}, 629 "click": function(){ 630 pnotify.pnotify_remove(); 631 pnotify.sticker.css("visibility", "hidden"); 632 pnotify.closer.css("visibility", "hidden"); 633 } 634 }) 635 .append($("<span />", {"class": styles.closer})) 636 .appendTo(pnotify.container); 637 if (!opts.closer || opts.nonblock) 638 pnotify.closer.css("display", "none"); 639 640 // Provide a button to stick the notice. 641 pnotify.sticker = $("<div />", { 642 "class": "ui-pnotify-sticker", 643 "css": {"cursor": "pointer", "visibility": opts.sticker_hover ? "hidden" : "visible"}, 644 "click": function(){ 645 opts.hide = !opts.hide; 646 if (opts.hide) 647 pnotify.pnotify_queue_remove(); 648 else 649 pnotify.pnotify_cancel_remove(); 650 $(this).trigger("pnotify_icon"); 651 } 652 }) 653 .bind("pnotify_icon", function(){ 654 $(this).children().removeClass(styles.pin_up+" "+styles.pin_down).addClass(opts.hide ? styles.pin_up : styles.pin_down); 655 }) 656 .append($("<span />", {"class": styles.pin_up})) 657 .appendTo(pnotify.container); 658 if (!opts.sticker || opts.nonblock) 659 pnotify.sticker.css("display", "none"); 660 661 // Add the appropriate icon. 662 if (opts.icon !== false) { 663 $("<div />", {"class": "ui-pnotify-icon"}) 664 .append($("<span />", {"class": opts.icon === true ? (opts.type == "error" ? styles.error_icon : (opts.type == "info" ? styles.info_icon : (opts.type == "success" ? styles.success_icon : styles.notice_icon))) : opts.icon})) 665 .prependTo(pnotify.container); 666 } 667 668 // Add a title. 669 pnotify.title_container = $("<h4 />", { 670 "class": "ui-pnotify-title" 671 }) 672 .appendTo(pnotify.container); 673 if (opts.title === false) 674 pnotify.title_container.hide(); 675 else if (opts.title_escape) 676 pnotify.title_container.text(opts.title); 677 else 678 pnotify.title_container.html(opts.title); 679 680 // Add text. 681 pnotify.text_container = $("<div />", { 682 "class": "ui-pnotify-text" 683 }) 684 .appendTo(pnotify.container); 685 if (opts.text === false) 686 pnotify.text_container.hide(); 687 else if (opts.text_escape) 688 pnotify.text_container.text(opts.text); 689 else 690 pnotify.text_container.html(opts.insert_brs ? String(opts.text).replace(/\n/g, "<br />") : opts.text); 691 692 // Set width and min height. 693 if (typeof opts.width == "string") 694 pnotify.css("width", opts.width); 695 if (typeof opts.min_height == "string") 696 pnotify.container.css("min-height", opts.min_height); 697 698 // The history variable controls whether the notice gets redisplayed 699 // by the history pull down. 700 pnotify.pnotify_history = opts.history; 701 // The hide variable controls whether the history pull down should 702 // queue a removal timer. 703 pnotify.pnotify_hide = opts.hide; 704 705 // Add the notice to the notice array. 706 var notices_data = jwindow.data("pnotify"); 707 if (notices_data == null || typeof notices_data != "object") 708 notices_data = []; 709 if (opts.stack.push == "top") 710 notices_data = $.merge([pnotify], notices_data); 711 else 712 notices_data = $.merge(notices_data, [pnotify]); 713 jwindow.data("pnotify", notices_data); 714 // Now position all the notices if they are to push to the top. 715 if (opts.stack.push == "top") 716 pnotify.pnotify_queue_position(1); 717 718 // Run callback. 719 if (opts.after_init) 720 opts.after_init(pnotify); 721 722 if (opts.history) { 723 // If there isn't a history pull down, create one. 724 var history_menu = jwindow.data("pnotify_history"); 725 if (typeof history_menu == "undefined") { 726 history_menu = $("<div />", { 727 "class": "ui-pnotify-history-container "+styles.hi_menu, 728 "mouseleave": function(){ 729 history_menu.animate({top: "-"+history_handle_top+"px"}, {duration: 100, queue: false}); 730 } 731 }) 732 .append($("<div />", {"class": "ui-pnotify-history-header", "text": "Redisplay"})) 733 .append($("<button />", { 734 "class": "ui-pnotify-history-all "+styles.hi_btn, 735 "text": "All", 736 "mouseenter": function(){ 737 $(this).addClass(styles.hi_btnhov); 738 }, 739 "mouseleave": function(){ 740 $(this).removeClass(styles.hi_btnhov); 741 }, 742 "click": function(){ 743 // Display all notices. (Disregarding non-history notices.) 744 $.each(notices_data, function(){ 745 if (this.pnotify_history) { 746 if (this.is(":visible")) { 747 if (this.pnotify_hide) 748 this.pnotify_queue_remove(); 749 } else if (this.pnotify_display) 750 this.pnotify_display(); 751 } 752 }); 753 return false; 754 } 755 })) 756 .append($("<button />", { 757 "class": "ui-pnotify-history-last "+styles.hi_btn, 758 "text": "Last", 759 "mouseenter": function(){ 760 $(this).addClass(styles.hi_btnhov); 761 }, 762 "mouseleave": function(){ 763 $(this).removeClass(styles.hi_btnhov); 764 }, 765 "click": function(){ 766 // Look up the last history notice, and display it. 767 var i = -1; 768 var notice; 769 do { 770 if (i == -1) 771 notice = notices_data.slice(i); 772 else 773 notice = notices_data.slice(i, i+1); 774 if (!notice[0]) 775 break; 776 i--; 777 } while (!notice[0].pnotify_history || notice[0].is(":visible")); 778 if (!notice[0]) 779 return false; 780 if (notice[0].pnotify_display) 781 notice[0].pnotify_display(); 782 return false; 783 } 784 })) 785 .appendTo(body); 786 787 // Make a handle so the user can pull down the history tab. 788 var handle = $("<span />", { 789 "class": "ui-pnotify-history-pulldown "+styles.hi_hnd, 790 "mouseenter": function(){ 791 history_menu.animate({top: "0"}, {duration: 100, queue: false}); 792 } 793 }) 794 .appendTo(history_menu); 795 796 // Get the top of the handle. 797 history_handle_top = handle.offset().top + 2; 798 // Hide the history pull down up to the top of the handle. 799 history_menu.css({top: "-"+history_handle_top+"px"}); 800 // Save the history pull down. 801 jwindow.data("pnotify_history", history_menu); 802 } 803 } 804 805 // Mark the stack so it won't animate the new notice. 806 opts.stack.animation = false; 807 808 // Display the notice. 809 pnotify.pnotify_display(); 810 811 return pnotify; 812 } 813 }); 814 815 // Some useful regexes. 816 var re_on = /^on/, 817 re_mouse_events = /^(dbl)?click$|^mouse(move|down|up|over|out|enter|leave)$|^contextmenu$/, 818 re_ui_events = /^(focus|blur|select|change|reset)$|^key(press|down|up)$/, 819 re_html_events = /^(scroll|resize|(un)?load|abort|error)$/; 820 // Fire a DOM event. 821 var dom_event = function(e, orig_e){ 822 var event_object; 823 e = e.toLowerCase(); 824 if (document.createEvent && this.dispatchEvent) { 825 // FireFox, Opera, Safari, Chrome 826 e = e.replace(re_on, ''); 827 if (e.match(re_mouse_events)) { 828 // This allows the click event to fire on the notice. There is 829 // probably a much better way to do it. 830 $(this).offset(); 831 event_object = document.createEvent("MouseEvents"); 832 event_object.initMouseEvent( 833 e, orig_e.bubbles, orig_e.cancelable, orig_e.view, orig_e.detail, 834 orig_e.screenX, orig_e.screenY, orig_e.clientX, orig_e.clientY, 835 orig_e.ctrlKey, orig_e.altKey, orig_e.shiftKey, orig_e.metaKey, orig_e.button, orig_e.relatedTarget 836 ); 837 } else if (e.match(re_ui_events)) { 838 event_object = document.createEvent("UIEvents"); 839 event_object.initUIEvent(e, orig_e.bubbles, orig_e.cancelable, orig_e.view, orig_e.detail); 840 } else if (e.match(re_html_events)) { 841 event_object = document.createEvent("HTMLEvents"); 842 event_object.initEvent(e, orig_e.bubbles, orig_e.cancelable); 843 } 844 if (!event_object) return; 845 this.dispatchEvent(event_object); 846 } else { 847 // Internet Explorer 848 if (!e.match(re_on)) e = "on"+e; 849 event_object = document.createEventObject(orig_e); 850 this.fireEvent(e, event_object); 851 } 852 }; 853 854 $.pnotify.defaults = { 855 // The notice's title. 856 title: false, 857 // Whether to escape the content of the title. (Not allow HTML.) 858 title_escape: false, 859 // The notice's text. 860 text: false, 861 // Whether to escape the content of the text. (Not allow HTML.) 862 text_escape: false, 863 // What styling classes to use. (Can be either jqueryui or bootstrap.) 864 styling: "bootstrap", 865 // Additional classes to be added to the notice. (For custom styling.) 866 addclass: "", 867 // Class to be added to the notice for corner styling. 868 cornerclass: "", 869 // Create a non-blocking notice. It lets the user click elements underneath it. 870 nonblock: false, 871 // The opacity of the notice (if it's non-blocking) when the mouse is over it. 872 nonblock_opacity: .2, 873 // Display a pull down menu to redisplay previous notices, and place the notice in the history. 874 history: true, 875 // Width of the notice. 876 width: "300px", 877 // Minimum height of the notice. It will expand to fit content. 878 min_height: "16px", 879 // Type of the notice. "notice", "info", "success", or "error". 880 type: "notice", 881 // Set icon to true to use the default icon for the selected style/type, false for no icon, or a string for your own icon class. 882 icon: true, 883 // 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. 884 animation: "fade", 885 // Speed at which the notice animates in and out. "slow", "def" or "normal", "fast" or number of milliseconds. 886 animate_speed: "slow", 887 // Opacity of the notice. 888 opacity: 1, 889 // Display a drop shadow. 890 shadow: true, 891 // Provide a button for the user to manually close the notice. 892 closer: true, 893 // Only show the closer button on hover. 894 closer_hover: true, 895 // Provide a button for the user to manually stick the notice. 896 sticker: true, 897 // Only show the sticker button on hover. 898 sticker_hover: true, 899 // After a delay, remove the notice. 900 hide: true, 901 // Delay in milliseconds before the notice is removed. 902 delay: 8000, 903 // Reset the hide timer if the mouse moves over the notice. 904 mouse_reset: true, 905 // Remove the notice's elements from the DOM after it is removed. 906 remove: true, 907 // Change new lines to br tags. 908 insert_brs: true, 909 // The stack on which the notices will be placed. Also controls the direction the notices stack. 910 stack: {"dir1": "down", "dir2": "left", "push": "bottom", "spacing1": 25, "spacing2": 25} 911 }; 912})(jQuery);