Ext.ux.SearchField = Ext.extend(Ext.form.TwinTriggerField,
{
    initComponent: function()
    {
        if (!this.store.baseParams) {
            this.store.baseParams = {};
        }
        Ext.ux.SearchField.superclass.initComponent.call(this);
        this.on('specialkey', function(f, e) {
            if (e.getKey() == e.ENTER) {
                this.onTrigger2Click();
            }
        }, this);
    },

    validationEvent: false,
    validateOnBlur: false,
    trigger1Class: 'x-form-clear-trigger',
    trigger2Class: 'x-form-search-trigger',
    hideTrigger1: true,
    width: 180,
    hasSearch: false,
    paramName: 'query',

    onTrigger1Click: function()
    {
        if (this.hasSearch){

            for (var i = 0; i < this.optionFields.length; i++) {
                if (typeof(this.optionFields[i]) != 'object') {
                    continue;
                }
                this.store.baseParams[this.optionFields[i].name] = '';
            }
            var o = {start: 0};
            this.store.reload({params: o});
            this.el.dom.value = '';
            this.triggers[0].hide();
            this.hasSearch = false;
            this.focus();
        }
    },
    
    search: function()
    {
        var v = this.getRawValue();
        if (v.length < 1) {
            this.onTrigger1Click();
            return;
        }

        for (var i = 0; i < this.optionFields.length; i++) {
            if (typeof(this.optionFields[i]) != 'object' || !this.optionFields[i].field) {
                continue;
            }
            if (!this.optionFields[i].field.getValue()) {
                return;
            }
            this.store.baseParams[this.optionFields[i].name] = this.optionFields[i].field.getValue();
        }

        this.store.baseParams['searchFor'] = v;
    },

    onTrigger2Click: function()
    {
        this.search();
        this.hasSearch = true;
        this.triggers[0].show();
        this.focus();

        var o = {start: 0};
        this.store.reload({params: o});
    },

    setValue: function(in_value, in_option)
    {
        if (in_option) {
            for (var i = 0; i < this.optionFields.length; i++) {
                if (typeof(this.optionFields[i]) != 'object' || !this.optionFields[i].field) {
                    continue;
                }
                this.optionFields[i].field.setValue(typeof(in_option) == 'object' && typeof(in_option[i]) != 'undefined' ? in_option[i] : in_option);
            }
        }

        if (this.triggers) {
            this.hasSearch = true;
            this.triggers[0].show();
        } else {
            var func = function() {
                this.hasSearch = true;
                this.triggers[0].show();
                this.un('render', func);
            };
            this.on('render', func, this);
        }

        Ext.ux.SearchField.superclass.setValue.call(this, in_value);
        this.search();
    },

    run: function()
    {
        return this.onTrigger2Click();
    }
});

Ext.ComponentMgr.registerType('searchField', Ext.ux.SearchField);

Ext.form.Field.prototype.initComponent = Ext.Component.prototype.initComponent.createInterceptor(function()
{
    this.on({
        'change': function() {
            if (this.onChange) {
                this.dirty = true;
                this.addClass('dirty');
                this.onChange();
            }
        },
        'check': function() {
            if (this.onChange) {
                this.dirty = true;
                this.addClass('dirty');
                this.onChange();
            }
        }
    });
});

Ext.ux.TableField = Ext.extend(Ext.form.Field,
{
    initComponent: function(in_config)
    {
        this.on('render', function(in_this)
        {
            var container = Ext.get('x-form-el-' + this.id);
            container.addClass('mduTableField');
            container.update('');
            var rawValue = this.getValue();           
            var rows = [];
            
            if (typeof(rawValue) == 'undefined') {
                return;
            }

            for (var i = 0; i < rawValue.length; i++) {

                var cols = [];

                for (var j = 0; j < this.initialConfig.columns.length; j++) {

                    var value = rawValue[i][this.initialConfig.columns[j].index];

                    if (this.initialConfig.columns[j].renderer) {
                        value = this.initialConfig.columns[j].renderer(value);
                    }

                    cols.push(
                        '<td' + (this.initialConfig.columns[j].style ? ' style="' + this.initialConfig.columns[j].style + '"' : '') + '>'
                        + value
                        + '</td>'
                    );
                }

                rows.push('<tr>' + cols.join('') + '</tr>');
            }

            in_this.body = container.createChild({
                cls: 'body',
                html: '<table class="mduFormMiniList">' + rows.join('') + '</table>'
            });

            if (this.initialConfig.style) {
                in_this.body.setStyle(this.initialConfig.style);
            }

            if (this.initialConfig.anchor) {
                in_this.body.setStyle('width', this.initialConfig.anchor);
            }

            return Ext.ux.TableField.superclass.initComponent.call(this, in_config);
        }, this);
    },

    setValue: function(in_value)
    {
        Ext.ux.ActionField.superclass.setValue.call(this, in_value);
        this.fireEvent('render', this);
    },

    getValue: function()
    {
        return this.value;
    }
});

Ext.ComponentMgr.registerType('tableField', Ext.ux.TableField);

Ext.ux.ActionField = Ext.extend(Ext.form.Field,
{
    initComponent: function(in_config)
    {
        this.on('render', function(in_this)
        {
            var container = Ext.get('x-form-el-' + this.id);
            container.addClass('mduField');
            container.update('');
            var value = this.getValue();
            var stringValue = value;

            if (this.renderer && typeof(value) != 'undefined') {
                stringValue = this.renderer(value);
            }

            if (!this.scope) {
                this.scope = this;
            }

            if (this.click) {
                var button = container.createChild({cls: 'button'});
                button.on('click', this.click, this.scope);
            }

            in_this.body = container.createChild({
                cls: 'body',
                html: stringValue
            });

            if (!this.click) {
                in_this.body.setStyle('float', 'left');
            }

            if (this.initialConfig.style) {
                in_this.body.setStyle(this.initialConfig.style);
            }

            if (this.initialConfig.anchor) {
                in_this.body.setStyle('width', this.initialConfig.anchor);
            }

            if (value && typeof(value) != 'undefined' && typeof(value) == 'object' && (value.constructor !== Array || value.length)) {
                if (value.constructor === Array) {
                    for (var i = 0; i < value.length; i++) {
                        if (typeof(value[i]) == 'object') {
                            if (value[i].constructor === Array) {
                                for (var j = 0; j < value[j].length; j++) {
                                    container.createChild({
                                        name: this.name + '_' + j + '[]',
                                        tag: 'input',
                                        type: 'hidden',
                                        value: value[i][j]
                                    });
                                }
                            } else {
                                for (o_key in value[i]) {
                                    container.createChild({
                                        name: this.name + '_' + o_key + '[]',
                                        tag: 'input',
                                        type: 'hidden',
                                        value: value[i][o_key]
                                    });
                                }
                            }
                        } else {
                            container.createChild({
                                name: this.name + '[]',
                                tag: 'input',
                                type: 'hidden',
                                value: value[i]
                            });
                        }
                    }
                } else {
                    for (key in value) {
                        if (value[key] && typeof(value[key]) == 'object') {
                            if (value[key].constructor === Array) {
                                for (var j = 0; j < value[j].length; j++) {
                                    container.createChild({
                                        name: this.name + '_' + j + '[]',
                                        tag: 'input',
                                        type: 'hidden',
                                        value: value[key][j]
                                    });
                                }
                            } else {
                                for (o_key in value[key]) {
                                    container.createChild({
                                        name: this.name + '_' + key + '_' + o_key,
                                        tag: 'input',
                                        type: 'hidden',
                                        value: value[key][o_key]
                                    });
                                }
                            }
                        } else {
                            container.createChild({
                                name: this.name + '_' + key,
                                tag: 'input',
                                type: 'hidden',
                                value: value[key]
                            });
                        }
                    }
                }
            } else {
                container.createChild({
                    name: this.name,
                    tag: 'input',
                    type: 'hidden',
                    value: typeof(value) == 'undefined' ? 0 : value
                });
            }

            if (this.click) {
                in_this.body.on('click', this.click, this.scope);
                in_this.body.setStyle('cursor', 'pointer');
            }

            if (!this.click && this.link) {
                in_this.body.on('click', this.link, this.scope);
                in_this.body.addClass('ActionLink');
            }

            if ((stringValue == '') || (stringValue == null) || (typeof(stringValue) == 'undefined')) {
                in_this.body.setHeight(22);
            }
        }, this);

        return Ext.ux.ActionField.superclass.initComponent.call(this, in_config);
    },

    addClass: function(in_class)
    {
        this.body.addClass(in_class);
    },

    removeClass: function(in_class)
    {
        this.body.removeClass(in_class);
    },

    setValue: function(in_value)
    {
        Ext.ux.ActionField.superclass.setValue.call(this, in_value);
        this.fireEvent('render', this);
    },

    getValue: function()
    {
        return this.value;
    }
});

Ext.ComponentMgr.registerType('actionField', Ext.ux.ActionField);

Ext.ux.MultipleActionField = Ext.extend(Ext.form.Field,
{
    value: [],

    dirtyIds: {},

    initComponent: function(in_config)
    {
        this.value = [];
        this.setValue([]);
        this.on({
            'change': function(in_field, in_id, in_old) {
                if (this.onChange) {
                    if (typeof(in_id) == 'undefined') {
                        return;
                    }
                    this.dirtyIds[in_id] = true;
                    this.dirty = true;
                    this.onChange(in_field);
                }
                this.fireEvent('render', this);
            }
        });

        this.on('render', function(in_this)
        {
            var values = this.getValue();

            if (!this.dirty) {
                this.dirtyIds = {};
            }

            if (typeof(values) == 'undefined') {
                return;
            }

            if (!this.scope) {
                this.scope = this;
            }

            var container = Ext.get('x-form-el-' + this.id);
            container.addClass('mduMultipleActionField');
            container.update('');

            if (this.add) {
                values.push({
                    added: true
                });
            }

            for (var i = 0; i < values.length; i++) {

                if (values[i].removed) {

                    var lineContainer = container;

                } else if (values[i] !== false) {

                    values[i].removed = false;
                    var lineContainer = container.createChild();
                    lineContainer.addClass('mduField');

                    if (values[i].added) {
                        var stringValue = this.add;
                        var button = lineContainer.createChild({cls: 'button'});
                        button.on('click', this.click, this.scope, {added: true});
                    } else {
                        var stringValue = values[i];
                        if (this.renderer && typeof(values[i]) != 'undefined') {
                            stringValue = this.renderer(values[i]);
                        }
                        if (this.click) {
                            var button = lineContainer.createChild({cls: 'button'});
                            button.on('click', this.click, this.scope, {field: this, id: i});
                        }
                    }

                    var body = lineContainer.createChild({
                        cls: 'body',
                        html: stringValue,
                        style: this.initialConfig.style
                    });

                    if (this.dirty && typeof(this.dirtyIds[i]) != 'undefined') {
                        body.addClass('dirty');
                    }

                    if (values[i].added) {
                        body.on('click', this.click, this.scope, {added: true});
                        body.addClass('added');
                    } else if (this.click) {
                        body.on('click', this.click, this.scope, {field: this, id: i});
                    }
                }

                if (values[i] !== false) {
                    if (!values[i].added) {
                        if (typeof(values[i]) == 'object') {
                            if (values[i].constructor === Array) {
                                for (var j = 0; j < values[i].length; j++) {
                                    if (values[i][j] && typeof(values[i][j]) == 'object') {
                                        if (values[i][j].constructor === Array) {
                                            for (var k = 0; k < values[i][j].length; j++) {
                                                container.createChild({
                                                    name: this.name + '[][][]',
                                                    tag: 'input',
                                                    type: 'hidden',
                                                    value: values[i][j][k]
                                                });
                                            }
                                        } else {
                                            for (o_key in values[i][j]) {
                                                container.createChild({
                                                    name: this.name + '[' + i + '][' + j + ']' + '[' + o_key + ']',
                                                    tag: 'input',
                                                    type: 'hidden',
                                                    value: values[i][j][o_key]
                                                });
                                            }
                                        }
                                    } else {
                                        container.createChild({
                                            name: this.name + '[][]',
                                            tag: 'input',
                                            type: 'hidden',
                                            value: values[i][j]
                                        });
                                    }
                                }
                            } else {
                                for (key in values[i]) {
                                    if (values[i][key] && typeof(values[i][key]) == 'object') {
                                        if (values[i][key].constructor === Array) {
                                            for (var k = 0; k < values[i][key].length; j++) {
                                                container.createChild({
                                                    name: this.name + '[' + i + '][' + k + '][' + key + ']',
                                                    tag: 'input',
                                                    type: 'hidden',
                                                    value: values[i][key][k]
                                                });
                                            }
                                        } else {
                                            for (o_key in values[i][key]) {
                                                container.createChild({
                                                    name: this.name + '[' + i + '][' + key + '][' + o_key + ']',
                                                    tag: 'input',
                                                    type: 'hidden',
                                                    value: values[i][key][o_key]
                                                });
                                            }
                                        }
                                    } else {
                                        container.createChild({
                                            name: this.name + '[' + i + '][' + key + ']',
                                            tag: 'input',
                                            type: 'hidden',
                                            value: values[i][key]
                                        });
                                    }
                                }
                            }
                        } else {
                            lineContainer.createChild({
                                name: this.name + '[]',
                                tag: 'input',
                                type: 'hidden',
                                value: values[i]
                            });
                        }
                    }
                }
            }

        }, this);

        return Ext.ux.MultipleActionField.superclass.initComponent.call(this, in_config);
    },

    setValue: function(in_value)
    {
        Ext.ux.MultipleActionField.superclass.setValue.call(this, in_value);
        this.fireEvent('render', this);
    },

    getValue: function()
    {
        var values = [];

        for (var i = 0; i < this.value.length; i++) {
            if (!this.value[i].added) {
                values.push(this.value[i]);
            }
        }

        return values;
    },

    removeClass: function(in_class)
    {
        if (in_class == 'dirty' && this.dirty) {
            this.dirty = false;
            this.dirtyIds = {};
            this.fireEvent('render', this);
        }
    },

    addValue: function(in_data)
    {
        this.value.push(in_data);
        this.fireEvent('change', this, this.value.length - 1);
    },

    updateValue: function(in_id, in_data)
    {
        this.value[in_id] = in_data;
        this.fireEvent('change', this, in_id);
    },

    removeValue: function(in_id)
    {
        if (typeof(this.value[in_id]) == 'object') {
            this.value[in_id].removed = true;
        } else {
            this.value[in_id] = false;
        }
        this.fireEvent('change', this, in_id);
    }
});

Ext.ComponentMgr.registerType('multipleActionField', Ext.ux.MultipleActionField);

Ext.ux.SeparateField = Ext.extend(Ext.form.TextField,
{
    initComponent: function(in_config)
    {
        if (!in_config) {
            in_config = {};
        }

        in_config.hideLabel = true;
        in_config.labelSeparator = '';

        this.on('render', function(in_this)
        {
            var container = Ext.get('x-form-el-' + this.id);
            container.addClass('separateField');
            container.update('');

            in_this.body = container.createChild({
                cls: 'body'
            });
        }, this);
        
        Ext.apply(this, in_config);

        return Ext.ux.SeparateField.superclass.initComponent.call(this, in_config);
    }
});

Ext.ComponentMgr.registerType('separateField', Ext.ux.SeparateField);

/**
 * Makes a ComboBox more closely mimic an HTML SELECT.  Supports clicking and dragging
 * through the list, with item selection occurring when the mouse button is released.
 * When used will automatically set {@link #editable} to false and call {@link Ext.Element#unselectable}
 * on inner elements.  Re-enabling editable after calling this will NOT work.
 *
 * @author Corey Gilmore
 * http://extjs.com/forum/showthread.php?t=6392
 *
 * @history 2007-07-08 jvs
 * Slight mods for Ext 2.0
 */
 /*
Ext.ux.SelectBox = function(config)
{
    this.searchResetDelay = 1000;
    config = config || {};
    config = Ext.apply(config || {}, {
        editable: false,
        forceSelection: true,
        rowHeight: false,
        lastSearchTerm: false,
        triggerAction: 'all',
        mode: 'local'
    });

    Ext.ux.SelectBox.superclass.constructor.apply(this, arguments);

    this.lastSelectedIndex = this.selectedIndex || 0;
};

Ext.extend(Ext.ux.SelectBox, Ext.form.ComboBox,
{
    lazyInit: false,

    initEvents: function()
    {
        Ext.ux.SelectBox.superclass.initEvents.apply(this, arguments);
        // you need to use keypress to capture upper/lower case and shift+key, but it doesn't work in IE
        this.el.on('keydown', this.keySearch, this, true);
        this.cshTask = new Ext.util.DelayedTask(this.clearSearchHistory, this);
    },

    keySearch: function(e, target, options)
    {
        var raw = e.getKey();
        var key = String.fromCharCode(raw);
        var startIndex = 0;

        if (!this.store.getCount()) {
            return;
        }

        switch (raw) {
            case Ext.EventObject.HOME:
                e.stopEvent();
                this.selectFirst();
                return;

            case Ext.EventObject.END:
                e.stopEvent();
                this.selectLast();
                return;

            case Ext.EventObject.PAGEDOWN:
                this.selectNextPage();
                e.stopEvent();
                return;

            case Ext.EventObject.PAGEUP:
                this.selectPrevPage();
                e.stopEvent();
                return;
        }

        // skip special keys other than the shift key
        if ((e.hasModifier() && !e.shiftKey) || e.isNavKeyPress() || e.isSpecialKey()) {
            return;
        }
        if (this.lastSearchTerm == key) {
            startIndex = this.lastSelectedIndex;
        }

        this.search(this.displayField, key, startIndex);
        this.cshTask.delay(this.searchResetDelay);
    },

    onRender: function(ct, position)
    {
        this.store.on('load', this.calcRowsPerPage, this);
        Ext.ux.SelectBox.superclass.onRender.apply(this, arguments);
        if (this.mode == 'local') {
            this.calcRowsPerPage();
        }
    },

    onSelect: function(record, index, skipCollapse)
    {
        if (this.fireEvent('beforeselect', this, record, index) !== false) {
            this.setValue(record.data[this.valueField || this.displayField]);
            if(!skipCollapse) {
                this.collapse();
            }
            this.lastSelectedIndex = index + 1;
            this.fireEvent('select', this, record, index);
        }
    },

    render: function(ct)
    {
        Ext.ux.SelectBox.superclass.render.apply(this, arguments);
        if (Ext.isSafari) {
            this.el.swallowEvent('mousedown', true);
        }
        this.el.unselectable();
        this.innerList.unselectable();
        this.trigger.unselectable();
        this.innerList.on('mouseup', function(e, target, options) {
            if(target.id && target.id == this.innerList.id) {
                return;
            }
            this.onViewClick();
        }, this);

        this.innerList.on('mouseover', function(e, target, options) {
            if(target.id && target.id == this.innerList.id) {
                return;
            }
            this.lastSelectedIndex = this.view.getSelectedIndexes()[0] + 1;
            this.cshTask.delay(this.searchResetDelay);
        }, this);

        this.trigger.un('click', this.onTriggerClick, this);
        this.trigger.on('mousedown', function(e, target, options) {
            e.preventDefault();
            this.onTriggerClick();
        }, this);

        this.on('collapse', function(e, target, options) {
            Ext.getDoc().un('mouseup', this.collapseIf, this);
        }, this, true);

        this.on('expand', function(e, target, options) {
            Ext.getDoc().on('mouseup', this.collapseIf, this);
        }, this, true);
    },

    clearSearchHistory: function()
    {
        this.lastSelectedIndex = 0;
        this.lastSearchTerm = false;
    },

    selectFirst: function()
    {
        this.focusAndSelect(this.store.data.first());
    },

    selectLast: function()
    {
        this.focusAndSelect(this.store.data.last());
    },

    selectPrevPage: function()
    {
        if(!this.rowHeight) {
            return;
        }
        var index = Math.max(this.selectedIndex-this.rowsPerPage, 0);
        this.focusAndSelect(this.store.getAt(index));
    },

    selectNextPage: function()
    {
        if (!this.rowHeight) {
            return;
        }
        var index = Math.min(this.selectedIndex+this.rowsPerPage, this.store.getCount() - 1);
        this.focusAndSelect(this.store.getAt(index));
    },

    search: function(field, value, startIndex)
    {
        field = field || this.displayField;
        this.lastSearchTerm = value;
        var index = this.store.find.apply(this.store, arguments);
        if (index !== -1) {
            this.focusAndSelect(index);
        }
    },

    focusAndSelect: function(record)
    {
        var index = typeof record === 'number' ? record : this.store.indexOf(record);
        this.select(index, this.isExpanded());
        this.onSelect(this.store.getAt(record), index, this.isExpanded());
    },

    calcRowsPerPage: function()
    {
        if (this.store.getCount()) {
            this.rowHeight = Ext.fly(this.view.getNode(0)).getHeight();
            this.rowsPerPage = this.maxHeight / this.rowHeight;
        } else {
            this.rowHeight = false;
        }
    }

});

Ext.ComponentMgr.registerType('selectbox', Ext.ux.SelectBox);*/

Ext.ux.FileSelector = Ext.extend(Ext.form.TwinTriggerField,
{
    initComponent: function()
    {
        Ext.ux.FileSelector.superclass.initComponent.call(this);

        this.triggerConfig = {
            tag: 'span', cls: 'x-form-twin-triggers', cn: [ {
                tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class
            }, {
                tag:'span', cls:'ux-cabinet', cn: [ {
                    tag: "img", id: this.id + "Selector", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class
                }, {
                    tag: "div", id: this.id + "Wrapper", cls: "ux-input-file-wrapper", cn: [ {
                        tag: "input", name: this.name + "FILE", type: "file", cls: "ux-file", id: this.id + "File"
                    } ]
                } ]
            }, {
                tag: "input", name: this.name + "Keep", type: "checkbox", cls:"x-hidden", id: this.id + "Keep", value: 1
            } ]
        };

        var v = this.getRawValue();
        if (v.length > 0) {
            this.hasValue = true;
            this.uxInitialValue = v;
        }

        this.on("render", this.uxStylizeTrigger, this);
    },

    uxInitialValue: false,
    name: 'file',
    readOnly: true,
    validationEvent: false,
    validateOnBlur: false,
    trigger1Class: 'x-form-clear-trigger',
    trigger2Class: 'x-form-search-trigger',
    hideTrigger1: true,
    width: 250,
    hasValue: false,
    paramName: false,

    onTrigger1Click: function()
    {
        if (!this.hasValue) {
            return;
        }

        this.triggers[0].hide();
        this.hasValue = false;

        if (this.uxInitialValue) {
            var keep = Ext.get(this.id + "Keep");
            keep.dom.checked = true;
            this.setValue(this.uxInitialValue);
        } else {
            this.setValue('');
        }

        var input = Ext.get(this.id + "File");
        input.remove();

        var wrapper = Ext.get(this.id + "Wrapper");
        Ext.DomHelper.append(wrapper, {tag: "input", name: this.name + "FILE", type: "file", cls: "ux-file", id: this.id + "File"});

        var input_new = Ext.get(this.id + "File");
        input_new.on("change", this.uxHandleFile, this);
    },

    onTrigger2Click: function()
    {
        this.hasValue = true;
        this.triggers[0].show();

        var keep = Ext.get(this.id + "Keep");
        keep.dom.checked = false;
    },

    uxHandleFile: function()
    {
        var input = Ext.get(this.id + "File");

        //if this is unix style structure replace / with \
        var filePath = input.dom.value.replace(/\//g, '\\');

        //extract the filename from the value
        var indexPos = filePath.lastIndexOf('\\');
        var fileName = filePath.substring(indexPos + 1);

        this.setValue(fileName);

        search = /(zip|tar|gz)$/i;
        this.onTrigger2Click();
    },

    uxStylizeTrigger: function()
    {
        if (this.hasValue) {
            var keep = Ext.get(this.id + "Keep");
            keep.dom.checked = true;
        }

        var trigger = Ext.get(this.id + "Selector");
        var wrapper = Ext.get(this.id + "Wrapper");
        var input = Ext.get(this.id + "File");

        input.on("change", this.uxHandleFile, this);

        trigger.file = wrapper;
        trigger.on("mousemove", function(e) {
            var pageX = e.xy[0];
            var pageY = e.xy[1];

            ox = this.getX();
            oy = this.getY();

            var x = pageX - ox;
            var y = pageY - oy;
            var w = this.file.getWidth();
            var h = this.file.getHeight();

            this.file.setTop(y - (h / 2) - 5 + 'px');
            this.file.setLeft(x - (w - 15) + 'px');
        });
    }
});

Ext.ComponentMgr.registerType('fileSelector', Ext.ux.FileSelector);

Ext.apply(Ext.DataView.prototype, {
    deselect:function(node, suppressEvent){
    if(this.isSelected(node)){
            var node = this.getNode(node);
            this.selected.removeElement(node);
            if(this.last == node.viewIndex){
                this.last = false;
            }
            Ext.fly(node).removeClass(this.selectedClass);
            if(!suppressEvent){
                this.fireEvent('selectionchange', this, this.selected.elements);
            }
        }
    }
});

Ext.namespace('Ext.ux');

/**
 * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3
 *
 * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
 * @url http://www.siteartwork.de/wizardcomponent
 */

/**
 * @class Ext.ux.Wiz
 * @extends Ext.Window
 *
 * A specific {@link Ext.Window} that models a wizard component.
 * A wizard is basically a dialog that guides a user through various steps
 * where he has to fill out form-data.
 * A {@link Ext.ux.Wiz}-component consists typically of a {@link Ext.ux.Wiz.Header}
 * and window-buttons ({@link Ext.Button}) which are linked to the {@link Ext.ux.Wiz.Card}s
 * which themself represent the forms the user has to fill out.
 *
 * In order to switch between the cards in the wizard, you need the {@link Ext.ux.layout.CardLayout},
 * which will check if an active-item can be hidden, before the requested new item will be set to
 * 'active', i.e. shown. This is needed since the wizard may not allow a card to be hidden, if
 * the input entered by the user was not valid. You can get this custom layout at
 * {@link http://www.siteartwork.de/cardlayout}.
 *
 * @constructor
 * @param {Object} config The config object
 */
Ext.ux.Wiz = Ext.extend(Ext.Window, {

    /**
     * @cfg {Object} An object containing the messages for the {@link Ext.LoadMask}
     * covering the card-panel on request, whereas the property identifies the
     * msg-text to show, and the value is the message text itself. Defaults to
     <pre><code>
{
    default : 'Saving...'
}
     </code></pre>
     *
     * Depending on the contexts the loadMask has to be shown in (using the method
     * showLoadMask of this class), the object can be configure to hold
     * various messages.
<pre><code>
this.loadMaskConfig = {
    default    : 'Saving...',
    validating : 'Please wait, validating input...',
};
// loadMask will be shown, displaying the message 'Please wait, validating input...'
this.showLoadMask(true, 'validating');
     </code></pre>
     */
    loadMaskConfig : {
        'default' : 'Saving...'
    },

    /**
     * @cfg {Number} height The height of the dialog. Defaults to "400".
     */
    height : 400,

    /**
     * @cfg {Number} width The width of the dialog. Defaults to "540".
     */
    width : 540,

    /**
     * @cfg {Boolean} closable Wether the dialog is closable. Defaults to "true".
     */
    closable : true,

    /**
     * @cfg {Boolean} resizable Wether the dialog is resizable. Defaults to "false".
     */
    resizable : false,

    /**
     * @cfg {Boolean} resizable Wether the dialog is modal. Defaults to "true".
     */
    modal : true,

    /**
     * @cfg {Array} cards A numeric array with the configured {@link Ext.ux.Wiz.Card}s.
     * The index of the cards in the array represent the order in which they get displayed
     * in the wizard (i.e. card at index 0 gets displayed in the first step, card at index 1 gets
     * displayed in the second step and so on).
     */
    cards : null,

    /**
     * @cfg {String} previousButtonText The text to render the previous-button with.
     * Defaults to "&lt; Back" (< Back)
     */
    previousButtonText : '&lt; Previous',

    /**
     * @cfg {String} nextButtonText The text to render the next-button with.
     * Defaults to "Next &gt;" (Next >)
     */
    nextButtonText : 'Next &gt;',

    /**
     * @cfg {String} cancelButtonText The text to render the cancel-button with.
     * Defaults to "Cancel"
     */
    cancelButtonText : 'Cancel',

    /**
     * @cfg {String} finishButtonText The text to render the next-button with when the last
     * step of the wizard is reached. Defaults to "Finish"
     */
    finishButtonText : 'Finish',

    /**
     * @cfg {Object} headerConfig A config-object to use with {@link Ext.ux.Wiz.Header}.
     * If not present, it defaults to an empty object.
     */
    headerConfig : {},

    /**
     * @cfg {Object} cardPanelConfig A config-object to use with {@link Ext.Panel}, which
     * represents the card-panel in this dialog.
     * If not present, it defaults to an empty object
     */
    cardPanelConfig : {},

    /**
     * @param {Ext.Button} The window-button for paging to the previous card.
     * @private
     */
    previousButton : null,

    /**
     * @param {Ext.Button} The window-button for paging to the next card. When the
     * last card is reached, the event fired by and the text rendered to this button
     * will change.
     * @private
     */
    nextButton : null,

    /**
     * @param {Ext.Button} The window-button for canceling the wizard. The event
     * fired by this button will usually close the dialog.
     * @private
     */
    cancelButton : null,

    /**
     * @param {Ex.Panel} The card-panel that holds the various wizard cards
     * ({@link Ext.ux.Wiz.Card}). The card-panel itself uses the custom
     * {@link Ext.ux.layout.CardLayout}, which needs to be accessible by this class.
     * You can get it at {@link http://www.siteartwork.de/cardlayout}.
     * @private
     */
    cardPanel : null,

    /**
     * @param {Number} currentCard The current {@link Ext.ux.Wiz.Card} displayed.
     * Defaults to -1.
     * @private
     */
    currentCard : -1,

    /**
     * @param {Ext.ux.Wiz.Header} The header-panel of the wizard.
     * @private
     */
    headPanel : null,

    /**
     * @param {Number} cardCount Helper for storing the number of cards used
     * by this wizard. Defaults to 0 (inherits "cards.length" later on).
     * @private
     */
    cardCount : 0,

    /**
     * Inits this component with the specified config-properties and automatically
     * creates its components.
     */
    initComponent : function()
    {
        this.initButtons();
        this.initPanels();

        var title = this.title;
        title     = title || "";

        Ext.apply(this, {
            title     : title,
            layout    : 'border',
            cardCount : this.cards.length,
            buttons   : [
                this.previousButton,
                this.nextButton,
                this.cancelButton
            ],
            items : [
                this.headPanel,
                this.cardPanel
            ]
        });

        this.addEvents(
            /**
             * @event cancel
             * Fires after the cancel-button has been clicked.
             * @param {Ext.ux.Wiz} this
             */
            'cancel',
            /**
             * @event finish
             * Fires after the last card was reached in the wizard and the
             * next/finish-button has been clicked.
             * @param {Ext.ux.Wiz} this
             * @param {Object} data The collected data of the cards, whereas
             * the index is the id of the card and the specific values
             * are objects with key/value pairs in the form formElementName : value
             */
            'finish'
        );

        Ext.ux.Wiz.superclass.initComponent.call(this);
    },

// -------- helper
    /**
     * Returns the form-data of all cards in this wizard. The first index is the
     * id of the card in this wizard,
     * and the values are objects containing key/value pairs in the form of
     * fieldName : fieldValue.
     *
     * @return {Array}
     */
    getWizardData : function()
    {
        var formValues = {};
        var cards = this.cards;
        for (var i = 0, len = cards.length; i < len; i++) {
            if (cards[i].form) {
                formValues[cards[i].id] = cards[i].form.getValues(false);
            } else {
                formValues[cards[i].id] = {};
            }
        }

        return formValues;
    },

    /**
     * Switches the state of this wizard between disabled/enabled.
     * A disabled dialog will have a {@link Ext.LoadMask} covering the card-panel
     * to prevent user input, and the buttons will be rendered disabled/enabled.
     * If the dialog is clsable, the close-tool will be masked, too.
     *
     * @param {Boolean} enabled "false" to prevent user input and mask the elements,
     * otherwise true.
     * @param {String} type The type of msg for the {@Ext.LoadMask} covering
     * the cardPanel, as defined in the cfg property "loadMaskConfig"
     */
    switchDialogState : function(enabled, type)
    {
        this.showLoadMask(!enabled, type);

        this.previousButton.setDisabled(!enabled);
        this.nextButton.setDisabled(!enabled);
        this.cancelButton.setDisabled(true);

        if (this.closable) {
            var ct = this.tools['close'];
            switch (enabled) {
                case true:
                    this.tools['close'].unmask();
                break;

                default:
                    this.tools['close'].mask();
                break;
            }
        }
    },

    /**
     * Shows the load mask for this wizard. By default, the cardPanel's body
     * will be masked.
     *
     * @param {Boolean} show true to show the load mask, otherwise false.
     * @param {String} type The type of message for the {@Ext.LoadMask} covering
     * the cardPanel, as defined in the cfg property "loadMaskConfig"
     */
    showLoadMask : function(show, type)
    {
        if (!type) {
            type = 'default';
        }

        if (show) {
            if (this.loadMask == null) {
                this.loadMask = new Ext.LoadMask(this.body);
            }
            this.loadMask.msg = this.loadMaskConfig['type'];
            this.loadMask.show();
        } else {
            if (this.loadMask) {
                this.loadMask.hide();
            }
        }
    },


    /**
     * Inits the listener for the various {@link Ext.ux.Wiz.Card}s used
     * by this component.
     */
    initEvents : function()
    {
        Ext.ux.Wiz.superclass.initEvents.call(this);

        var cards = this.cards;

        for (var i = 0, len = cards.length; i < len; i++) {
            cards[i].on('show', this.onCardShow, this);
            cards[i].on('hide', this.onCardHide, this);
            cards[i].on('clientvalidation', this.onClientValidation, this);
        }
    },

    /**
     * Creates the head- and the card-panel.
     * Be sure to have the custom {@link Ext.ux.layout.CardLayout} available
     * in order to make the card-panel work as expected by this component
     * ({@link http://www.siteartwork.de/cardlayout}).
     */
    initPanels : function()
    {
        var cards           = this.cards;
        var cardPanelConfig = this.cardPanelConfig;

        for (var i = 0; i < this.cards.length; i++) {
            if (this.cards[i].callback) {
                this.cards[i].callback.apply(this.scope, [this.cards[i], this]);
            }
        }

        Ext.apply(this.headerConfig, {
            steps : cards.length
        });

        this.headPanel = new Ext.ux.Wiz.Header(this.headerConfig);

        Ext.apply(this.cardPanelConfig, {
            defaults: {
                cls: 'WizCard',
                baseCls: 'x-small-editor',
                border: false
            }
        });

        Ext.apply(cardPanelConfig, {
            layout : 'card',
            items  : cards
        });

        Ext.applyIf(cardPanelConfig, {
            region     : 'center',
            border     : false,
            activeItem : 0
        });

        this.cardPanel = new Ext.Panel(cardPanelConfig);

        Ext.apply(this, {
            keys: {
                key: 13,
                scope: this,
                fn: function() {
                    if (!this.nextButton.disabled) {
                        this.onNextClick();
                    }
                }
            }
        });

        this.on('activate', function() {
            setTimeout(function() { cards[0].getComponent(0).focus() }, 100);
            for (var i = 0; i < cards.length; i++) {
                cards[i].on('show', function(in_this) {
                    in_this.getComponent(0).focus();
                }, this);
            }
        }, this);
    },

    /**
     * Creates the instances for the the window buttons.
     */
    initButtons : function()
    {
        this.previousButton = new Ext.Button({
            text     : this.previousButtonText,
            disabled : true,
            minWidth : 75,
            handler  : this.onPreviousClick,
            scope    : this
        });

        this.nextButton = new Ext.Button({
            text     : this.nextButtonText,
            minWidth : 75,
            handler  : this.onNextClick,
            scope    : this
        });

        this.cancelButton = new Ext.Button({
            text     : this.cancelButtonText,
            handler  : this.onCancelClick,
            scope    : this,
            minWidth : 75
        });
    },

// -------- listeners
    /**
     * By default, the card firing this event monitors user input in a frequent
     * interval and fires the 'clientvalidation'-event along with it. This listener
     * will enable/disable the next/finish-button in accordance with it, based upon
     * the parameter isValid. isValid" will be set by the form validation and depends
     * on the validators you are using for the different input-elemnts in your form.
     * If the card does not contain any forms, this listener will never be called by the
     * card itself.
     *
     * @param {Ext.ux.Wiz.Card} The card that triggered the event.
     * @param {Boolean} isValid "true", if the user input was valid, otherwise
     * "false"
     */
    onClientValidation : function(card, isValid)
    {
        if (!isValid) {
            this.nextButton.setDisabled(true);
        } else {
            this.nextButton.setDisabled(false);
        }
    },

    /**
     * This will render the "next" button as disabled since the bindHandler's delay
     * of the next card to show might be lagging on slower systems
     *
     */
    onCardHide : function(card)
    {
        if (this.cardPanel.layout.activeItem.id === card.id) {
            this.nextButton.setDisabled(true);
        }
    },


    /**
     * Listener for the "show" event of the card that gets shown in the card-panel.
     * Renders the next/previous buttons based on the position of the card in the wizard
     * and updates the head-panel accordingly.
     *
     * @param {Ext.ux.Wiz.Card} The card being shown.
     */
    onCardShow : function(card)
    {
        var parent = card.ownerCt;

        var items = parent.items;

        for (var i = 0, len = items.length; i < len; i++) {
            if (items.get(i).id == card.id) {
                break;
            }
        }

        this.currentCard = i;
        this.headPanel.updateStep(i, card.title);

        if (i == len-1) {
            this.nextButton.setText(this.finishButtonText);
        } else {
            this.nextButton.setText(this.nextButtonText);
        }

        if (card.isValid()) {
            this.nextButton.setDisabled(false);
        }

        if (i == 0) {
            this.previousButton.setDisabled(true);
        } else {
            this.previousButton.setDisabled(false);
        }

        card.onCardShow();
    },

    /**
     * Fires the 'cancel'-event. Closes this dialog if the return value of the
     * listeners does not equal to "false".
     */
    onCancelClick : function()
    {
        if (this.fireEvent('cancel', this) !== false) {
            this.close();
        }
    },

    /**
     * Fires the 'finish'-event. Closes this dialog if the return value of the
     * listeners does not equal to "false".
     */
    onFinish : function()
    {
        if (this.fireEvent('finish', this, this.getWizardData()) !== false) {
            this.close();
        }
    },

    /**
     * Listener for the previous-button.
     * Switches to the previous displayed {@link Ext.ux.Wiz.Card}.
     */
    onPreviousClick : function()
    {
        if (this.currentCard > 0) {
            this.cardPanel.getLayout().setActiveItem(this.currentCard - 1);
        }
    },

    /**
     * Listener for the next-button. Switches to the next {@link Ext.ux.Wiz.Card}
     * if the 'beforehide'-method of it did not return false. The functionality
     * for this is implemented in {@link Ext.ux.layout.CardLayout}, which is needed
     * as the layout for the card-panel of this component.
     */
    onNextClick : function()
    {
        if (this.currentCard == this.cardCount-1) {
            this.onFinish();
        } else {
            this.cardPanel.getLayout().setActiveItem(this.currentCard+1);
        }
    }
});

Ext.namespace('Ext.ux.Wiz');

/**
 * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3
 *
 * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
 * @url http://www.siteartwork.de/wizardcomponent
 */

/**
 * @class Ext.ux.Wiz.Header
 * @extends Ext.BoxComponent
 *
 * A specific {@link Ext.BoxComponent} that can be used to show the current process in an
 * {@link Ext.ux.Wiz}.
 *
 * An instance of this class is usually being created by {@link Ext.ux.Wiz#initPanels} using the
 * {@link Ext.ux.Wiz#headerConfig}-object.
 *
 * @private
 * @constructor
 * @param {Object} config The config object
 */
Ext.ux.Wiz.Header = Ext.extend(Ext.BoxComponent, {

    /**
     * @cfg {Number} height The height of this component. Defaults to "55".
     */
    height : 45,

    /**
     * @cfg {String} region The Region of this component. Since a {@link Ext.ux.Wiz}
     * usually uses a {@link Ext.layout.BorderLayout}, this property defaults to
     * "north". If you want to change this property, you should also change the appropriate
     * css-classes that are used for this component.
     */
    region : 'north',

    /**
     * @cfg {String} title The title that gets rendered in the head of the component. This
     * should be a text describing the purpose of the wizard.
     */
    title : 'Wizard',

    /**
     * @cfg {Number} steps The overall number of steps the user has to go through
     * to finish the wizard.
     */
    steps : 0,

    /**
     * @cfg {String} stepText The text in the header indicating the current process in the wizard.
     * (defaults to "Step {0} of {1}: {2}").
     * {0} is replaced with the index (+1) of the current card, {1} is replaced by the
     * total number of cards in the wizard and {2} is replaced with the title-property of the
     * {@link Ext.ux.Wiz.Card}
     * @type String
     */
    stepText : "Step {0} of {1}: {2}",

    /**
     * @cfg {Object} autoEl The element markup used to render this component.
     */
    autoEl : {
        tag : 'div',
        cls      : 'ext-ux-wiz-Header',
        children : [{
            tag  : 'div',
                children : [{
                tag      : 'div',
                cls      : 'ext-ux-wiz-Header-title'
            }, {
                tag : 'div',
                cls : 'ext-ux-wiz-Header-step'
            }, {
                tag : 'div',
                cls : 'Clear'
            }, {
                tag : 'div',
                cls : 'ext-ux-wiz-Header-stepIndicator-container'
            } ]
        }]
    },

    /**
     * @param {Ext.Element}
     */
    titleEl : null,

    /**
     * @param {Ext.Element}
     */
    stepEl  : null,

    /**
     * @param {Ext.Element}
     */
    imageContainer : null,

    /**
     * @param {Array}
     */
    indicators : null,

    /**
     * @param {Ext.Template}
     */
    stepTemplate : null,

    /**
     * @param {Number} lastActiveStep Stores the index of the last active card that
     * was shown-
     */
    lastActiveStep : -1,

// -------- helper
    /**
     * Gets called by  {@link Ext.ux.Wiz#onCardShow()} and updates the header
     * with the approppriate information, such as the progress of the wizard
     * (i.e. which card is being shown etc.)
     *
     * @param {Number} currentStep The index of the card currently shown in
     * the wizard
     * @param {String} title The title-property of the {@link Ext.ux.Wiz.Card}
     *
     * @private
     */
    updateStep : function(currentStep, title)
    {
        var html = this.stepTemplate.apply({
            0 : currentStep+1,
            1 : this.steps,
            2 : title
        });

        this.stepEl.update(html);

        if (this.lastActiveStep != -1) {
            this.indicators[this.lastActiveStep].removeClass('ext-ux-wiz-Header-stepIndicator-active');
        }

        this.indicators[currentStep].addClass('ext-ux-wiz-Header-stepIndicator-active');

        this.lastActiveStep = currentStep;
    },


// -------- listener
    /**
     * Overrides parent implementation to render this component properly.
     */
    onRender : function(ct, position)
    {
        Ext.ux.Wiz.Header.superclass.onRender.call(this, ct, position);

        this.indicators   = [];
        this.stepTemplate = new Ext.Template(this.stepText),
        this.stepTemplate.compile();

        var el = this.el.dom.firstChild;
        //var ns = el.nextSibling;

        this.titleEl        = new Ext.Element(el.firstChild);
        this.stepEl         = new Ext.Element(el.childNodes[1]);
        this.imageContainer = new Ext.Element(el.lastChild);

        this.titleEl.update(this.title);

        var image = null;
        for (var i = 0, len = this.steps; i < len; i++) {
            image = document.createElement('div');
            image.innerHTML = "&#160;";
            image.className = 'ext-ux-wiz-Header-stepIndicator';
            this.indicators[i] = new Ext.Element(image);
            this.imageContainer.appendChild(image);
        }

        image = document.createElement('div');
        image.innerHTML = "&#160;";
        image.className = 'Clear';
        this.imageContainer.appendChild(image);
    }
});


Ext.namespace('Ext.ux.Wiz');

/**
 * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3
 *
 * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
 * @url http://www.siteartwork.de/wizardcomponent
 */

/**
 * @class Ext.ux.Wiz.Card
 * @extends Ext.FormPanel
 *
 * A specific {@link Ext.FormPanel} that can be used as a card in a
 * {@link Ext.ux.Wiz}-component. An instance of this card does only work properly
 * if used in a panel that uses a {@see Ext.layout.CardLayout}-layout.
 *
 * @constructor
 * @param {Object} config The config object
 */
Ext.ux.Wiz.Card = Ext.extend(Ext.FormPanel, {

    /**
     * @cfg {Boolean} header "True" to create the header element. Defaults to
     * "false". See {@link Ext.form.FormPanel#header}
     */
    header : false,

    /**
     * @cfg {Strting} hideMode Hidemode of this component. Defaults to "offsets".
     * See {@link Ext.form.FormPanel#hideMode}
     */
    hideMode : 'display',

    initComponent : function()
    {
        this.addEvents(
            /**
             * @event beforecardhide
             * If you want to add additional checks to your card which cannot be easily done
             * using default validators of input-fields (or using the monitorValid-config option),
             * add your specific listeners to this event.
             * This event gets only fired if the activeItem of the ownerCt-component equals to
             * this instance of {@see Ext.ux.Wiz.Card}. This is needed since a card layout usually
             * hides it's items right after rendering them, involving the beforehide-event.
             * If those checks would be attached to the normal beforehide-event, the card-layout
             * would never be able to hide this component after rendering it, depending on the
             * listeners return value.
             *
             * @param {Ext.ux.Wiz.Card} card The card that triggered the event
             */
            'beforecardhide'
        );

        Ext.ux.Wiz.Card.superclass.initComponent.call(this);
    },

// -------- helper
    isValid : function()
    {
        if (this.monitorValid) {
            return this.bindHandler();
        }

        return true;
    },

// -------- overrides
    /**
     * Overrides parent implementation since we allow to add any element
     * in this component which must not be neccessarily be a form-element.
     * So before a call to "isValid()" is about to be made, this implementation
     * checks first if the specific item sitting in this component has a method "isValid"
     * to prevent errors.
     */
    bindHandler : function()
    {
        if(!this.bound){
            return false; // stops binding
        }
        var valid = true;
        this.form.items.each(function(f){
            if(f.isValid && !f.isValid(true)){
                valid = false;
                return false;
            }
        });
        if(this.buttons){
            for(var i = 0, len = this.buttons.length; i < len; i++){
                var btn = this.buttons[i];
                if(btn.formBind === true && btn.disabled === valid){
                    btn.setDisabled(!valid);
                }
            }
        }
        this.fireEvent('clientvalidation', this, valid);
    },

    /**
     * Overrides parent implementation. This is needed because in case
     * this method uses "monitorValid=true", the method "startMonitoring" must
     * not be called, until the "show"-event of this card fires.
     */
    initEvents : function()
    {
        var old = this.monitorValid;
        this.monitorValid = false;
        Ext.ux.Wiz.Card.superclass.initEvents.call(this);
        this.monitorValid = old;

        this.on('beforehide',     this.bubbleBeforeHideEvent, this);
        this.on('beforecardhide', this.isValid,    this);
        this.on('show',           this.onCardShow, this);
        this.on('hide',           this.onCardHide, this);
    },

// -------- listener
    /**
     * Checks wether the beforecardhide-event may be triggered.
     */
    bubbleBeforeHideEvent : function()
    {
        var ly         = this.ownerCt.layout;
        var activeItem = ly.activeItem;

        if (activeItem && activeItem.id === this.id) {
            return this.fireEvent('beforecardhide', this);
        }

        return true;
    },

    /**
     * Stops monitoring the form elements in this component when the
     * 'hide'-event gets fired.
     */
    onCardHide : function()
    {
        if (this.monitorValid) {
            this.stopMonitoring();
        }
    },

    /**
     * Starts monitoring the form elements in this component when the
     * 'show'-event gets fired.
     */
    onCardShow : function()
    {
        if (this.monitorValid) {
            this.startMonitoring();
        }
    }

});

Ext.grid.CheckColumn = function(config)
{
    Ext.apply(this, config);

    if(!this.id){
        this.id = Ext.id();
    }

    this.renderer = this.renderer.createDelegate(this);
};

Ext.grid.CheckColumn.prototype ={

    init : function(grid)
    {
        this.grid = grid;

        this.grid.on('render', function(){

            var view = this.grid.getView();
            view.mainBody.on('mousedown', this.onMouseDown, this);

        }, this);
    },

    onMouseDown : function(e, t)
    {
        if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){

            e.stopEvent();
            var index = this.grid.getView().findRowIndex(t);
            var record = this.grid.store.getAt(index);
            if (record.get('chkDisabled')) {
                return;
            }
            record.set(this.dataIndex, !record.data[this.dataIndex]);
        }
    },

    renderer : function(v, p, record)
    {
        if (v === null) {
            return '';
        }

        p.css += ' x-grid3-check-col-td';

        return '<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'">&#160;</div>';
    }
};

Ext.grid.GroupSummary = function(config){
    Ext.apply(this, config);
};

Ext.extend(Ext.grid.GroupSummary, Ext.util.Observable, {
    init : function(grid){
        this.grid = grid;
        this.cm = grid.getColumnModel();
        this.view = grid.getView();

        var v = this.view;
        v.doGroupEnd = this.doGroupEnd.createDelegate(this);

        v.afterMethod('onColumnWidthUpdated', this.doWidth, this);
        v.afterMethod('onAllColumnWidthsUpdated', this.doAllWidths, this);
        v.afterMethod('onColumnHiddenUpdated', this.doHidden, this);
        v.afterMethod('onUpdate', this.doUpdate, this);
        v.afterMethod('onRemove', this.doRemove, this);

        if(!this.rowTpl){
            this.rowTpl = new Ext.Template(
                '<div class="x-grid3-summary-row" style="{tstyle}">',
                '<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
                    '<tbody><tr>{cells}</tr></tbody>',
                '</table></div>'
            );
            this.rowTpl.disableFormats = true;
        }
        this.rowTpl.compile();

        if(!this.cellTpl){
            this.cellTpl = new Ext.Template(
                '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',
                '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on">{value}</div>',
                "</td>"
            );
            this.cellTpl.disableFormats = true;
        }
        this.cellTpl.compile();
    },

    toggleSummaries : function(visible){
        var el = this.grid.getGridEl();
        if(el){
            if(visible === undefined){
                visible = el.hasClass('x-grid-hide-summary');
            }
            el[visible ? 'removeClass' : 'addClass']('x-grid-hide-summary');
        }
    },

    renderSummary : function(o, cs){
        cs = cs || this.view.getColumnData();
        var cfg = this.cm.config;

        var buf = [], c, p = {}, cf, last = cs.length-1;
        for(var i = 0, len = cs.length; i < len; i++){
            c = cs[i];
            cf = cfg[i];
            p.id = c.id;
            p.style = c.style;
            p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
            if(cf.summaryType || cf.summaryRenderer){
                p.value = (cf.summaryRenderer || c.renderer)(o.data[c.name], p, o);
            }else{
                p.value = '';
            }
            if(p.value == undefined || p.value === "") p.value = "&#160;";
            buf[buf.length] = this.cellTpl.apply(p);
        }

        return this.rowTpl.apply({
            tstyle: 'width:'+this.view.getTotalWidth()+';',
            cells: buf.join('')
        });
    },

    calculate : function(rs, cs){
        var data = {}, r, c, cfg = this.cm.config, cf;
        for(var j = 0, jlen = rs.length; j < jlen; j++){
            r = rs[j];
            for(var i = 0, len = cs.length; i < len; i++){
                c = cs[i];
                cf = cfg[i];
                if(cf.summaryType){
                    data[c.name] = Ext.grid.GroupSummary.Calculations[cf.summaryType](data[c.name] || 0, r, c.name, data);
                }
            }
        }
        return data;
    },

    doGroupEnd : function(buf, g, cs, ds, colCount){
        var data = this.calculate(g.rs, cs);
        buf.push('</div>', this.renderSummary({data: data}, cs), '</div>');
    },

    doWidth : function(col, w, tw){
        var gs = this.view.getGroups(), s;
        for(var i = 0, len = gs.length; i < len; i++){
            s = gs[i].childNodes[2];
            s.style.width = tw;
            s.firstChild.style.width = tw;
            s.firstChild.rows[0].childNodes[col].style.width = w;
        }
    },

    doAllWidths : function(ws, tw){
        var gs = this.view.getGroups(), s, cells, wlen = ws.length;
        for(var i = 0, len = gs.length; i < len; i++){
            s = gs[i].childNodes[2];
            s.style.width = tw;
            s.firstChild.style.width = tw;
            cells = s.firstChild.rows[0].childNodes;
            for(var j = 0; j < wlen; j++){
                cells[j].style.width = ws[j];
            }
        }
    },

    doHidden : function(col, hidden, tw){
        var gs = this.view.getGroups(), s, display = hidden ? 'none' : '';
        for(var i = 0, len = gs.length; i < len; i++){
            s = gs[i].childNodes[2];
            s.style.width = tw;
            s.firstChild.style.width = tw;
            s.firstChild.rows[0].childNodes[col].style.display = display;
        }
    },

    // Note: requires that all (or the first) record in the
    // group share the same group value. Returns false if the group
    // could not be found.
    refreshSummary : function(groupValue){
        return this.refreshSummaryById(this.view.getGroupId(groupValue));
    },

    getSummaryNode : function(gid){
        var g = Ext.fly(gid, '_gsummary');
        if(g){
            return g.down('.x-grid3-summary-row', true);
        }
        return null;
    },

    refreshSummaryById : function(gid){
        var g = document.getElementById(gid);
        if(!g){
            return false;
        }
        var rs = [];
        this.grid.store.each(function(r){
            if(r._groupId == gid){
                rs[rs.length] = r;
            }
        });
        var cs = this.view.getColumnData();
        var data = this.calculate(rs, cs);
        var markup = this.renderSummary({data: data}, cs);

        var existing = this.getSummaryNode(gid);
        if(existing){
            g.removeChild(existing);
        }
        Ext.DomHelper.append(g, markup);
        return true;
    },

    doUpdate : function(ds, record){
        this.refreshSummaryById(record._groupId);
    },

    doRemove : function(ds, record, index, isUpdate){
        if(!isUpdate){
            this.refreshSummaryById(record._groupId);
        }
    },

    showSummaryMsg : function(groupValue, msg){
        var gid = this.view.getGroupId(groupValue);
        var node = this.getSummaryNode(gid);
        if(node){
            node.innerHTML = '<div class="x-grid3-summary-msg">' + msg + '</div>';
        }
    }
});

Ext.grid.GroupSummary.Calculations = {
    'sum' : function(v, record, field){
        v = parseFloat(v);
        return v + (parseFloat(record.data[field])||0);
    },

    'count' : function(v, record, field, data){
        return data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1);
    },

    'max' : function(v, record, field, data){
        var v = record.data[field];
        var max = data[field+'max'] === undefined ? (data[field+'max'] = v) : data[field+'max'];
        return v > max ? (data[field+'max'] = v) : max;
    },

    'min' : function(v, record, field, data){
        var v = record.data[field];
        var min = data[field+'min'] === undefined ? (data[field+'min'] = v) : data[field+'min'];
        return v < min ? (data[field+'min'] = v) : min;
    },

    'average' : function(v, record, field, data){
        var c = data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1);
        var t = (data[field+'total'] = ((data[field+'total']||0) + (record.data[field]||0)));
        return t === 0 ? 0 : t / c;
    }
}

Ext.grid.HybridSummary = Ext.extend(Ext.grid.GroupSummary, {
    calculate : function(rs, cs){
        var gcol = this.view.getGroupField();
        var gvalue = rs[0].data[gcol];
        var gdata = this.getSummaryData(gvalue);
        return gdata || Ext.grid.HybridSummary.superclass.calculate.call(this, rs, cs);
    },

    updateSummaryData : function(groupValue, data, skipRefresh){
        var json = this.grid.store.reader.jsonData;
        if(!json.summaryData){
            json.summaryData = {};
        }
        json.summaryData[groupValue] = data;
        if(!skipRefresh){
            this.refreshSummary(groupValue);
        }
    },

    getSummaryData : function(groupValue){
        var json = this.grid.store.reader.jsonData;
        if(json && json.summaryData){
            return json.summaryData[groupValue];
        }
        return null;
    }
});

Ext.menu.Button = Ext.extend(Ext.menu.Item,
{
    onRender: function(container, position)
    {
        var config = new cloneObject(this.initialConfig);
        config.minWidth = container.getWidth();
        var button = new Ext.Button(config);
        button.render(container, position);
        this.el = button.el;
    }
});
