/* * jQuery Pines Notify (pnotify) Plugin 1.2.0 * * http://pinesframework.org/pnotify/ * Copyright (c) 2009-2012 Hunter Perrin * * Triple license under the GPL, LGPL, and MPL: * http://www.gnu.org/licenses/gpl.html * http://www.gnu.org/licenses/lgpl.html * http://www.mozilla.org/MPL/MPL-1.1.html */ (function($) { var history_handle_top, timer, body, jwindow = $(window), styling = { jqueryui: { container: "ui-widget ui-widget-content ui-corner-all", notice: "ui-state-highlight", // (The actual jQUI notice icon looks terrible.) notice_icon: "ui-icon ui-icon-info", info: "", info_icon: "ui-icon ui-icon-info", success: "ui-state-default", success_icon: "ui-icon ui-icon-circle-check", error: "ui-state-error", error_icon: "ui-icon ui-icon-alert", closer: "ui-icon ui-icon-close", pin_up: "ui-icon ui-icon-pin-w", pin_down: "ui-icon ui-icon-pin-s", hi_menu: "ui-state-default ui-corner-bottom", hi_btn: "ui-state-default ui-corner-all", hi_btnhov: "ui-state-hover", hi_hnd: "ui-icon ui-icon-grip-dotted-horizontal" }, bootstrap: { container: "alert", notice: "", notice_icon: "icon-exclamation-sign", info: "alert-info", info_icon: "icon-info-sign", success: "alert-success", success_icon: "icon-ok-sign", error: "alert-error", error_icon: "icon-warning-sign", closer: "icon-remove", pin_up: "icon-pause", pin_down: "icon-play", hi_menu: "well", hi_btn: "btn", hi_btnhov: "", hi_hnd: "icon-chevron-down" } }; // Set global variables. var do_when_ready = function(){ body = $("body"); jwindow = $(window); // Reposition the notices when the window resizes. jwindow.bind('resize', function(){ if (timer) clearTimeout(timer); timer = setTimeout($.pnotify_position_all, 10); }); }; if (document.body) do_when_ready(); else $(do_when_ready); $.extend({ pnotify_remove_all: function () { var notices_data = jwindow.data("pnotify"); /* POA: Added null-check */ if (notices_data && notices_data.length) { $.each(notices_data, function(){ if (this.pnotify_remove) this.pnotify_remove(); }); } }, pnotify_position_all: function () { // This timer is used for queueing this function so it doesn't run // repeatedly. if (timer) clearTimeout(timer); timer = null; // Get all the notices. var notices_data = jwindow.data("pnotify"); if (!notices_data || !notices_data.length) return; // Reset the next position data. $.each(notices_data, function(){ var s = this.opts.stack; if (!s) return; s.nextpos1 = s.firstpos1; s.nextpos2 = s.firstpos2; s.addpos2 = 0; s.animation = true; }); $.each(notices_data, function(){ this.pnotify_position(); }); }, pnotify: function(options) { // Stores what is currently being animated (in or out). var animating; // Build main options. var opts; if (typeof options != "object") { opts = $.extend({}, $.pnotify.defaults); opts.text = options; } else { opts = $.extend({}, $.pnotify.defaults, options); } // Translate old pnotify_ style options. for (var i in opts) { if (typeof i == "string" && i.match(/^pnotify_/)) opts[i.replace(/^pnotify_/, "")] = opts[i]; } if (opts.before_init) { if (opts.before_init(opts) === false) return null; } // This keeps track of the last element the mouse was over, so // mouseleave, mouseenter, etc can be called. var nonblock_last_elem; // This is used to pass events through the notice if it is non-blocking. var nonblock_pass = function(e, e_name){ pnotify.css("display", "none"); var element_below = document.elementFromPoint(e.clientX, e.clientY); pnotify.css("display", "block"); var jelement_below = $(element_below); var cursor_style = jelement_below.css("cursor"); pnotify.css("cursor", cursor_style != "auto" ? cursor_style : "default"); // If the element changed, call mouseenter, mouseleave, etc. if (!nonblock_last_elem || nonblock_last_elem.get(0) != element_below) { if (nonblock_last_elem) { dom_event.call(nonblock_last_elem.get(0), "mouseleave", e.originalEvent); dom_event.call(nonblock_last_elem.get(0), "mouseout", e.originalEvent); } dom_event.call(element_below, "mouseenter", e.originalEvent); dom_event.call(element_below, "mouseover", e.originalEvent); } dom_event.call(element_below, e_name, e.originalEvent); // Remember the latest element the mouse was over. nonblock_last_elem = jelement_below; }; // Get our styling object. var styles = styling[opts.styling]; // Create our widget. // Stop animation, reset the removal timer, and show the close // button when the user mouses over. var pnotify = $("
", { "class": "ui-pnotify "+opts.addclass, "css": {"display": "none"}, "mouseenter": function(e){ if (opts.nonblock) e.stopPropagation(); if (opts.mouse_reset && animating == "out") { // If it's animating out, animate back in really quickly. pnotify.stop(true); animating = "in"; pnotify.css("height", "auto").animate({"width": opts.width, "opacity": opts.nonblock ? opts.nonblock_opacity : opts.opacity}, "fast"); } if (opts.nonblock) { // If it's non-blocking, animate to the other opacity. pnotify.animate({"opacity": opts.nonblock_opacity}, "fast"); } // Stop the close timer. if (opts.hide && opts.mouse_reset) pnotify.pnotify_cancel_remove(); // Show the buttons. if (opts.sticker && !opts.nonblock) pnotify.sticker.trigger("pnotify_icon").css("visibility", "visible"); if (opts.closer && !opts.nonblock) pnotify.closer.css("visibility", "visible"); }, "mouseleave": function(e){ if (opts.nonblock) e.stopPropagation(); nonblock_last_elem = null; pnotify.css("cursor", "auto"); // Animate back to the normal opacity. if (opts.nonblock && animating != "out") pnotify.animate({"opacity": opts.opacity}, "fast"); // Start the close timer. if (opts.hide && opts.mouse_reset) pnotify.pnotify_queue_remove(); // Hide the buttons. if (opts.sticker_hover) pnotify.sticker.css("visibility", "hidden"); if (opts.closer_hover) pnotify.closer.css("visibility", "hidden"); $.pnotify_position_all(); }, "mouseover": function(e){ if (opts.nonblock) e.stopPropagation(); }, "mouseout": function(e){ if (opts.nonblock) e.stopPropagation(); }, "mousemove": function(e){ if (opts.nonblock) { e.stopPropagation(); nonblock_pass(e, "onmousemove"); } }, "mousedown": function(e){ if (opts.nonblock) { e.stopPropagation(); e.preventDefault(); nonblock_pass(e, "onmousedown"); } }, "mouseup": function(e){ if (opts.nonblock) { e.stopPropagation(); e.preventDefault(); nonblock_pass(e, "onmouseup"); } }, "click": function(e){ if (opts.nonblock) { e.stopPropagation(); nonblock_pass(e, "onclick"); } }, "dblclick": function(e){ if (opts.nonblock) { e.stopPropagation(); nonblock_pass(e, "ondblclick"); } } }); pnotify.opts = opts; // Create a container for the notice contents. pnotify.container = $("
", {"class": styles.container+" ui-pnotify-container "+(opts.type == "error" ? styles.error : (opts.type == "info" ? styles.info : (opts.type == "success" ? styles.success : styles.notice)))}) .appendTo(pnotify); if (opts.cornerclass != "") pnotify.container.removeClass("ui-corner-all").addClass(opts.cornerclass); // Create a drop shadow. if (opts.shadow) pnotify.container.addClass("ui-pnotify-shadow"); // The current version of Pines Notify. pnotify.pnotify_version = "1.2.0"; // This function is for updating the notice. pnotify.pnotify = function(options) { // Update the notice. var old_opts = opts; if (typeof options == "string") opts.text = options; else opts = $.extend({}, opts, options); // Translate old pnotify_ style options. for (var i in opts) { if (typeof i == "string" && i.match(/^pnotify_/)) opts[i.replace(/^pnotify_/, "")] = opts[i]; } pnotify.opts = opts; // Update the corner class. if (opts.cornerclass != old_opts.cornerclass) pnotify.container.removeClass("ui-corner-all").addClass(opts.cornerclass); // Update the shadow. if (opts.shadow != old_opts.shadow) { if (opts.shadow) pnotify.container.addClass("ui-pnotify-shadow"); else pnotify.container.removeClass("ui-pnotify-shadow"); } // Update the additional classes. if (opts.addclass === false) pnotify.removeClass(old_opts.addclass); else if (opts.addclass !== old_opts.addclass) pnotify.removeClass(old_opts.addclass).addClass(opts.addclass); // Update the title. if (opts.title === false) pnotify.title_container.slideUp("fast"); else if (opts.title !== old_opts.title) { if (opts.title_escape) pnotify.title_container.text(opts.title).slideDown(200); else pnotify.title_container.html(opts.title).slideDown(200); } // Update the text. if (opts.text === false) { pnotify.text_container.slideUp("fast"); } else if (opts.text !== old_opts.text) { if (opts.text_escape) pnotify.text_container.text(opts.text).slideDown(200); else pnotify.text_container.html(opts.insert_brs ? String(opts.text).replace(/\n/g, "
") : opts.text).slideDown(200); } // Update values for history menu access. pnotify.pnotify_history = opts.history; pnotify.pnotify_hide = opts.hide; // Change the notice type. if (opts.type != old_opts.type) 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))); if (opts.icon !== old_opts.icon || (opts.icon === true && opts.type != old_opts.type)) { // Remove any old icon. pnotify.container.find("div.ui-pnotify-icon").remove(); if (opts.icon !== false) { // Build the new icon. $("
", {"class": "ui-pnotify-icon"}) .append($("", {"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})) .prependTo(pnotify.container); } } // Update the width. if (opts.width !== old_opts.width) pnotify.animate({width: opts.width}); // Update the minimum height. if (opts.min_height !== old_opts.min_height) pnotify.container.animate({minHeight: opts.min_height}); // Update the opacity. if (opts.opacity !== old_opts.opacity) pnotify.fadeTo(opts.animate_speed, opts.opacity); // Update the sticker and closer buttons. if (!opts.closer || opts.nonblock) pnotify.closer.css("display", "none"); else pnotify.closer.css("display", "block"); if (!opts.sticker || opts.nonblock) pnotify.sticker.css("display", "none"); else pnotify.sticker.css("display", "block"); // Update the sticker icon. pnotify.sticker.trigger("pnotify_icon"); // Update the hover status of the buttons. if (opts.sticker_hover) pnotify.sticker.css("visibility", "hidden"); else if (!opts.nonblock) pnotify.sticker.css("visibility", "visible"); if (opts.closer_hover) pnotify.closer.css("visibility", "hidden"); else if (!opts.nonblock) pnotify.closer.css("visibility", "visible"); // Update the timed hiding. if (!opts.hide) pnotify.pnotify_cancel_remove(); else if (!old_opts.hide) pnotify.pnotify_queue_remove(); pnotify.pnotify_queue_position(); return pnotify; }; // Position the notice. dont_skip_hidden causes the notice to // position even if it's not visible. pnotify.pnotify_position = function(dont_skip_hidden){ // Get the notice's stack. var s = pnotify.opts.stack; if (!s) return; if (!s.nextpos1) s.nextpos1 = s.firstpos1; if (!s.nextpos2) s.nextpos2 = s.firstpos2; if (!s.addpos2) s.addpos2 = 0; var hidden = pnotify.css("display") == "none"; // Skip this notice if it's not shown. if (!hidden || dont_skip_hidden) { var curpos1, curpos2; // Store what will need to be animated. var animate = {}; // Calculate the current pos1 value. var csspos1; switch (s.dir1) { case "down": csspos1 = "top"; break; case "up": csspos1 = "bottom"; break; case "left": csspos1 = "right"; break; case "right": csspos1 = "left"; break; } curpos1 = parseInt(pnotify.css(csspos1)); if (isNaN(curpos1)) curpos1 = 0; // Remember the first pos1, so the first visible notice goes there. if (typeof s.firstpos1 == "undefined" && !hidden) { s.firstpos1 = curpos1; s.nextpos1 = s.firstpos1; } // Calculate the current pos2 value. var csspos2; switch (s.dir2) { case "down": csspos2 = "top"; break; case "up": csspos2 = "bottom"; break; case "left": csspos2 = "right"; break; case "right": csspos2 = "left"; break; } curpos2 = parseInt(pnotify.css(csspos2)); if (isNaN(curpos2)) curpos2 = 0; // Remember the first pos2, so the first visible notice goes there. if (typeof s.firstpos2 == "undefined" && !hidden) { s.firstpos2 = curpos2; s.nextpos2 = s.firstpos2; } // Check that it's not beyond the viewport edge. if ((s.dir1 == "down" && s.nextpos1 + pnotify.height() > jwindow.height()) || (s.dir1 == "up" && s.nextpos1 + pnotify.height() > jwindow.height()) || (s.dir1 == "left" && s.nextpos1 + pnotify.width() > jwindow.width()) || (s.dir1 == "right" && s.nextpos1 + pnotify.width() > jwindow.width()) ) { // If it is, it needs to go back to the first pos1, and over on pos2. s.nextpos1 = s.firstpos1; s.nextpos2 += s.addpos2 + (typeof s.spacing2 == "undefined" ? 25 : s.spacing2); s.addpos2 = 0; } // Animate if we're moving on dir2. if (s.animation && s.nextpos2 < curpos2) { switch (s.dir2) { case "down": animate.top = s.nextpos2+"px"; break; case "up": animate.bottom = s.nextpos2+"px"; break; case "left": animate.right = s.nextpos2+"px"; break; case "right": animate.left = s.nextpos2+"px"; break; } } else pnotify.css(csspos2, s.nextpos2+"px"); // Keep track of the widest/tallest notice in the column/row, so we can push the next column/row. switch (s.dir2) { case "down": case "up": if (pnotify.outerHeight(true) > s.addpos2) s.addpos2 = pnotify.height(); break; case "left": case "right": if (pnotify.outerWidth(true) > s.addpos2) s.addpos2 = pnotify.width(); break; } // Move the notice on dir1. if (s.nextpos1) { // Animate if we're moving toward the first pos. if (s.animation && (curpos1 > s.nextpos1 || animate.top || animate.bottom || animate.right || animate.left)) { switch (s.dir1) { case "down": animate.top = s.nextpos1+"px"; break; case "up": animate.bottom = s.nextpos1+"px"; break; case "left": animate.right = s.nextpos1+"px"; break; case "right": animate.left = s.nextpos1+"px"; break; } } else pnotify.css(csspos1, s.nextpos1+"px"); } // Run the animation. if (animate.top || animate.bottom || animate.right || animate.left) pnotify.animate(animate, {duration: 500, queue: false}); // Calculate the next dir1 position. switch (s.dir1) { case "down": case "up": s.nextpos1 += pnotify.height() + (typeof s.spacing1 == "undefined" ? 25 : s.spacing1); break; case "left": case "right": s.nextpos1 += pnotify.width() + (typeof s.spacing1 == "undefined" ? 25 : s.spacing1); break; } } }; // Queue the positiona all function so it doesn't run repeatedly and // use up resources. pnotify.pnotify_queue_position = function(milliseconds){ if (timer) clearTimeout(timer); if (!milliseconds) milliseconds = 10; timer = setTimeout($.pnotify_position_all, milliseconds); }; // Display the notice. pnotify.pnotify_display = function() { // If the notice is not in the DOM, append it. if (!pnotify.parent().length) pnotify.appendTo(body); // Run callback. if (opts.before_open) { if (opts.before_open(pnotify) === false) return; } // Try to put it in the right position. if (opts.stack.push != "top") pnotify.pnotify_position(true); // First show it, then set its opacity, then hide it. if (opts.animation == "fade" || opts.animation.effect_in == "fade") { // If it's fading in, it should start at 0. pnotify.show().fadeTo(0, 0).hide(); } else { // Or else it should be set to the opacity. if (opts.opacity != 1) pnotify.show().fadeTo(0, opts.opacity).hide(); } pnotify.animate_in(function(){ if (opts.after_open) opts.after_open(pnotify); pnotify.pnotify_queue_position(); // Now set it to hide. if (opts.hide) pnotify.pnotify_queue_remove(); }); }; // Remove the notice. pnotify.pnotify_remove = function() { if (pnotify.timer) { window.clearTimeout(pnotify.timer); pnotify.timer = null; } // Run callback. if (opts.before_close) { if (opts.before_close(pnotify) === false) return; } pnotify.animate_out(function(){ if (opts.after_close) { if (opts.after_close(pnotify) === false) return; } pnotify.pnotify_queue_position(); // If we're supposed to remove the notice from the DOM, do it. if (opts.remove) pnotify.detach(); }); }; // Animate the notice in. pnotify.animate_in = function(callback){ // Declare that the notice is animating in. (Or has completed animating in.) animating = "in"; var animation; if (typeof opts.animation.effect_in != "undefined") animation = opts.animation.effect_in; else animation = opts.animation; if (animation == "none") { pnotify.show(); callback(); } else if (animation == "show") pnotify.show(opts.animate_speed, callback); else if (animation == "fade") pnotify.show().fadeTo(opts.animate_speed, opts.opacity, callback); else if (animation == "slide") pnotify.slideDown(opts.animate_speed, callback); else if (typeof animation == "function") animation("in", callback, pnotify); else pnotify.show(animation, (typeof opts.animation.options_in == "object" ? opts.animation.options_in : {}), opts.animate_speed, callback); }; // Animate the notice out. pnotify.animate_out = function(callback){ // Declare that the notice is animating out. (Or has completed animating out.) animating = "out"; var animation; if (typeof opts.animation.effect_out != "undefined") animation = opts.animation.effect_out; else animation = opts.animation; if (animation == "none") { pnotify.hide(); callback(); } else if (animation == "show") pnotify.hide(opts.animate_speed, callback); else if (animation == "fade") pnotify.fadeOut(opts.animate_speed, callback); else if (animation == "slide") pnotify.slideUp(opts.animate_speed, callback); else if (typeof animation == "function") animation("out", callback, pnotify); else pnotify.hide(animation, (typeof opts.animation.options_out == "object" ? opts.animation.options_out : {}), opts.animate_speed, callback); }; // Cancel any pending removal timer. pnotify.pnotify_cancel_remove = function() { if (pnotify.timer) window.clearTimeout(pnotify.timer); }; // Queue a removal timer. pnotify.pnotify_queue_remove = function() { // Cancel any current removal timer. pnotify.pnotify_cancel_remove(); pnotify.timer = window.setTimeout(function(){ pnotify.pnotify_remove(); }, (isNaN(opts.delay) ? 0 : opts.delay)); }; // Provide a button to close the notice. pnotify.closer = $("
", { "class": "ui-pnotify-closer", "css": {"cursor": "pointer", "visibility": opts.closer_hover ? "hidden" : "visible"}, "click": function(){ pnotify.pnotify_remove(); pnotify.sticker.css("visibility", "hidden"); pnotify.closer.css("visibility", "hidden"); } }) .append($("", {"class": styles.closer})) .appendTo(pnotify.container); if (!opts.closer || opts.nonblock) pnotify.closer.css("display", "none"); // Provide a button to stick the notice. pnotify.sticker = $("
", { "class": "ui-pnotify-sticker", "css": {"cursor": "pointer", "visibility": opts.sticker_hover ? "hidden" : "visible"}, "click": function(){ opts.hide = !opts.hide; if (opts.hide) pnotify.pnotify_queue_remove(); else pnotify.pnotify_cancel_remove(); $(this).trigger("pnotify_icon"); } }) .bind("pnotify_icon", function(){ $(this).children().removeClass(styles.pin_up+" "+styles.pin_down).addClass(opts.hide ? styles.pin_up : styles.pin_down); }) .append($("", {"class": styles.pin_up})) .appendTo(pnotify.container); if (!opts.sticker || opts.nonblock) pnotify.sticker.css("display", "none"); // Add the appropriate icon. if (opts.icon !== false) { $("
", {"class": "ui-pnotify-icon"}) .append($("", {"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})) .prependTo(pnotify.container); } // Add a title. pnotify.title_container = $("

", { "class": "ui-pnotify-title" }) .appendTo(pnotify.container); if (opts.title === false) pnotify.title_container.hide(); else if (opts.title_escape) pnotify.title_container.text(opts.title); else pnotify.title_container.html(opts.title); // Add text. pnotify.text_container = $("
", { "class": "ui-pnotify-text" }) .appendTo(pnotify.container); if (opts.text === false) pnotify.text_container.hide(); else if (opts.text_escape) pnotify.text_container.text(opts.text); else pnotify.text_container.html(opts.insert_brs ? String(opts.text).replace(/\n/g, "
") : opts.text); // Set width and min height. if (typeof opts.width == "string") pnotify.css("width", opts.width); if (typeof opts.min_height == "string") pnotify.container.css("min-height", opts.min_height); // The history variable controls whether the notice gets redisplayed // by the history pull down. pnotify.pnotify_history = opts.history; // The hide variable controls whether the history pull down should // queue a removal timer. pnotify.pnotify_hide = opts.hide; // Add the notice to the notice array. var notices_data = jwindow.data("pnotify"); if (notices_data == null || typeof notices_data != "object") notices_data = []; if (opts.stack.push == "top") notices_data = $.merge([pnotify], notices_data); else notices_data = $.merge(notices_data, [pnotify]); jwindow.data("pnotify", notices_data); // Now position all the notices if they are to push to the top. if (opts.stack.push == "top") pnotify.pnotify_queue_position(1); // Run callback. if (opts.after_init) opts.after_init(pnotify); if (opts.history) { // If there isn't a history pull down, create one. var history_menu = jwindow.data("pnotify_history"); if (typeof history_menu == "undefined") { history_menu = $("
", { "class": "ui-pnotify-history-container "+styles.hi_menu, "mouseleave": function(){ history_menu.animate({top: "-"+history_handle_top+"px"}, {duration: 100, queue: false}); } }) .append($("
", {"class": "ui-pnotify-history-header", "text": "Redisplay"})) .append($("