You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2013/02/26 00:28:31 UTC

[3/24] git commit: ISIS-349, ISIS-350: jgrowl integration, error page for exceptions

ISIS-349, ISIS-350: jgrowl integration, error page for exceptions

implemented.


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/345f22fb
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/345f22fb
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/345f22fb

Branch: refs/heads/dan/ISIS-233-ro
Commit: 345f22fbfb56de82b41721fbb49d4fc0fcdc7f23
Parents: 4ad06ff
Author: Dan Haywood <da...@apache.org>
Authored: Thu Feb 21 19:30:10 2013 +0000
Committer: Dan Haywood <da...@apache.org>
Committed: Thu Feb 21 19:30:10 2013 +0000

----------------------------------------------------------------------
 .../integration/wicket/WebRequestCycleForIsis.java |   38 +-
 .../viewer/wicket/model/models/ActionModel.java    |   29 ++-
 .../ui/components/widgets/cssmenu/CssMenuItem.java |    7 +-
 .../viewer/wicket/ui/feedback/JGrowlBehavior.java  |  101 ++++
 .../isis/viewer/wicket/ui/pages/PageAbstract.css   |   37 ++
 .../isis/viewer/wicket/ui/pages/PageAbstract.html  |   20 +-
 .../isis/viewer/wicket/ui/pages/PageAbstract.java  |   38 ++-
 .../viewer/wicket/ui/pages/action/ActionPage.java  |    1 +
 .../viewer/wicket/ui/pages/error/ErrorPage.css     |   81 ++++
 .../viewer/wicket/ui/pages/error/ErrorPage.html    |   60 +++
 .../viewer/wicket/ui/pages/error/ErrorPage.java    |   71 +++
 .../isis/viewer/wicket/ui/pages/jquery.jgrowl.css  |  136 ++++++
 .../isis/viewer/wicket/ui/pages/jquery.jgrowl.js   |  352 +++++++++++++++
 13 files changed, 934 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/345f22fb/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java
index a135449..debf3ff 100644
--- a/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java
+++ b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/integration/wicket/WebRequestCycleForIsis.java
@@ -19,16 +19,19 @@
 
 package org.apache.isis.viewer.wicket.viewer.integration.wicket;
 
-import org.apache.log4j.Logger;
-import org.apache.wicket.protocol.http.WebSession;
-import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
-import org.apache.wicket.request.cycle.RequestCycle;
-
 import org.apache.isis.core.commons.authentication.AuthenticationSession;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 import org.apache.isis.core.runtime.system.session.IsisSession;
 import org.apache.isis.core.runtime.system.transaction.IsisTransaction;
 import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager;
+import org.apache.isis.viewer.wicket.ui.pages.error.ErrorPage;
+import org.apache.log4j.Logger;
+import org.apache.wicket.core.request.handler.PageProvider;
+import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
+import org.apache.wicket.protocol.http.WebSession;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
+import org.apache.wicket.request.cycle.RequestCycle;
 
 /**
  * Isis-specific implementation of the Wicket's {@link WebRequestCycle},
@@ -39,18 +42,6 @@ public class WebRequestCycleForIsis /*extends WebRequestCycle*/ extends Abstract
 
     private static final Logger LOG = Logger.getLogger(WebRequestCycleForIsis.class);
 
-//    public WebRequestCycleForIsis(final WebApplication application, final WebRequest request, final Response response) {
-//        super(application, request, response);
-//    }
-//
-//    /**
-//     * Convenience, downcasts.
-//     */
-//    @Override
-//    public AuthenticatedWebSessionForIsis getWebSession() {
-//        return (AuthenticatedWebSessionForIsis) super.getWebSession();
-//    }
-
       private AuthenticatedWebSessionForIsis getWebSession() {
           return (AuthenticatedWebSessionForIsis) WebSession.get();
       }
@@ -82,7 +73,6 @@ public class WebRequestCycleForIsis /*extends WebRequestCycle*/ extends Abstract
             commitTransactionIfAny();
             getIsisContext().closeSessionInstance();
         }
-        //super.onEndRequest();
     }
 
     private void commitTransactionIfAny() {
@@ -96,6 +86,11 @@ public class WebRequestCycleForIsis /*extends WebRequestCycle*/ extends Abstract
         }
     }
 
+    @Override
+    public IRequestHandler onException(RequestCycle cycle, Exception ex) {
+        return new RenderPageRequestHandler(new PageProvider(new ErrorPage(ex)));
+    }
+    
     /**
      * Factored out so can be overridden in testing.
      */
@@ -103,13 +98,6 @@ public class WebRequestCycleForIsis /*extends WebRequestCycle*/ extends Abstract
         return IsisContext.getInstance();
     }
 
-//    /**
-//     * Simply downcasts superclass' implementation, for convenience of callers.
-//     */
-//    @Override
-//    protected WebClientInfo newClientInfo() {
-//        return (WebClientInfo) super.newClientInfo();
-//    }
 
     protected IsisTransactionManager getTransactionManager() {
         return IsisContext.getTransactionManager();

http://git-wip-us.apache.org/repos/asf/isis/blob/345f22fb/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java b/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
index 0fbe844..aed3834 100644
--- a/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
+++ b/component/viewer/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/ActionModel.java
@@ -19,18 +19,24 @@
 
 package org.apache.isis.viewer.wicket.model.models;
 
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import com.google.common.base.Predicate;
+import com.google.common.base.Throwables;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
 
 import org.apache.wicket.Component;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 
+import org.apache.isis.applib.ApplicationException;
 import org.apache.isis.applib.Identifier;
+import org.apache.isis.core.commons.exceptions.IsisApplicationException;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager.ConcurrencyChecking;
 import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
@@ -433,12 +439,31 @@ public class ActionModel extends BookmarkableModel<ObjectAdapter> {
         return results;
     }
 
+    
+    // TODO: hacky!!
+    public static ThreadLocal<String> applicationError = new ThreadLocal<String>();
+    
     private ObjectAdapter executeAction() {
         final ObjectAdapter targetAdapter = getTargetAdapter();
         final ObjectAdapter[] arguments = getArgumentsAsArray();
         final ObjectAction action = getActionMemento().getAction();
-        final ObjectAdapter results = action.execute(targetAdapter, arguments);
-        return results;
+        try {
+            final ObjectAdapter results = action.execute(targetAdapter, arguments);
+            return results;
+        } catch(RuntimeException ex) {
+            final ApplicationException appEx = getApplicationExceptionIfAny(ex);
+            if(appEx != null) {
+                applicationError.set(appEx.getMessage());
+                return null;
+            }
+            throw ex;
+        }
+    }
+
+    private ApplicationException getApplicationExceptionIfAny(Exception ex) {
+        Iterable<ApplicationException> appEx = Iterables.filter(Throwables.getCausalChain(ex), ApplicationException.class);
+        Iterator<ApplicationException> iterator = appEx.iterator();
+        return iterator.hasNext() ? iterator.next() : null;
     }
 
     public String getReasonInvalidIfAny() {

http://git-wip-us.apache.org/repos/asf/isis/blob/345f22fb/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/CssMenuItem.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/CssMenuItem.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/CssMenuItem.java
index 541c5f9..3a1e3f0 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/CssMenuItem.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/cssmenu/CssMenuItem.java
@@ -48,6 +48,7 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.progmodel.facets.actions.bulk.BulkFacet;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.mementos.ObjectAdapterMemento;
+import org.apache.isis.viewer.wicket.ui.feedback.JGrowlBehavior;
 import org.apache.isis.viewer.wicket.ui.pages.PageAbstract;
 import org.apache.isis.viewer.wicket.ui.util.Components;
 import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
@@ -229,6 +230,8 @@ public class CssMenuItem implements Serializable {
 
         final LinkAndLabel linkAndLabel = cssMenuLinkFactory.newLink(null, objectAction, PageAbstract.ID_MENU_LINK);
 
+        linkAndLabel.getLink().add(new JGrowlBehavior());
+
         final AbstractLink link = linkAndLabel.getLink();
         final String actionLabel = linkAndLabel.getLabel();
 
@@ -262,8 +265,8 @@ public class CssMenuItem implements Serializable {
             // hide link...
             Components.permanentlyHide(markupContainer, ID_MENU_LINK);
             // ... and show label, along with disabled reason
-            label.add(new AttributeModifier("title", true, Model.of(this.getDisabledReason())));
-            label.add(new AttributeModifier("class", true, Model.of("disabled")));
+            label.add(new AttributeModifier("title", Model.of(this.getDisabledReason())));
+            label.add(new AttributeModifier("class", Model.of("disabled")));
             markupContainer.add(label);
 
             return label;

http://git-wip-us.apache.org/repos/asf/isis/blob/345f22fb/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/feedback/JGrowlBehavior.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/feedback/JGrowlBehavior.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/feedback/JGrowlBehavior.java
new file mode 100644
index 0000000..24fdb98
--- /dev/null
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/feedback/JGrowlBehavior.java
@@ -0,0 +1,101 @@
+package org.apache.isis.viewer.wicket.ui.feedback;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.viewer.wicket.model.models.ActionModel;
+import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.feedback.FeedbackMessage;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+
+/**
+ * Attach to any component to display jGrowl messages.
+ * 
+ * Displays only session-level messages. If you need component-level messages,
+ * see http://pastebin.com/f6db2ec0e for an example. Basically, instead of
+ * Session.get().getFeedbackMessages(), you would call
+ * getComponent().getFeedbackMessage().
+ * 
+ * Requires the following be included: "jquery.js", "jquery.ui.all.js",
+ * "jquery.jgrowl.js", "jquery.jgrowl.css". These can be downloaded from
+ * http://plugins.jquery.com/files/jGrowl-1.2.0.tgz.
+ * 
+ * @author jsinai Based on an example by Alex Objelean, see the above link.
+ */
+public class JGrowlBehavior extends AbstractDefaultAjaxBehavior {
+    
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Displays an info message that is sticky. The default is non-sticky.
+     * Sample usage: session.getFeedbackMessages().add(new FeedbackMessage(null,
+     * "my message", JGrowlBehavior.INFO_STICKY));
+     */
+    public static final int INFO_STICKY = 250;
+
+    @Override
+    protected void respond(AjaxRequestTarget target) {
+        final String feedbackMsg = renderFeedback();
+        if (!StringUtils.isEmpty(feedbackMsg)) {
+            target.appendJavaScript(feedbackMsg);
+        }
+    }
+
+    @Override
+    public void renderHead(Component component, IHeaderResponse response) {
+        super.renderHead(component, response);
+        final String feedbackMsg = renderFeedback();
+        if (!StringUtils.isEmpty(feedbackMsg)) {
+            response.render(OnDomReadyHeaderItem.forScript(feedbackMsg));
+        }
+    }
+
+    private String renderFeedback() {
+
+        final StringBuilder buf = new StringBuilder();
+        
+        for (String info : IsisContext.getMessageBroker().getMessages()) {
+            addJGrowlCall(info, "INFO", false, buf);
+        }
+
+        for (String warning : IsisContext.getMessageBroker().getWarnings()) {
+            addJGrowlCall(warning, "WARNING", true, buf);
+        }
+        
+        try {
+            final String error = ActionModel.applicationError.get();
+            if(error!=null) {
+                    addJGrowlCall(error, "ERROR", true, buf);
+            }
+        } finally {
+            ActionModel.applicationError.remove();
+        }
+
+        return buf.toString();
+    }
+
+    void addJGrowlCall(final String msg, final String cssClassSuffix, boolean sticky, final StringBuilder buf) {
+        buf.append("$.jGrowl(\"").append(msg).append('\"');
+        buf.append(", {");
+        buf.append("theme: \'jgrowl-").append(cssClassSuffix).append("\'");
+        if (sticky) {
+            buf.append(", sticky: true");
+        }
+        buf.append("}");
+        buf.append(");");
+    }
+
+    boolean isSticky(final FeedbackMessage message) {
+        return message.getLevel() > FeedbackMessage.INFO;
+    }
+
+    String messageFor(final FeedbackMessage message) {
+        return (message.getMessage() == null) ? StringUtils.EMPTY : message.getMessage().toString();
+    }
+
+    String levelFor(final FeedbackMessage message) {
+        return (message.getLevel() == INFO_STICKY) ? "INFO" : message.getLevelAsString();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/345f22fb/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.css
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.css b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.css
index e24076b..1cdadb5 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.css
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.css
@@ -689,3 +689,40 @@ div.actionPanelHeaderNew .actions {
 
 
 
+div#jGrowl {
+ margin-top: 55px;
+ margin-right: 25px;
+ color: white;
+ font-size: larger;
+ opacity: .90;
+ filter: alpha(opacity = 90);
+}
+
+div#jGrowl div.jgrowl-ERROR {
+ background-color: #BF0B0B;
+}
+div#jGrowl div.jgrowl-WARNING {
+ background-color: orange;
+}
+div#jGrowl div.jgrowl-INFO {
+ background-color: #20B5C2;
+}
+div#jGrowl div.jgrowl-WARNING {
+ background-color: orange;
+}
+div#jGrowl div.jGrowl-closer {
+ background-color: #F0EFEA;
+ color: #46423C;
+ font-size: small;
+}
+
+/*
+colors
+#413D37 - dark banner
+#46423C - dark (writing?)
+#E4E4DB - page background
+#20B5C2 - accent
+#BF0B0B - jgrowl error
+#00477F - dark blue
+#F0EFEA - bookmark background
+*/

http://git-wip-us.apache.org/repos/asf/isis/blob/345f22fb/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.html
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.html b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.html
index d3da481..f5bbc65 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.html
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.html
@@ -22,13 +22,14 @@
       xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd"  
       xml:lang="en"  
       lang="en">
-	<head>
+	<wicket:head>
+        <wicket:link>
+    		<link href="cssreset.css" rel="stylesheet" type="text/css"/>
+    		<link href="PageAbstract.css" rel="stylesheet" type="text/css"/>
+            <link href="jquery.jgrowl.css" rel="stylesheet" type="text/css"/>
+        </wicket:link>
 		<title wicket:id="pageTitle"></title>
-		<wicket:link>
-			<link href="cssreset.css" rel="stylesheet" type="text/css"/>
-			<link href="PageAbstract.css" rel="stylesheet" type="text/css"/>
-		</wicket:link>
-	</head>
+	</wicket:head>
 	<body>
 		
 		<div id="container" class="page">
@@ -60,7 +61,12 @@
 
 				<div class="clear"/>
 			</div>
-
+            
+            <form wicket:id="form">
+            <input type="submit" value="Normal OK"/>
+            <input type="submit" value="Ajax OK" wicket:id="ajaxbutton"/>
+            </form>
+            
 			<div id="footer">
 				<div class="links">
 					powered by: <a href="http://isis.apache.org">Apache Isis</a>

http://git-wip-us.apache.org/repos/asf/isis/blob/345f22fb/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
index d4d59da..5c283a3 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/PageAbstract.java
@@ -36,20 +36,26 @@ import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
 import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistry;
 import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistryAccessor;
+import org.apache.isis.viewer.wicket.ui.feedback.JGrowlBehavior;
 import org.apache.isis.viewer.wicket.ui.pages.about.AboutPage;
 import org.apache.isis.viewer.wicket.ui.pages.login.WicketSignInPage;
-
 import org.apache.log4j.Logger;
 import org.apache.wicket.RestartResponseAtInterceptPageException;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.feedback.FeedbackMessage;
 import org.apache.wicket.markup.head.CssReferenceHeaderItem;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.JavaScriptReferenceHeaderItem;
 import org.apache.wicket.markup.html.WebPage;
 import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.link.ExternalLink;
 import org.apache.wicket.markup.html.link.Link;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
 
 import com.google.inject.Inject;
 import com.google.inject.name.Named;
@@ -99,6 +105,7 @@ public abstract class PageAbstract extends WebPage {
     @Named("applicationJs")
     private String applicationJs;
 
+    
     public PageAbstract(final PageParameters pageParameters, final ComponentType... childComponentIds) {
         try {
             addApplicationActionsComponent();
@@ -106,6 +113,7 @@ public abstract class PageAbstract extends WebPage {
             this.pageParameters = pageParameters;
             addHomePageLinkAndApplicationName();
             addUserName();
+            addNotificationPanel();
             addLogoutLink();
             addAboutLink();
             add(new Label(ID_PAGE_TITLE, PageParameterNames.PAGE_TITLE.getStringFrom(pageParameters, applicationName)));
@@ -118,9 +126,12 @@ public abstract class PageAbstract extends WebPage {
         }
     }
 
+    private static final JavaScriptResourceReference JQUERY_JGROWL_JS = new JavaScriptResourceReference(PageAbstract.class, "jquery.jgrowl.js");
+    
     @Override
     public void renderHead(IHeaderResponse response) {
         super.renderHead(response);
+        response.render(JavaScriptReferenceHeaderItem.forReference(JQUERY_JGROWL_JS));
         if(applicationCss != null) {
             response.render(CssReferenceHeaderItem.forUrl(applicationCss));
         }
@@ -164,6 +175,31 @@ public abstract class PageAbstract extends WebPage {
         });
     }
 
+    private void addNotificationPanel() {
+        Form<?> form = new Form("form") {
+
+            @Override
+            protected void onSubmit() {
+                Session.get().error("Test error");
+                Session.get().warn("Test warning");
+                Session.get().info("Test info");
+                Session.get().getFeedbackMessages().add(new FeedbackMessage(null, "Test sticky info", JGrowlBehavior.INFO_STICKY));
+            }
+        };
+        add(form);
+
+        AjaxButton b = new AjaxButton("ajaxbutton", form) {
+
+            @Override
+            protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
+                target.add(form);
+            }
+        };
+        form.add(b);
+        form.add(new JGrowlBehavior());
+    
+    }
+
 
     /**
      * As provided in the {@link #PageAbstract(ComponentType) constructor}.

http://git-wip-us.apache.org/repos/asf/isis/blob/345f22fb/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/action/ActionPage.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/action/ActionPage.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/action/ActionPage.java
index 8aaddd0..bfbb8fe 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/action/ActionPage.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/action/ActionPage.java
@@ -27,6 +27,7 @@ import org.apache.isis.applib.annotation.ActionSemantics;
 import org.apache.isis.viewer.wicket.model.models.ActionModel;
 import org.apache.isis.viewer.wicket.model.models.ActionModel.Mode;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
+import org.apache.isis.viewer.wicket.ui.feedback.JGrowlBehavior;
 import org.apache.isis.viewer.wicket.ui.pages.PageAbstract;
 
 /**

http://git-wip-us.apache.org/repos/asf/isis/blob/345f22fb/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.css
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.css b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.css
new file mode 100644
index 0000000..1685bf1
--- /dev/null
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.css
@@ -0,0 +1,81 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+
+.errorPage {
+    margin-left: 50px;
+    margin-right: 50px;
+    margin-top: 50px;
+}
+
+.errorPage #message {
+    background:#FFFFFF;
+    border-radius:4px;
+    -moz-border-radius:4px;
+    -webkit-border-radius:4px;
+    padding: 15px;
+    display: block;
+    text-align:center;
+    font-size:1.2em;
+}
+
+ .errorPage .errorDetail {
+    margin-top: 30px; 
+}
+
+.errorPage .heading {
+    border-radius:4px;
+    -moz-border-radius:4px;
+    -webkit-border-radius:4px;
+    background-color:#F0EFEA;
+
+    display:block;
+    font-style:normal !important;
+
+    padding:1px 6px 1px 6px;
+}
+
+.errorPage .heading span {
+    display:block;
+    font-style:normal !important;
+    padding:3px 3px 3px 3px;
+    text-transform: uppercase;
+    font-size: 0.8em;
+    text-transform:uppercase;
+    font-weight:bold;
+}
+
+.errorPage .heading:hover {
+    background-color:#FFFFFF;
+    cursor: pointer;
+}
+
+.errorPage h3 {
+    font-size: larger;
+}
+
+.errorPage .exceptionMessage {
+    margin-top: 30px; 
+}
+
+ .errorPage .exceptionStackTrace {
+    margin-top: 30px; 
+}
+
+

http://git-wip-us.apache.org/repos/asf/isis/blob/345f22fb/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.html
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.html b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.html
new file mode 100644
index 0000000..76adce9
--- /dev/null
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.html
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+         http://www.apache.org/licenses/LICENSE-2.0
+         
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"  
+      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd"  
+      xml:lang="en"  
+      lang="en">
+	<wicket:head>
+		<wicket:link>
+			<link href="ErrorPage.css" rel="stylesheet" type="text/css"/>
+		</wicket:link>
+<script type="text/javascript">
+    jQuery(document).ready(function() {
+      jQuery(".errorPage .content").hide();
+      //toggle the componenet with class msg_body
+      jQuery(".errorPage .heading").click(function()
+      {
+        jQuery(this).next(".errorPage .content").slideToggle(500);
+      });
+    });
+</script>
+	</wicket:head>
+	<body>
+		<wicket:extend>
+			<div class="errorPage">
+                <span id="message">Sorry, an unexpected error occurred.</span>
+                <div class="errorDetail">
+                    <div class="heading"><span>Show detail</span></div>
+                    <div class="content">
+                        <div class="exceptionMessage">
+                            <h3>Message:</h3>
+                            <p wicket:id="message">Message goes here</p>
+                        </div>
+                        <div class="exceptionStackTrace">
+                            <h3>Stack trace:</h3>
+                            <p wicket:id="stackTrace">Stacktrace goes here</p>
+                        </div>
+                    </div>
+                </div>
+			</div>
+		</wicket:extend>
+	</body>
+</html>

http://git-wip-us.apache.org/repos/asf/isis/blob/345f22fb/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.java
new file mode 100644
index 0000000..3147f8d
--- /dev/null
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/error/ErrorPage.java
@@ -0,0 +1,71 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.viewer.wicket.ui.pages.error;
+
+import org.apache.isis.viewer.wicket.ui.pages.PageAbstract;
+import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+import com.google.common.base.Throwables;
+
+/**
+ * Web page representing the home page (showing a welcome message).
+ */
+@AuthorizeInstantiation("org.apache.isis.viewer.wicket.roles.USER")
+public class ErrorPage extends PageAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final String ID_MESSAGE = "message";
+    private static final String ID_STACK_TRACE = "stackTrace";
+
+    public ErrorPage(Exception ex) {
+        super(new PageParameters());
+        add(new Label(ID_MESSAGE, ex.getMessage()));
+        add(new Label(ID_STACK_TRACE, stackTraceAsString(ex)));
+    }
+
+    private static String stackTraceAsString(Throwable ex) {
+        StringBuilder buf = new StringBuilder();
+        appendStackTrace(ex, buf);
+        Throwable cause = ex.getCause();
+        while(cause != null) {
+            buf.append("\n\nCaused by:\n");
+            appendStackTrace(cause, buf);
+            cause = cause.getCause();
+        }
+        return buf.toString();
+    }
+
+    private static void appendStackTrace(Throwable ex, StringBuilder buf) {
+        for (StackTraceElement el : ex.getStackTrace()) {
+            buf. append(el.getClassName())
+                .append(el.getMethodName())
+                .append("(")
+                .append(el.getFileName())
+                .append(":")
+                .append(el.getLineNumber())
+                .append(")\n")
+                ;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/345f22fb/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/jquery.jgrowl.css
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/jquery.jgrowl.css b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/jquery.jgrowl.css
new file mode 100644
index 0000000..dbfde23
--- /dev/null
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/jquery.jgrowl.css
@@ -0,0 +1,136 @@
+
+div.jGrowl {
+	z-index: 			9999;
+	color: 				#fff;
+	font-size: 			12px;
+}
+
+/** Special IE6 Style Positioning **/
+div.ie6 {
+	position: 			absolute;
+}
+
+div.ie6.top-right {
+	right: 				auto;
+	bottom: 			auto;
+	left: 				expression( ( 0 - jGrowl.offsetWidth + ( document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
+	top: 				expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
+}
+
+div.ie6.top-left {
+	left: 				expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
+	top: 				expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
+}
+
+div.ie6.bottom-right {
+	left: 				expression( ( 0 - jGrowl.offsetWidth + ( document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
+	top: 				expression( ( 0 - jGrowl.offsetHeight + ( document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
+}
+
+div.ie6.bottom-left {
+	left: 				expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
+	top: 				expression( ( 0 - jGrowl.offsetHeight + ( document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
+}
+
+div.ie6.center {
+	left: 				expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
+	top: 				expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
+	width: 				100%;
+}
+
+/** Normal Style Positions **/
+div.jGrowl {
+	position:			absolute;
+}
+
+body > div.jGrowl {
+	position:			fixed;
+}
+
+div.jGrowl.top-left {
+	left: 				0px;
+	top: 				0px;
+}
+
+div.jGrowl.top-right {
+	right: 				0px;
+	top: 				0px;
+}
+
+div.jGrowl.bottom-left {
+	left: 				0px;
+	bottom:				0px;
+}
+
+div.jGrowl.bottom-right {
+	right: 				0px;
+	bottom: 			0px;
+}
+
+div.jGrowl.center {
+	top: 				0px;
+	width: 				50%;
+	left: 				25%;
+}
+
+/** Cross Browser Styling **/
+div.center div.jGrowl-notification, div.center div.jGrowl-closer {
+	margin-left: 		auto;
+	margin-right: 		auto;
+}
+
+div.jGrowl div.jGrowl-notification, div.jGrowl div.jGrowl-closer {
+	background-color: 		#000;
+	opacity: 				.85;
+	-ms-filter: 			"progid:DXImageTransform.Microsoft.Alpha(Opacity=85)"; 
+	filter: 				progid:DXImageTransform.Microsoft.Alpha(Opacity=85); 
+	zoom: 					1;
+	width: 					235px;
+	padding: 				10px;
+	margin-top: 			5px;
+	margin-bottom: 			5px;
+	font-family: 			Tahoma, Arial, Helvetica, sans-serif;
+	font-size: 				1em;
+	text-align: 			left;
+	display: 				none;
+	-moz-border-radius: 	5px;
+	-webkit-border-radius:	5px;
+}
+
+div.jGrowl div.jGrowl-notification {
+	min-height: 			40px;
+}
+
+div.jGrowl div.jGrowl-notification,
+div.jGrowl div.jGrowl-closer {
+	margin: 				10px;
+}
+
+div.jGrowl div.jGrowl-notification div.jGrowl-header {
+	font-weight: 			bold;
+	font-size:				.85em;
+}
+
+div.jGrowl div.jGrowl-notification div.jGrowl-close {
+	z-index:				99;
+	float: 					right;
+	font-weight: 			bold;
+	font-size: 				1em;
+	cursor:					pointer;
+}
+
+div.jGrowl div.jGrowl-closer {
+	padding-top: 			4px;
+	padding-bottom: 		4px;
+	cursor: 				pointer;
+	font-size:				.9em;
+	font-weight: 			bold;
+	text-align: 			center;
+}
+
+/** Hide jGrowl when printing **/
+@media print {
+	div.jGrowl {
+		display: 			none;
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/345f22fb/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/jquery.jgrowl.js
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/jquery.jgrowl.js b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/jquery.jgrowl.js
new file mode 100644
index 0000000..cd15d4a
--- /dev/null
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/jquery.jgrowl.js
@@ -0,0 +1,352 @@
+/**
+ * jGrowl 1.2.10
+ *
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
+ *
+ * Written by Stan Lemon <st...@gmail.com>
+ * Last updated: 2013.02.14
+ *
+ * jGrowl is a jQuery plugin implementing unobtrusive userland notifications.  These 
+ * notifications function similarly to the Growl Framework available for
+ * Mac OS X (http://growl.info).
+ *
+ * To Do:
+ * - Move library settings to containers and allow them to be changed per container
+ *
+ * Changes in 1.2.10
+ * - Fix beforeClose to be called in click event
+ *
+ * Changes in 1.2.9
+ * - Fixed BC break in jQuery 2.0 beta
+ *
+ * Changes in 1.2.8
+ * - Fixes for jQuery 1.9 and the MSIE6 check, note that with jQuery 2.0 support
+ *   jGrowl intends to drop support for IE6 altogether
+ *
+ * Changes in 1.2.6
+ * - Fixed js error when a notification is opening and closing at the same time
+ * 
+ * Changes in 1.2.5
+ * - Changed wrapper jGrowl's options usage to "o" instead of $.jGrowl.defaults
+ * - Added themeState option to control 'highlight' or 'error' for jQuery UI
+ * - Ammended some CSS to provide default positioning for nested usage.
+ * - Changed some CSS to be prefixed with jGrowl- to prevent namespacing issues
+ * - Added two new options - openDuration and closeDuration to allow 
+ *   better control of notification open and close speeds, respectively 
+ *   Patch contributed by Jesse Vincet.
+ * - Added afterOpen callback.  Patch contributed by Russel Branca.
+ *
+ * Changes in 1.2.4
+ * - Fixed IE bug with the close-all button
+ * - Fixed IE bug with the filter CSS attribute (special thanks to gotwic)
+ * - Update IE opacity CSS
+ * - Changed font sizes to use "em", and only set the base style
+ *
+ * Changes in 1.2.3
+ * - The callbacks no longer use the container as context, instead they use the actual notification
+ * - The callbacks now receive the container as a parameter after the options parameter
+ * - beforeOpen and beforeClose now check the return value, if it's false - the notification does
+ *   not continue.  The open callback will also halt execution if it returns false.
+ * - Fixed bug where containers would get confused
+ * - Expanded the pause functionality to pause an entire container.
+ *
+ * Changes in 1.2.2
+ * - Notification can now be theme rolled for jQuery UI, special thanks to Jeff Chan!
+ *
+ * Changes in 1.2.1
+ * - Fixed instance where the interval would fire the close method multiple times.
+ * - Added CSS to hide from print media
+ * - Fixed issue with closer button when div { position: relative } is set
+ * - Fixed leaking issue with multiple containers.  Special thanks to Matthew Hanlon!
+ *
+ * Changes in 1.2.0
+ * - Added message pooling to limit the number of messages appearing at a given time.
+ * - Closing a notification is now bound to the notification object and triggered by the close button.
+ *
+ * Changes in 1.1.2
+ * - Added iPhone styled example
+ * - Fixed possible IE7 bug when determining if the ie6 class shoudl be applied.
+ * - Added template for the close button, so that it's content could be customized.
+ *
+ * Changes in 1.1.1
+ * - Fixed CSS styling bug for ie6 caused by a mispelling
+ * - Changes height restriction on default notifications to min-height
+ * - Added skinned examples using a variety of images
+ * - Added the ability to customize the content of the [close all] box
+ * - Added jTweet, an example of using jGrowl + Twitter
+ *
+ * Changes in 1.1.0
+ * - Multiple container and instances.
+ * - Standard $.jGrowl() now wraps $.fn.jGrowl() by first establishing a generic jGrowl container.
+ * - Instance methods of a jGrowl container can be called by $.fn.jGrowl(methodName)
+ * - Added glue preferenced, which allows notifications to be inserted before or after nodes in the container
+ * - Added new log callback which is called before anything is done for the notification
+ * - Corner's attribute are now applied on an individual notification basis.
+ *
+ * Changes in 1.0.4
+ * - Various CSS fixes so that jGrowl renders correctly in IE6.
+ *
+ * Changes in 1.0.3
+ * - Fixed bug with options persisting across notifications
+ * - Fixed theme application bug
+ * - Simplified some selectors and manipulations.
+ * - Added beforeOpen and beforeClose callbacks
+ * - Reorganized some lines of code to be more readable
+ * - Removed unnecessary this.defaults context
+ * - If corners plugin is present, it's now customizable.
+ * - Customizable open animation.
+ * - Customizable close animation.
+ * - Customizable animation easing.
+ * - Added customizable positioning (top-left, top-right, bottom-left, bottom-right, center)
+ *
+ * Changes in 1.0.2
+ * - All CSS styling is now external.
+ * - Added a theme parameter which specifies a secondary class for styling, such
+ *   that notifications can be customized in appearance on a per message basis.
+ * - Notification life span is now customizable on a per message basis.
+ * - Added the ability to disable the global closer, enabled by default.
+ * - Added callbacks for when a notification is opened or closed.
+ * - Added callback for the global closer.
+ * - Customizable animation speed.
+ * - jGrowl now set itself up and tears itself down.
+ *
+ * Changes in 1.0.1:
+ * - Removed dependency on metadata plugin in favor of .data()
+ * - Namespaced all events
+ */
+(function($) {
+	/** Compatibility holdover for 1.9 to check IE6 **/
+	var $ie6 = (function(){
+		return false === $.support.boxModel && $.support.objectAll && $support.leadingWhitespace;
+	})();
+
+	/** jGrowl Wrapper - Establish a base jGrowl Container for compatibility with older releases. **/
+	$.jGrowl = function( m , o ) {
+		// To maintain compatibility with older version that only supported one instance we'll create the base container.
+		if ( $('#jGrowl').size() == 0 ) 
+			$('<div id="jGrowl"></div>').addClass( (o && o.position) ? o.position : $.jGrowl.defaults.position ).appendTo('body');
+
+		// Create a notification on the container.
+		$('#jGrowl').jGrowl(m,o);
+	};
+
+
+	/** Raise jGrowl Notification on a jGrowl Container **/
+	$.fn.jGrowl = function( m , o ) {
+		if ( $.isFunction(this.each) ) {
+			var args = arguments;
+
+			return this.each(function() {
+				var self = this;
+
+				/** Create a jGrowl Instance on the Container if it does not exist **/
+				if ( $(this).data('jGrowl.instance') == undefined ) {
+					$(this).data('jGrowl.instance', $.extend( new $.fn.jGrowl(), { notifications: [], element: null, interval: null } ));
+					$(this).data('jGrowl.instance').startup( this );
+				}
+
+				/** Optionally call jGrowl instance methods, or just raise a normal notification **/
+				if ( $.isFunction($(this).data('jGrowl.instance')[m]) ) {
+					$(this).data('jGrowl.instance')[m].apply( $(this).data('jGrowl.instance') , $.makeArray(args).slice(1) );
+				} else {
+					$(this).data('jGrowl.instance').create( m , o );
+				}
+			});
+		};
+	};
+
+	$.extend( $.fn.jGrowl.prototype , {
+
+		/** Default JGrowl Settings **/
+		defaults: {
+			pool: 			0,
+			header: 		'',
+			group: 			'',
+			sticky: 		false,
+			position: 		'top-right',
+			glue: 			'after',
+			theme: 			'default',
+			themeState: 	'highlight',
+			corners: 		'10px',
+			check: 			250,
+			life: 			3000,
+			closeDuration:  'normal',
+			openDuration:   'normal',
+			easing: 		'swing',
+			closer: 		true,
+			closeTemplate: '&times;',
+			closerTemplate: '<div>[ close all ]</div>',
+			log: 			function(e,m,o) {},
+			beforeOpen: 	function(e,m,o) {},
+			afterOpen: 		function(e,m,o) {},
+			open: 			function(e,m,o) {},
+			beforeClose: 	function(e,m,o) {},
+			close: 			function(e,m,o) {},
+			animateOpen: 	{
+				opacity: 	'show'
+			},
+			animateClose: 	{
+				opacity: 	'hide'
+			}
+		},
+		
+		notifications: [],
+		
+		/** jGrowl Container Node **/
+		element: 	null,
+	
+		/** Interval Function **/
+		interval:   null,
+		
+		/** Create a Notification **/
+		create: 	function( message , o ) {
+			var o = $.extend({}, this.defaults, o);
+
+			/* To keep backward compatibility with 1.24 and earlier, honor 'speed' if the user has set it */
+			if (typeof o.speed !== 'undefined') {
+				o.openDuration = o.speed;
+				o.closeDuration = o.speed;
+			}
+
+			this.notifications.push({ message: message , options: o });
+			
+			o.log.apply( this.element , [this.element,message,o] );
+		},
+		
+		render: 		function( notification ) {
+			var self = this;
+			var message = notification.message;
+			var o = notification.options;
+
+			// Support for jQuery theme-states, if this is not used it displays a widget header
+			o.themeState = (o.themeState == '') ? '' : 'ui-state-' + o.themeState;
+
+			var notification = $('<div/>')
+		        .addClass('jGrowl-notification ' + o.themeState + ' ui-corner-all' + ((o.group != undefined && o.group != '') ? ' ' + o.group : ''))
+		        .append($('<div/>').addClass('jGrowl-close').html(o.closeTemplate))
+		        .append($('<div/>').addClass('jGrowl-header').html(o.header))
+		        .append($('<div/>').addClass('jGrowl-message').html(message))
+		        .data("jGrowl", o).addClass(o.theme).children('div.jGrowl-close').bind("click.jGrowl", function() {
+		        	$(this).parent().trigger('jGrowl.beforeClose');		        
+		        })
+		        .parent();
+
+
+			/** Notification Actions **/
+			$(notification).bind("mouseover.jGrowl", function() {
+				$('div.jGrowl-notification', self.element).data("jGrowl.pause", true);
+			}).bind("mouseout.jGrowl", function() {
+				$('div.jGrowl-notification', self.element).data("jGrowl.pause", false);
+			}).bind('jGrowl.beforeOpen', function() {
+				if ( o.beforeOpen.apply( notification , [notification,message,o,self.element] ) != false ) {
+					$(this).trigger('jGrowl.open');
+				}
+			}).bind('jGrowl.open', function() {
+				if ( o.open.apply( notification , [notification,message,o,self.element] ) != false ) {
+					if ( o.glue == 'after' ) {
+						$('div.jGrowl-notification:last', self.element).after(notification);
+					} else {
+						$('div.jGrowl-notification:first', self.element).before(notification);
+					}
+					
+					$(this).animate(o.animateOpen, o.openDuration, o.easing, function() {
+						// Fixes some anti-aliasing issues with IE filters.
+						if ($.support.opacity === false) 
+							this.style.removeAttribute('filter');
+
+						if ( $(this).data("jGrowl") != null ) // Happens when a notification is closing before it's open.
+							$(this).data("jGrowl").created = new Date();
+						
+						$(this).trigger('jGrowl.afterOpen');
+					});
+				}
+			}).bind('jGrowl.afterOpen', function() {
+				o.afterOpen.apply( notification , [notification,message,o,self.element] );
+			}).bind('jGrowl.beforeClose', function() {
+				if ( o.beforeClose.apply( notification , [notification,message,o,self.element] ) != false )
+					$(this).trigger('jGrowl.close');
+			}).bind('jGrowl.close', function() {
+				// Pause the notification, lest during the course of animation another close event gets called.
+				$(this).data('jGrowl.pause', true);
+				$(this).animate(o.animateClose, o.closeDuration, o.easing, function() {
+					if ( $.isFunction(o.close) ) {
+						if ( o.close.apply( notification , [notification,message,o,self.element] ) !== false )
+							$(this).remove();
+					} else {
+						$(this).remove();
+					}
+				});
+			}).trigger('jGrowl.beforeOpen');
+		
+			/** Optional Corners Plugin **/
+			if ( o.corners != '' && $.fn.corner != undefined ) $(notification).corner( o.corners );
+
+			/** Add a Global Closer if more than one notification exists **/
+			if ( $('div.jGrowl-notification:parent', self.element).size() > 1 && 
+				 $('div.jGrowl-closer', self.element).size() == 0 && this.defaults.closer != false ) {
+				$(this.defaults.closerTemplate).addClass('jGrowl-closer ' + this.defaults.themeState + ' ui-corner-all').addClass(this.defaults.theme)
+					.appendTo(self.element).animate(this.defaults.animateOpen, this.defaults.speed, this.defaults.easing)
+					.bind("click.jGrowl", function() {
+						$(this).siblings().trigger("jGrowl.beforeClose");
+
+						if ( $.isFunction( self.defaults.closer ) ) {
+							self.defaults.closer.apply( $(this).parent()[0] , [$(this).parent()[0]] );
+						}
+					});
+			};
+		},
+
+		/** Update the jGrowl Container, removing old jGrowl notifications **/
+		update:	 function() {
+			$(this.element).find('div.jGrowl-notification:parent').each( function() {
+				if ( $(this).data("jGrowl") != undefined && $(this).data("jGrowl").created != undefined && 
+					 ($(this).data("jGrowl").created.getTime() + parseInt($(this).data("jGrowl").life))  < (new Date()).getTime() && 
+					 $(this).data("jGrowl").sticky != true && 
+					 ($(this).data("jGrowl.pause") == undefined || $(this).data("jGrowl.pause") != true) ) {
+
+					// Pause the notification, lest during the course of animation another close event gets called.
+					$(this).trigger('jGrowl.beforeClose');
+				}
+			});
+
+			if ( this.notifications.length > 0 && 
+				 (this.defaults.pool == 0 || $(this.element).find('div.jGrowl-notification:parent').size() < this.defaults.pool) )
+				this.render( this.notifications.shift() );
+
+			if ( $(this.element).find('div.jGrowl-notification:parent').size() < 2 ) {
+				$(this.element).find('div.jGrowl-closer').animate(this.defaults.animateClose, this.defaults.speed, this.defaults.easing, function() {
+					$(this).remove();
+				});
+			}
+		},
+
+		/** Setup the jGrowl Notification Container **/
+		startup:	function(e) {
+			this.element = $(e).addClass('jGrowl').append('<div class="jGrowl-notification"></div>');
+			this.interval = setInterval( function() { 
+				$(e).data('jGrowl.instance').update(); 
+			}, parseInt(this.defaults.check));
+			
+			if ($ie6) {
+				$(this.element).addClass('ie6');
+			}
+		},
+
+		/** Shutdown jGrowl, removing it and clearing the interval **/
+		shutdown:   function() {
+			$(this.element).removeClass('jGrowl').find('div.jGrowl-notification').remove();
+			clearInterval( this.interval );
+		},
+		
+		close: 	function() {
+			$(this.element).find('div.jGrowl-notification').each(function(){
+				$(this).trigger('jGrowl.beforeClose');
+			});
+		}
+	});
+	
+	/** Reference the Defaults Object for compatibility with older versions of jGrowl **/
+	$.jGrowl.defaults = $.fn.jGrowl.prototype.defaults;
+
+})(jQuery);