/* * jQuery Pines Notify (pnotify) Plugin 1.0.0 * * Copyright (c) 2009 Hunter Perrin * * Licensed (along with all of Pines) under the GNU Affero GPL: * http://www.gnu.org/licenses/agpl.html */(function($){varhistory_handle_top,timer;varbody;varjwindow;function$14(element,options){varattrFn={val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true};varo=jQuery(element);for(varkeyinoptions){if(jQuery.isFunction(options[key])){o.bind(key,options[key]);}elseif(keyinattrFn){o[key](options[key]);}else{o.attr(key,options[key]);}}returno;}$.extend({pnotify_remove_all:function(){varbody_data=body.data("pnotify");/* POA: Added null-check */if(body_data&&body_data.length){$.each(body_data,function(){if(this.pnotify_remove)this.pnotify_remove();});}},pnotify_position_all:function(){if(timer)clearTimeout(timer);timer=null;varbody_data=body.data("pnotify");if(!body_data||!body_data.length)return;$.each(body_data,function(){vars=this.opts.pnotify_stack;if(!s)return;if(!s.nextpos1)s.nextpos1=s.firstpos1;if(!s.nextpos2)s.nextpos2=s.firstpos2;if(!s.addpos2)s.addpos2=0;if(this.css("display")!="none"){varcurpos1,curpos2;varanimate={};// Calculate the current pos1 value.varcsspos1;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(this.css(csspos1));if(isNaN(curpos1))curpos1=0;// Remember the first pos1, so the first visible notice goes there.if(typeofs.firstpos1=="undefined"){s.firstpos1=curpos1;s.nextpos1=s.firstpos1;}// Calculate the current pos2 value.varcsspos2;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(this.css(csspos2));if(isNaN(curpos2))curpos2=0;// Remember the first pos2, so the first visible notice goes there.if(typeofs.firstpos2=="undefined"){s.firstpos2=curpos2;s.nextpos2=s.firstpos2;}// Check that it's not beyond the viewport edge.if((s.dir1=="down"&&s.nextpos1+this.height()>jwindow.height())||(s.dir1=="up"&&s.nextpos1+this.height()>jwindow.height())||(s.dir1=="left"&&s.nextpos1+this.width()>jwindow.width())||(s.dir1=="right"&&s.nextpos1+this.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+10;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;}}elsethis.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(this.outerHeight(true)>s.addpos2)s.addpos2=this.height();break;case"left":case"right":if(this.outerWidth(true)>s.addpos2)s.addpos2=this.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;}}elsethis.css(csspos1,s.nextpos1+"px");}if(animate.top||animate.bottom||animate.right||animate.left)this.animate(animate,{duration:500,queue:false});// Calculate the next dir1 position.switch(s.dir1){case"down":case"up":s.nextpos1+=this.height()+10;break;case"left":case"right":s.nextpos1+=this.width()+10;break;}}});// Reset the next position data.$.each(body_data,function(){vars=this.opts.pnotify_stack;if(!s)return;s.nextpos1=s.firstpos1;s.nextpos2=s.firstpos2;s.addpos2=0;s.animation=true;});},pnotify:function(options){if(!body)body=$("body");if(!jwindow)jwindow=$(window);varanimating;// Build main options.varopts;if(typeofoptions!="object"){opts=$.extend({},$.pnotify.defaults);opts.pnotify_text=options;}else{opts=$.extend({},$.pnotify.defaults,options);if(opts['pnotify_animation']instanceofObject){opts['pnotify_animation']=$.extend({effect_in:$.pnotify.defaults.pnotify_animation,effect_out:$.pnotify.defaults.pnotify_animation},opts['pnotify_animation']);}}if(opts.pnotify_before_init){if(opts.pnotify_before_init(opts)===false)returnnull;}// This keeps track of the last element the mouse was over, so// mouseleave, mouseenter, etc can be called.varnonblock_last_elem;// This is used to pass events through the notice if it is non-blocking.varnonblock_pass=function(e,e_name){pnotify.css("display","none");varelement_below=document.elementFromPoint(e.clientX,e.clientY);pnotify.css("display","block");varjelement_below=$(element_below);varcursor_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;};// Create our widget.// Stop animation, reset the removal timer, and show the close// button when the user mouses over.varpnotify=$14("<div />",{"class":"rf-ny "+opts.pnotify_addclass,"css":{"display":"none"},"mouseenter":function(e){if(opts.pnotify_nonblock)e.stopPropagation();if(opts.pnotify_mouse_reset&&animating=="out"){// If it's animating out, animate back in really quick.pnotify.stop(true);pnotify.css("height","auto").animate({"width":opts.pnotify_width,"opacity":opts.pnotify_nonblock?opts.pnotify_nonblock_opacity:opts.pnotify_opacity},"fast");}elseif(opts.pnotify_nonblock&&animating!="out"){// If it's non-blocking, animate to the other opacity.pnotify.animate({"opacity":opts.pnotify_nonblock_opacity},"fast");}if(opts.pnotify_hide&&opts.pnotify_mouse_reset)pnotify.pnotify_cancel_remove();if(opts.pnotify_closer&&!opts.pnotify_nonblock)pnotify.closer.show();},"mouseleave":function(e){if(opts.pnotify_nonblock)e.stopPropagation();nonblock_last_elem=null;pnotify.css("cursor","auto");if(opts.pnotify_nonblock&&animating!="out")pnotify.animate({"opacity":opts.pnotify_opacity},"fast");if(opts.pnotify_hide&&opts.pnotify_mouse_reset)pnotify.pnotify_queue_remove();pnotify.closer.hide();$.pnotify_position_all();},"mouseover":function(e){if(opts.pnotify_nonblock)e.stopPropagation();},"mouseout":function(e){if(opts.pnotify_nonblock)e.stopPropagation();},"mousemove":function(e){if(opts.pnotify_nonblock){e.stopPropagation();nonblock_pass(e,"onmousemove");}},"mousedown":function(e){if(opts.pnotify_nonblock){e.stopPropagation();e.preventDefault();nonblock_pass(e,"onmousedown");}},"mouseup":function(e){if(opts.pnotify_nonblock){e.stopPropagation();e.preventDefault();nonblock_pass(e,"onmouseup");}},"click":function(e){if(opts.pnotify_nonblock){e.stopPropagation();nonblock_pass(e,"onclick");}},"dblclick":function(e){if(opts.pnotify_nonblock){e.stopPropagation();nonblock_pass(e,"ondblclick");}}});pnotify.opts=opts;// Create a drop shadow.if(opts.pnotify_shadow&&!$.browser.msie)pnotify.shadow_container=$14("<div />",{"class":"rf-ny-sh"}).prependTo(pnotify);// Create a container for the notice contents.pnotify.container=$14("<div />",{"class":"rf-ny-co"}).appendTo(pnotify);pnotify.pnotify_version="1.0.0";// This function is for updating the notice.pnotify.pnotify=function(options){// Update the notice.varold_opts=opts;if(typeofoptions=="string")opts.pnotify_text=options;elseopts=$.extend({},opts,options);pnotify.opts=opts;// Update the shadow.if(opts.pnotify_shadow!=old_opts.pnotify_shadow){if(opts.pnotify_shadow&&!$.browser.msie)pnotify.shadow_container=$14("<div />",{"class":"rf-ny-sh"}).prependTo(pnotify);elsepnotify.children(".rf-ny-sh").remove();}// Update the additional classes.if(opts.pnotify_addclass===false)pnotify.removeClass(old_opts.pnotify_addclass);elseif(opts.pnotify_addclass!==old_opts.pnotify_addclass)pnotify.removeClass(old_opts.pnotify_addclass).addClass(opts.pnotify_addclass);// Update the title.if(opts.pnotify_title===false)pnotify.title_container.hide("fast");elseif(opts.pnotify_title!==old_opts.pnotify_title)pnotify.title_container.html(opts.pnotify_title).show(200);// Update the text.if(opts.pnotify_text===false){pnotify.text_container.hide("fast");}elseif(opts.pnotify_text!==old_opts.pnotify_text){if(opts.pnotify_insert_brs)opts.pnotify_text=opts.pnotify_text.replace(/\n/g,"<br />");pnotify.text_container.html(opts.pnotify_text).show(200);}pnotify.pnotify_history=opts.pnotify_history;// Change the notice type.if(opts.pnotify_type!=old_opts.pnotify_type)pnotify.container.toggleClass("rf-ny-co rf-ny-co-hover");if((opts.pnotify_notice_icon!=old_opts.pnotify_notice_icon&&opts.pnotify_type=="notice")||(opts.pnotify_error_icon!=old_opts.pnotify_error_icon&&opts.pnotify_type=="error")||(opts.pnotify_type!=old_opts.pnotify_type)){// Remove any old icon.pnotify.container.find("div.rf-ny-ic").remove();// if ((opts.pnotify_error_icon && opts.pnotify_type == "error") || (opts.pnotify_notice_icon)) {// Build the new icon.$14("<div />",{"class":"rf-ny-ic"}).append($14("<span />",{"class":opts.pnotify_type=="error"?opts.pnotify_error_icon:opts.pnotify_notice_icon})).prependTo(pnotify.container);// }}// Update the width.if(opts.pnotify_width!==old_opts.pnotify_width)pnotify.animate({width:opts.pnotify_width});// Update the minimum height.if(opts.pnotify_min_height!==old_opts.pnotify_min_height)pnotify.container.animate({minHeight:opts.pnotify_min_height});// Update the opacity.if(opts.pnotify_opacity!==old_opts.pnotify_opacity)pnotify.fadeTo(opts.pnotify_animate_speed,opts.pnotify_opacity);if(!opts.pnotify_hide)pnotify.pnotify_cancel_remove();elseif(!old_opts.pnotify_hide)pnotify.pnotify_queue_remove();pnotify.pnotify_queue_position();returnpnotify;};// Queue the position function so it doesn't run repeatedly and use// up resources.pnotify.pnotify_queue_position=function(){if(timer)clearTimeout(timer);timer=setTimeout($.pnotify_position_all,10);};// 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.pnotify_before_open){if(opts.pnotify_before_open(pnotify)===false)return;}pnotify.pnotify_queue_position();// First show it, then set its opacity, then hide it.if(opts.pnotify_animation=="fade"||opts.pnotify_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.pnotify_opacity!=1)pnotify.show().fadeTo(0,opts.pnotify_opacity).hide();}pnotify.animate_in(function(){if(opts.pnotify_after_open)opts.pnotify_after_open(pnotify);pnotify.pnotify_queue_position();// Now set it to hide.if(opts.pnotify_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.pnotify_before_close){if(opts.pnotify_before_close(pnotify)===false)return;}pnotify.animate_out(function(){if(opts.pnotify_after_close){if(opts.pnotify_after_close(pnotify)===false)return;}pnotify.pnotify_queue_position();// If we're supposed to remove the notice from the DOM, do it.if(opts.pnotify_remove){if(opts.pnotify_history){if(pnotify[0].parentNode!=null){pnotify[0].parentNode.removeChild(pnotify[0]);}}else{pnotify.remove();}}});};// Animate the notice in.pnotify.animate_in=function(callback){// Declare that the notice is animating in. (Or has completed animating in.)animating="in";varanimation;if(typeofopts.pnotify_animation.effect_in!="undefined")animation=opts.pnotify_animation.effect_in;elseanimation=opts.pnotify_animation;if(animation=="none"){pnotify.show();callback();}elseif(animation=="show")pnotify.show(opts.pnotify_animate_speed,callback);elseif(animation=="fade")pnotify.show().fadeTo(opts.pnotify_animate_speed,opts.pnotify_opacity,callback);elseif(animation=="slide")pnotify.slideDown(opts.pnotify_animate_speed,callback);elseif(typeofanimation=="function")animation("in",callback,pnotify);elseif(pnotify.effect)pnotify.effect(animation,{},opts.pnotify_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";varanimation;if(typeofopts.pnotify_animation.effect_out!="undefined")animation=opts.pnotify_animation.effect_out;elseanimation=opts.pnotify_animation;if(animation=="none"){pnotify.hide();callback();}elseif(animation=="show")pnotify.hide(opts.pnotify_animate_speed,callback);elseif(animation=="fade")pnotify.fadeOut(opts.pnotify_animate_speed,callback);elseif(animation=="slide")pnotify.slideUp(opts.pnotify_animate_speed,callback);elseif(typeofanimation=="function")animation("out",callback,pnotify);elseif(pnotify.effect)pnotify.effect(animation,{},opts.pnotify_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.pnotify_delay)?0:opts.pnotify_delay));};// Provide a button to close the notice.pnotify.closer=$14("<div />",{"class":"rf-ny-cl","css":{"cursor":"pointer","display":"none"},"click":function(){pnotify.pnotify_remove();pnotify.closer.hide();}}).append($14("<span />",{"class":"rf-ny-cl-ic"})).appendTo(pnotify.container);// Add the appropriate icon.// if ((opts.pnotify_error_icon && opts.pnotify_type == "error") || (opts.pnotify_notice_icon)) {$14("<div />",{"class":"rf-ny-ic"}).append($14("<span />",{"class":opts.pnotify_type=="error"?opts.pnotify_error_icon:opts.pnotify_notice_icon})).appendTo(pnotify.container);// }// Add a title.pnotify.title_container=$14("<div />",{"class":"rf-ny-tl","html":opts.pnotify_title}).appendTo(pnotify.container);if(opts.pnotify_title===false)pnotify.title_container.hide();// Replace new lines with HTML line breaks.if(opts.pnotify_insert_brs&&typeofopts.pnotify_text=="string")opts.pnotify_text=opts.pnotify_text.replace(/\n/g,"<br />");// Add text.pnotify.text_container=$14("<div />",{"class":"rf-ny-te","html":opts.pnotify_text}).appendTo(pnotify.container);if(opts.pnotify_text===false)pnotify.text_container.hide();// Set width and min height.if(typeofopts.pnotify_width=="string")pnotify.css("width",opts.pnotify_width);if(typeofopts.pnotify_min_height=="string")pnotify.container.css("min-height",opts.pnotify_min_height);// The history variable controls whether the notice gets redisplayed// by the history pull down.pnotify.pnotify_history=opts.pnotify_history;// Add the notice to the notice array.varbody_data=body.data("pnotify");if(body_data==null||typeofbody_data!="object")body_data=[];if(opts.pnotify_stack.push=="top")body_data=$.merge([pnotify],body_data);elsebody_data=$.merge(body_data,[pnotify]);body.data("pnotify",body_data);// Run callback.if(opts.pnotify_after_init)opts.pnotify_after_init(pnotify);if(opts.pnotify_history){// If there isn't a history pull down, create one.varbody_history=body.data("pnotify_history");if(typeofbody_history=="undefined"){body_history=$14("<div />",{"class":"rf-ny-hc","mouseleave":function(){body_history.animate({top:"-"+history_handle_top+"px"},{duration:100,queue:false});}}).append($14("<div />",{"class":"rf-ny-hh","text":"Redisplay"})).append($14("<button />",{"class":"rf-ny-ha","text":"All",// "mouseenter": function(){// $(this).addClass("ui-state-hover");// },// "mouseleave": function(){// $(this).removeClass("ui-state-hover");// },"click":function(){// Display all notices. (Disregarding non-history notices.)$.each(body.data("pnotify"),function(){if(this.pnotify_history&&this.pnotify_display)this.pnotify_display();});returnfalse;}})).append($14("<button />",{"class":"rf-ny-hl","text":"Last",// "mouseenter": function(){// $(this).addClass("ui-state-hover");// },// "mouseleave": function(){// $(this).removeClass("ui-state-hover");// },"click":function(){// Look up the last history notice, and display it.vari=1;varbody_data=body.data("pnotify");while(!body_data[body_data.length-i]||!body_data[body_data.length-i].pnotify_history||body_data[body_data.length-i].is(":visible")){if(body_data.length-i===0)returnfalse;i++;}varn=body_data[body_data.length-i];if(n.pnotify_display)n.pnotify_display();returnfalse;}})).appendTo(body);// Make a handle so the user can pull down the history pull down.varhandle=$14("<span />",{"class":"rf-ny-hp","mouseenter":function(){body_history.animate({top:"0"},{duration:100,queue:false});}}).appendTo(body_history);// Get the top of the handle.history_handle_top=handle.offset().top+2;// Hide the history pull down up to the top of the handle.body_history.css({top:"-"+history_handle_top+"px"});// Save the history pull down.body.data("pnotify_history",body_history);}}// Mark the stack so it won't animate the new notice.opts.pnotify_stack.animation=false;// Display the notice.pnotify.pnotify_display();returnpnotify;}});// Some useful regexes.varre_on=/^on/;varre_mouse_events=/^(dbl)?click$|^mouse(move|down|up|over|out|enter|leave)$|^contextmenu$/;varre_ui_events=/^(focus|blur|select|change|reset)$|^key(press|down|up)$/;varre_html_events=/^(scroll|resize|(un)?load|abort|error)$/;// Fire a DOM event.vardom_event=function(e,orig_e){varevent_object;e=e.toLowerCase();if(document.createEvent&&this.dispatchEvent){// FireFox, Opera, Safari, Chromee=e.replace(re_on,'');if(e.match(re_mouse_events)){// This allows the click event to fire on the notice. There is// probably a much better way to do it.$(this).offset();event_object=document.createEvent("MouseEvents");event_object.initMouseEvent(e,orig_e.bubbles,orig_e.cancelable,orig_e.view,orig_e.detail,orig_e.screenX,orig_e.screenY,orig_e.clientX,orig_e.clientY,orig_e.ctrlKey,orig_e.altKey,orig_e.shiftKey,orig_e.metaKey,orig_e.button,orig_e.relatedTarget);}elseif(e.match(re_ui_events)){event_object=document.createEvent("UIEvents");event_object.initUIEvent(e,orig_e.bubbles,orig_e.cancelable,orig_e.view,orig_e.detail);}elseif(e.match(re_html_events)){event_object=document.createEvent("HTMLEvents");event_object.initEvent(e,orig_e.bubbles,orig_e.cancelable);}if(!event_object)return;this.dispatchEvent(event_object);}else{// Internet Explorerif(!e.match(re_on))e="on"+e;event_object=document.createEventObject(orig_e);this.fireEvent(e,event_object);}};$.pnotify.defaults={// The notice's title.pnotify_title:false,// The notice's text.pnotify_text:false,// Additional classes to be added to the notice. (For custom styling.)pnotify_addclass:"",// Create a non-blocking notice. It lets the user click elements underneath it.pnotify_nonblock:false,// The opacity of the notice (if it's non-blocking) when the mouse is over it.pnotify_nonblock_opacity:.2,// Display a pull down menu to redisplay previous notices, and place the notice in the history.pnotify_history:true,// Width of the notice.pnotify_width:"300px",// Minimum height of the notice. It will expand to fit content.pnotify_min_height:"16px",// Type of the notice. "notice" or "error".pnotify_type:"notice",// The icon class to use if type is notice.pnotify_notice_icon:"",// The icon class to use if type is error.pnotify_error_icon:"",// 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.pnotify_animation:"fade",// Speed at which the notice animates in and out. "slow", "def" or "normal", "fast" or number of milliseconds.pnotify_animate_speed:"slow",// Opacity of the notice.pnotify_opacity:1,// Display a drop shadow.pnotify_shadow:false,// Provide a button for the user to manually close the notice.pnotify_closer:true,// After a delay, remove the notice.pnotify_hide:true,// Delay in milliseconds before the notice is removed.pnotify_delay:8000,// Reset the hide timer if the mouse moves over the notice.pnotify_mouse_reset:true,// Remove the notice's elements from the DOM after it is removed.pnotify_remove:true,// Change new lines to br tags.pnotify_insert_brs:true,// The stack on which the notices will be placed. Also controls the direction the notices stack.pnotify_stack:{"dir1":"down","dir2":"left","push":"bottom"}};})(jQuery);