    /**
     * A {@link Ext.ux.statusbar.StatusBar} plugin that provides automatic error
     * notification when the associated form contains validation errors.
     */
    Ext.define('Ext.ux.statusbar.ValidationStatus', {
        extend: 'Ext.Component',
        requires: ['Ext.util.MixedCollection'],
        /**
         * @cfg {String} errorIconCls
         * The {@link Ext.ux.statusbar.StatusBar#iconCls iconCls} value to be applied
         * to the status message when there is a validation error.
         */
        errorIconCls : 'x-status-error',
        /**
         * @cfg {String} errorListCls
         * The css class to be used for the error list when there are validation errors.
         */
        errorListCls : 'x-status-error-list',
        /**
         * @cfg {String} validIconCls
         * The {@link Ext.ux.statusbar.StatusBar#iconCls iconCls} value to be applied
         * to the status message when the form validates.
         */
        validIconCls : 'x-status-valid',

        /**
         * @cfg {String} showText
         * The {@link Ext.ux.statusbar.StatusBar#text text} value to be applied when
         * there is a form validation error.
         */
        showText : 'The form has errors (click for details...)',
        /**
         * @cfg {String} hideText
         * The {@link Ext.ux.statusbar.StatusBar#text text} value to display when
         * the error list is displayed.
         */
        hideText : 'Click again to hide the error list',
        /**
         * @cfg {String} submitText
         * The {@link Ext.ux.statusbar.StatusBar#text text} value to be applied when
         * the form is being submitted.
         */
        submitText : 'Saving...',

        // private
        init : function(sb) {
            var me = this;

            me.statusBar = sb;
            sb.on({
                single: true,
                scope: me,
                render: me.onStatusbarRender,
                beforedestroy: me.destroy
            });
            sb.on({
                click: {
                    element: 'el',
                    fn: me.onStatusClick,
                    scope: me,
                    buffer: 200
                }
            });
        },

        onStatusbarRender: function(sb) {
            var me = this,
                startMonitor = function() {
                    me.monitor = true;
                };

            me.monitor = true;
            me.errors = Ext.create('Ext.util.MixedCollection');
            me.listAlign = (sb.statusAlign === 'right' ? 'br-tr?' : 'bl-tl?');

            if (me.form) {
                me.formPanel = Ext.getCmp(me.form);
                me.basicForm = me.formPanel.getForm();
                me.startMonitoring();
                me.basicForm.on('beforeaction', function(f, action) {
                    if (action.type === 'submit') {
                        // Ignore monitoring while submitting otherwise the field validation
                        // events cause the status message to reset too early
                        me.monitor = false;
                    }
                });
                me.basicForm.on('actioncomplete', startMonitor);
                me.basicForm.on('actionfailed', startMonitor);
            }
       },

        // private
        startMonitoring : function() {
            this.basicForm.getFields().each(function(f) {
                f.on('validitychange', this.onFieldValidation, this);
            }, this);
        },

        // private
        stopMonitoring : function() {
            this.basicForm.getFields().each(function(f) {
                f.un('validitychange', this.onFieldValidation, this);
            }, this);
        },

        // private
        onDestroy : function() {
            this.stopMonitoring();
            this.statusBar.statusEl.un('click', this.onStatusClick, this);
            this.callParent(arguments);
        },

        // private
        onFieldValidation : function(f, isValid) {
            var me = this,
                msg;

            if (!me.monitor) {
                return false;
            }
            msg = f.getErrors()[0];
            if (msg) {
                me.errors.add(f.id, {field:f, msg:msg});
            } else {
                me.errors.removeAtKey(f.id);
            }
            this.updateErrorList();
            if (me.errors.getCount() > 0) {
                if (me.statusBar.getText() !== me.showText) {
                    me.statusBar.setStatus({
                        text: me.showText,
                        iconCls: me.errorIconCls
                    });
                }
            } else {
                me.statusBar.clearStatus().setIcon(me.validIconCls);
            }
        },

        // private
        updateErrorList : function() {
            var me = this,
                msg,
                msgEl = me.getMsgEl();

            if (me.errors.getCount() > 0) {
                msg = ['<ul>'];
                this.errors.each(function(err) {
                    msg.push('<li id="x-err-', err.field.id, '"><a href="#">', err.msg, '</a></li>');
                });
                msg.push('</ul>');
                msgEl.update(msg.join(''));
            } else {
                msgEl.update('');
            }
            // reset msgEl size
            msgEl.setSize('auto', 'auto');
        },

        // private
        getMsgEl : function() {
            var me = this,
                msgEl = me.msgEl,
                t;

            if (!msgEl) {
                msgEl = me.msgEl = Ext.DomHelper.append(Ext.getBody(), {
                    cls: me.errorListCls
                }, true);
                msgEl.hide();
                msgEl.on('click', function(e) {
                    t = e.getTarget('li', 10, true);
                    if (t) {
                        Ext.getCmp(t.id.split('x-err-')[1]).focus();
                        me.hideErrors();
                    }
                }, null, {stopEvent: true}); // prevent anchor click navigation
            }
            return msgEl;
        },

        // private
        showErrors : function() {
            var me = this;

            me.updateErrorList();
            me.getMsgEl().alignTo(me.statusBar.getEl(), me.listAlign).slideIn('b', {duration: 300, easing: 'easeOut'});
            me.statusBar.setText(me.hideText);
            me.formPanel.body.on('click', me.hideErrors, me, {single:true}); // hide if the user clicks directly into the form
        },

        // private
        hideErrors : function() {
            var el = this.getMsgEl();
            if (el.isVisible()) {
                el.slideOut('b', {duration: 300, easing: 'easeIn'});
                this.statusBar.setText(this.showText);
            }
            this.formPanel.body.un('click', this.hideErrors, this);
        },

        // private
        onStatusClick : function() {
            if (this.getMsgEl().isVisible()) {
                this.hideErrors();
            } else if (this.errors.getCount() > 0) {
                this.showErrors();
            }
        }
    });