Ext.apply(Ext, {
    isEmpty : function(v, allowBlank) {
        return v === null || v === undefined || (Ext.isArray(v) && !v.length) || (!allowBlank ? v === '' : false);
    }
});

Ext.override(Ext.form.HtmlEditor, {
    insertAtCursor : function(text){
        if(!this.activated){
            return;
        }
        if(Ext.isIE){
            this.win.focus();
            var r = this.doc.selection.createRange();
            if(r){
                r.collapse(true);
                r.pasteHTML(text);
                this.syncValue();
                this.deferFocus();
            }
        }else if(Ext.isGecko || Ext.isOpera){
            this.win.focus();
            this.execCmd('InsertHTML', text);
            this.deferFocus();
        }else if(Ext.isSafari){
            this.execCmd('InsertHTML', '');
            this.execCmd('InsertHTML', text);
            this.deferFocus();
        }
    }
});

Ext.StatusBar.override({
    /**
     * Sets the status {@link #text} and/or {@link #iconCls}. Also supports automatically clearing the
     * status that was set after a specified interval.
     * @param {Object/String} config A config object specifying what status to set, or a string assumed
     * to be the status text (and all other options are defaulted as explained below). A config
     * object containing any or all of the following properties can be passed:<ul>
     * <li><tt>text</tt> {String} : (optional) The status text to display.  If not specified, any current
     * status text will remain unchanged.</li>
     * <li><tt>iconCls</tt> {String} : (optional) The CSS class used to customize the status icon (see
     * {@link #iconCls} for details). If not specified, any current iconCls will remain unchanged.</li>
     * <li><tt>clear</tt> {Boolean/Number/Object} : (optional) Allows you to set an internal callback that will
     * automatically clear the status text and iconCls after a specified amount of time has passed. If clear is not
     * specified, the new status will not be auto-cleared and will stay until updated again or cleared using
     * {@link #clearStatus}. If <tt>true</tt> is passed, the status will be cleared using {@link #autoClear},
     * {@link #defaultText} and {@link #defaultIconCls} via a fade out animation. If a numeric value is passed,
     * it will be used as the callback interval (in milliseconds), overriding the {@link #autoClear} value.
     * All other options will be defaulted as with the boolean option.  To customize any other options,
     * you can pass an object in the format:<ul>
     *    <li><tt>wait</tt> {Number} : (optional) The number of milliseconds to wait before clearing
     *    (defaults to {@link #autoClear}).</li>
     *    <li><tt>anim</tt> {Number} : (optional) False to clear the status immediately once the callback
     *    executes (defaults to true which fades the status out).</li>
     *    <li><tt>useDefaults</tt> {Number} : (optional) False to completely clear the status text and iconCls
     *    (defaults to true which uses {@link #defaultText} and {@link #defaultIconCls}).</li>
     * </ul></li></ul>
     * Example usage:<pre><code>
// Simple call to update the text
statusBar.setStatus('New status');

// Set the status and icon, auto-clearing with default options:
statusBar.setStatus({
    text: 'New status',
    iconCls: 'x-status-custom',
    clear: true
});

// Auto-clear with custom options:
statusBar.setStatus({
    text: 'New status',
    iconCls: 'x-status-custom',
    clear: {
        wait: 8000,
        anim: false,
        useDefaults: false
    }
});
</code></pre>
     * @return {Ext.StatusBar} this
     */
    setStatus : function(o){
        o = o || {};

        if(typeof o == 'string'){
            o = {text:o};
        }
        if(o.text !== undefined){
            this.setText(o.text);
        }
        if(o.iconCls !== undefined){
            if ( o.iconCls === '' ) {
                this.statusEl.removeClass('x-status-with-icon');
            } else {
                this.statusEl.addClass('x-status-with-icon');
            }

            this.setIcon(o.iconCls);
        }

        if(o.clear){
            var c = o.clear,
                wait = this.autoClear,
                defaults = {useDefaults: true, anim: true};

            if(typeof c == 'object'){
                c = Ext.applyIf(c, defaults);
                if(c.wait){
                    wait = c.wait;
                }
            }else if(typeof c == 'number'){
                wait = c;
                c = defaults;
            }else if(typeof c == 'boolean'){
                c = defaults;
            }

            c.threadId = this.activeThreadId;
            this.clearStatus.defer(wait, this, [c]);
        }
        return this;
    },

    /**
     * Clears the status {@link #text} and {@link #iconCls}. Also supports clearing via an optional fade out animation.
     * @param {Object} config (optional) A config object containing any or all of the following properties.  If this
     * object is not specified the status will be cleared using the defaults below:<ul>
     * <li><tt>anim</tt> {Boolean} : (optional) True to clear the status by fading out the status element (defaults
     * to false which clears immediately).</li>
     * <li><tt>useDefaults</tt> {Boolean} : (optional) True to reset the text and icon using {@link #defaultText} and
     * {@link #defaultIconCls} (defaults to false which sets the text to '' and removes any existing icon class).</li>
     * </ul>
     * @return {Ext.StatusBar} this
     */
    clearStatus : function(o){
        o = o || {};

        if(o.threadId && o.threadId !== this.activeThreadId){
            // this means the current call was made internally, but a newer
            // thread has set a message since this call was deferred.  Since
            // we don't want to overwrite a newer message just ignore.
            return this;
        }

        var text = o.useDefaults ? (this.defaultText ? this.defaultText : '') : '',
            iconCls = o.useDefaults ? (this.defaultIconCls ? this.defaultIconCls : '') : '';

        if(o.anim){
            this.statusEl.fadeOut({
                remove: false,
                useDisplay: true,
                scope: this,
                callback: function(){
                    this.setStatus({
                        text: text,
                        iconCls: iconCls
                    });
                    this.statusEl.show();

                    if(o.callback) {
                        Ext.callback(o.callback, o.scope || this, [o], o.delay || ( this.autoClear || 0 ));
                    }
                }
            });
        }else{
            // hide/show the el to avoid jumpy text or icon
            this.statusEl.hide();
            this.setStatus({
                text: text,
                iconCls: iconCls
            });
            this.statusEl.show();
            if(o.callback) {
                Ext.callback(o.callback, o.scope || this, [o], o.delay || ( this.autoClear || 0 ));
            }
        }
        return this;
    }
});

Ext.override(Ext.data.GroupingStore, {
    applySort : function(){
        Ext.data.GroupingStore.superclass.applySort.call(this);

        if(!this.groupOnSort && !this.remoteGroup){
            var gs = this.getGroupState();
            var si = this.sortInfo || {};

            if(gs && gs != si.field){
                this.sortData(this.groupField);
            }
        }
    }
});


Ext.override(Ext.form.Field, {
    msgTarget : 'under'
});

Ext.override(Ext.grid.ColumnModel, {
    setHidden : function(colIndex, hidden){
        var c = this.config[colIndex];
        if(c && c.hidden !== hidden){
            c.hidden = hidden;
            this.totalWidth = null;
            this.fireEvent("hiddenchange", this, colIndex, hidden);
        }
    }
});

Ext.override(Ext.form.RadioGroup, {
    afterRender: function() {
        var group = this;
        this.items.each(function(field) {
            // Listen for 'check' event on each child item
            field.on("check", function(self, checked) {
              // if checkbox is checked, then fire 'change' event on RadioGroup container
              if(checked) {
                  // Note, oldValue (third parameter in 'change' event listener) is not passed,
                // because is not easy to get it
                group.fireEvent('change', group, self.value);
              }
            });
        });

        Ext.form.RadioGroup.superclass.afterRender.call(this);
    }
});

Ext.override(Ext.layout.FormLayout, {
    renderItem : function(c, position, target) {
        if(c && !c.rendered && c.isFormField && c.inputType != 'hidden') {
            var args = [
                   c.id, c.fieldLabel,
                   c.labelStyle||this.labelStyle||'',
                   this.elementStyle||'',
                   typeof c.labelSeparator == 'undefined' ? this.labelSeparator : c.labelSeparator,
                   (c.itemCls||this.container.itemCls||'') + (c.hideLabel ? ' x-hide-label' : ''),
                   c.clearCls || 'x-form-clear-left'
            ];
            if(typeof position == 'number') {
                position = target.dom.childNodes[position] || null;
            }
            if(position) {
                c.formItem = this.fieldTpl.insertBefore(position, args, true);
            }else{
                c.formItem = this.fieldTpl.append(target, args, true);
            }
            c.actionMode = 'formItem';
            c.render('x-form-el-'+c.id);
            c.container = c.formItem;
            c.actionMode = 'container';
        }else {
            Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
        }
    }
});

Ext.override(Ext.form.TriggerField, {
	actionMode: 'wrap',
	onShow: Ext.form.TriggerField.superclass.onShow,
	onHide: Ext.form.TriggerField.superclass.onHide
});

Ext.override(Ext.tree.TreeNode, {
    findChild:  function(attribute, value, deep) {
        var cs = this.childNodes;

        for(var i = 0, len = cs.length; i < len; i++) {
            if(cs[i].attributes[attribute] == value) {
                return cs[i];
            } else if(deep) {
                return cs[i].findChild(attribute, value);
            }
        }

        return null;
    }
});

Ext.override(Ext.form.CheckboxGroup, {
  getNames: function() {
    var n = [];

    this.items.each(function(item) {
      if (item.getValue()) {
        n.push(item.getName());
      }
    });

    return n;
  },

  getValues: function() {
    var v = [];

    this.items.each(function(item) {
      if (item.getValue()) {
        v.push(item.getRawValue());
      }
    });

    return v;
  },

  setValues: function(v) {
    var r = new RegExp('(' + v.join('|') + ')');

    this.items.each(function(item) {
      item.setValue(r.test(item.getRawValue()));
    });
  }
});

Ext.override(Ext.form.RadioGroup, {
    getName: function() {
        return this.items.first().getName();
    },

    getValue: function() {
        var v;
        this.items.each(function(item) {
            if (!item.getValue()) {
                return true;
            }
            v = item.getRawValue();
            return false;
        });
        return v;
    },

    setValue: function(v) {
        this.items.each(function(item) {
        item.setValue(item.getRawValue() == v);
        });
    }
});

/**
 * Adds focus/blur events to Ext.Viewport which generally translate to
 * browser window focus/blur.
 *
 * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
 */
Ext.Viewport.prototype.initComponent = Ext.Viewport.prototype.initComponent.createInterceptor(
    function() {
       this.addEvents(
            /**
             * @event blur
             * Fires when the viewport loses its focus, i.e. when the browser window/tab
             * loses its focus
             * @param {Ext.Viewport}
             * @param {HTMLElement} lastActiveElement
             */
            'blur',
            /**
             * @event focus
             * @event blur
             * Fires when the viewport gains focus, i.e. when the browser window/tab
             * gains focus
             * @param {Ext.Viewport}
             * @param {HTMLElement} lastActiveElement
             */
            'focus'
        );

        var focusEl    = window;
        var eventNames = ['focus', 'blur'];

        if (Ext.isIE) {
            focusEl    = document;
            eventNames = ['focusin', 'focusout'];
        }

        Ext.EventManager.on(focusEl, eventNames[0], function(e) {
            if (this._hasFocus) {
                return;
            }
            this._hasFocus = true;
            this.fireEvent('focus', this, this._activeElement);
        }, this, {stopPropagation : true});

        Ext.EventManager.on(focusEl, eventNames[1], function(e) {
            if (this._activeElement != document.activeElement) {
                this._activeElement = document.activeElement;
                // ie detects focus loss if current activeElement
                // equals to last active element
                if (Ext.isIE) {
                    return;
                }
            }
            this._hasFocus = false;
            this.fireEvent('blur', this, this._activeElement);
        }, this, {stopPropagation : true});

        this._activeElement = document.activeElement;
    }
);

