﻿//TabCloseMenu
Ext.ux.TabCloseMenu = function(){
    var tabs, menu, ctxItem;
    this.init = function(tp){
        tabs = tp;
        tabs.on('contextmenu', onContextMenu);
    }

    function onContextMenu(ts, item, e){
        if(!menu){ // create context menu on first right click
            menu = new Ext.menu.Menu([{
                id: tabs.id + '-close',
                iconCls:'i-tab-close',
                text: '关闭',
                handler : function(){
                    tabs.remove(ctxItem);
                }
            },{
                id: tabs.id + '-close-others',
                iconCls:'i-tab-close',
                text: '除此之外全部关闭',
                handler : function(){
                    tabs.items.each(function(item){
                        if(item.closable && item != ctxItem){
                            tabs.remove(item);
                        }
                    });
                }
            },{ 
                id: tabs.id + '-close-all', 
                iconCls:'i-tab-close',
                text: '关闭全部标签', 
                handler : function(){ 
                    tabs.items.each(function(item){ 
                        if(item.closable){ 
                            tabs.remove(item); 
                        } 
                    }); 
                } 
            }]);
        }
        ctxItem = item;
        var items = menu.items;
        items.get(tabs.id + '-close').setDisabled(!item.closable);
        var disableOthers = true;
        tabs.items.each(function(){
            if(this != item && this.closable){
                disableOthers = false;
                return false;
            }
        });
        items.get(tabs.id + '-close-others').setDisabled(disableOthers);
        menu.showAt(e.getPoint());
    }
};

//Spinner
//数字
Ext.ux.Spinner = Ext.extend(Ext.util.Observable, {
    incrementValue: 1,
    alternateIncrementValue: 5,
    triggerClass: 'x-form-spinner-trigger',
    splitterClass: 'x-form-spinner-splitter',
    alternateKey: Ext.EventObject.shiftKey,
    defaultValue: 0,
    accelerate: false,

    constructor: function(config){
        Ext.ux.Spinner.superclass.constructor.call(this, config);
        Ext.apply(this, config);
        this.mimicing = false;
    },

    init: function(field){
        this.field = field;

        field.afterMethod('onRender', this.doRender, this);
        field.afterMethod('onEnable', this.doEnable, this);
        field.afterMethod('onDisable', this.doDisable, this);
        field.afterMethod('afterRender', this.doAfterRender, this);
        field.afterMethod('onResize', this.doResize, this);
        field.afterMethod('onFocus', this.doFocus, this);
        field.beforeMethod('onDestroy', this.doDestroy, this);
    },

    doRender: function(ct, position){
        var el = this.el = this.field.getEl();
        var f = this.field;

        if (!f.wrap) {
            f.wrap = this.wrap = el.wrap({
                cls: "x-form-field-wrap"
            });
        }
        else {
            this.wrap = f.wrap.addClass('x-form-field-wrap');
        }

        this.trigger = this.wrap.createChild({
            tag: "img",
            src: Ext.BLANK_IMAGE_URL,
            cls: "x-form-trigger " + this.triggerClass
        });

        if (!f.width) {
            this.wrap.setWidth(el.getWidth() + this.trigger.getWidth());
        }

        this.splitter = this.wrap.createChild({
            tag: 'div',
            cls: this.splitterClass,
            style: 'width:13px; height:2px;'
        });
        this.splitter.setRight((Ext.isIE) ? 1 : 2).setTop(10).show();

        this.proxy = this.trigger.createProxy('', this.splitter, true);
        this.proxy.addClass("x-form-spinner-proxy");
        this.proxy.setStyle('left', '0px');
        this.proxy.setSize(14, 1);
        this.proxy.hide();
        this.dd = new Ext.dd.DDProxy(this.splitter.dom.id, "SpinnerDrag", {
            dragElId: this.proxy.id
        });

        this.initTrigger();
        this.initSpinner();
    },

    doAfterRender: function(){
        var y;
        if (Ext.isIE && this.el.getY() != (y = this.trigger.getY())) {
            this.el.position();
            this.el.setY(y);
        }
    },

    doEnable: function(){
        if (this.wrap) {
            this.wrap.removeClass(this.field.disabledClass);
        }
    },

    doDisable: function(){
        if (this.wrap) {
            this.wrap.addClass(this.field.disabledClass);
            this.el.removeClass(this.field.disabledClass);
        }
    },

    doResize: function(w, h){
        if (typeof w == 'number') {
            this.el.setWidth(this.field.adjustWidth('input', w - this.trigger.getWidth()));
        }
        this.wrap.setWidth(this.el.getWidth() + this.trigger.getWidth());
    },

    doFocus: function(){
        if (!this.mimicing) {
            this.wrap.addClass('x-trigger-wrap-focus');
            this.mimicing = true;
            Ext.get(Ext.isIE ? document.body : document).on("mousedown", this.mimicBlur, this, {
                delay: 10
            });
            this.el.on('keydown', this.checkTab, this);
        }
    },

    // private
    checkTab: function(e){
        if (e.getKey() == e.TAB) {
            this.triggerBlur();
        }
    },

    // private
    mimicBlur: function(e){
        if (!this.wrap.contains(e.target) && this.field.validateBlur(e)) {
            this.triggerBlur();
        }
    },

    // private
    triggerBlur: function(){
        this.mimicing = false;
        Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this);
        this.el.un("keydown", this.checkTab, this);
        this.field.beforeBlur();
        this.wrap.removeClass('x-trigger-wrap-focus');
        this.field.onBlur.call(this.field);
    },

    initTrigger: function(){
        this.trigger.addClassOnOver('x-form-trigger-over');
        this.trigger.addClassOnClick('x-form-trigger-click');
    },

    initSpinner: function(){
        this.field.addEvents({
            'spin': true,
            'spinup': true,
            'spindown': true
        });

        this.keyNav = new Ext.KeyNav(this.el, {
            "up": function(e){
                e.preventDefault();
                this.onSpinUp();
            },

            "down": function(e){
                e.preventDefault();
                this.onSpinDown();
            },

            "pageUp": function(e){
                e.preventDefault();
                this.onSpinUpAlternate();
            },

            "pageDown": function(e){
                e.preventDefault();
                this.onSpinDownAlternate();
            },

            scope: this
        });

        this.repeater = new Ext.util.ClickRepeater(this.trigger, {
            accelerate: this.accelerate
        });
        this.field.mon(this.repeater, "click", this.onTriggerClick, this, {
            preventDefault: true
        });

        this.field.mon(this.trigger, {
            mouseover: this.onMouseOver,
            mouseout: this.onMouseOut,
            mousemove: this.onMouseMove,
            mousedown: this.onMouseDown,
            mouseup: this.onMouseUp,
            scope: this,
            preventDefault: true
        });

        this.field.mon(this.wrap, "mousewheel", this.handleMouseWheel, this);

        this.dd.setXConstraint(0, 0, 10)
        this.dd.setYConstraint(1500, 1500, 10);
        this.dd.endDrag = this.endDrag.createDelegate(this);
        this.dd.startDrag = this.startDrag.createDelegate(this);
        this.dd.onDrag = this.onDrag.createDelegate(this);
    },

    onMouseOver: function(){
        if (this.disabled) {
            return;
        }
        var middle = this.getMiddle();
        this.tmpHoverClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown';
        this.trigger.addClass(this.tmpHoverClass);
    },

    //private
    onMouseOut: function(){
        this.trigger.removeClass(this.tmpHoverClass);
    },

    //private
    onMouseMove: function(){
        if (this.disabled) {
            return;
        }
        var middle = this.getMiddle();
        if (((Ext.EventObject.getPageY() > middle) && this.tmpHoverClass == "x-form-spinner-overup") ||
        ((Ext.EventObject.getPageY() < middle) && this.tmpHoverClass == "x-form-spinner-overdown")) {
        }
    },

    //private
    onMouseDown: function(){
        if (this.disabled) {
            return;
        }
        var middle = this.getMiddle();
        this.tmpClickClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown';
        this.trigger.addClass(this.tmpClickClass);
    },

    //private
    onMouseUp: function(){
        this.trigger.removeClass(this.tmpClickClass);
    },

    //private
    onTriggerClick: function(){
        if (this.disabled || this.el.dom.readOnly) {
            return;
        }
        var middle = this.getMiddle();
        var ud = (Ext.EventObject.getPageY() < middle) ? 'Up' : 'Down';
        this['onSpin' + ud]();
    },

    //private
    getMiddle: function(){
        var t = this.trigger.getTop();
        var h = this.trigger.getHeight();
        var middle = t + (h / 2);
        return middle;
    },

    //private
    //checks if control is allowed to spin
    isSpinnable: function(){
        if (this.disabled || this.el.dom.readOnly) {
            Ext.EventObject.preventDefault(); //prevent scrolling when disabled/readonly
            return false;
        }
        return true;
    },

    handleMouseWheel: function(e){
        //disable scrolling when not focused
        if (this.wrap.hasClass('x-trigger-wrap-focus') == false) {
            return;
        }

        var delta = e.getWheelDelta();
        if (delta > 0) {
            this.onSpinUp();
            e.stopEvent();
        }
        else
            if (delta < 0) {
                this.onSpinDown();
                e.stopEvent();
            }
    },

    //private
    startDrag: function(){
        this.proxy.show();
        this._previousY = Ext.fly(this.dd.getDragEl()).getTop();
    },

    //private
    endDrag: function(){
        this.proxy.hide();
    },

    //private
    onDrag: function(){
        if (this.disabled) {
            return;
        }
        var y = Ext.fly(this.dd.getDragEl()).getTop();
        var ud = '';

        if (this._previousY > y) {
            ud = 'Up';
        } //up
        if (this._previousY < y) {
            ud = 'Down';
        } //down
        if (ud != '') {
            this['onSpin' + ud]();
        }

        this._previousY = y;
    },

    //private
    onSpinUp: function(){
        if (this.isSpinnable() == false) {
            return;
        }
        if (Ext.EventObject.shiftKey == true) {
            this.onSpinUpAlternate();
            return;
        }
        else {
            this.spin(false, false);
        }
        this.field.fireEvent("spin", this);
        this.field.fireEvent("spinup", this);
    },

    //private
    onSpinDown: function(){
        if (this.isSpinnable() == false) {
            return;
        }
        if (Ext.EventObject.shiftKey == true) {
            this.onSpinDownAlternate();
            return;
        }
        else {
            this.spin(true, false);
        }
        this.field.fireEvent("spin", this);
        this.field.fireEvent("spindown", this);
    },

    //private
    onSpinUpAlternate: function(){
        if (this.isSpinnable() == false) {
            return;
        }
        this.spin(false, true);
        this.field.fireEvent("spin", this);
        this.field.fireEvent("spinup", this);
    },

    //private
    onSpinDownAlternate: function(){
        if (this.isSpinnable() == false) {
            return;
        }
        this.spin(true, true);
        this.field.fireEvent("spin", this);
        this.field.fireEvent("spindown", this);
    },

    spin: function(down, alternate){
        var v = parseFloat(this.field.getValue());
        var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue;
        (down == true) ? v -= incr : v += incr;

        v = (isNaN(v)) ? this.defaultValue : v;
        v = this.fixBoundries(v);
        this.field.setRawValue(v);
    },

    fixBoundries: function(value){
        var v = value;

        if (this.field.minValue != undefined && v < this.field.minValue) {
            v = this.field.minValue;
        }
        if (this.field.maxValue != undefined && v > this.field.maxValue) {
            v = this.field.maxValue;
        }

        return this.fixPrecision(v);
    },

    // private
    fixPrecision: function(value){
        var nan = isNaN(value);
        if (!this.field.allowDecimals || this.field.decimalPrecision == -1 || nan || !value) {
            return nan ? '' : value;
        }
        return parseFloat(parseFloat(value).toFixed(this.field.decimalPrecision));
    },

    doDestroy: function(){
        if (this.trigger) {
            this.trigger.remove();
        }
        if (this.wrap) {
            this.wrap.remove();
            delete this.field.wrap;
        }

        if (this.splitter) {
            this.splitter.remove();
        }

        if (this.dd) {
            this.dd.unreg();
            this.dd = null;
        }

        if (this.proxy) {
            this.proxy.remove();
        }

        if (this.repeater) {
            this.repeater.purgeListeners();
        }
    }
});
Ext.form.Spinner = Ext.ux.Spinner;
Ext.ns('Ext.ux.form');
Ext.ux.form.SpinnerField = Ext.extend(Ext.form.NumberField, {
    deferHeight: true,
    autoSize: Ext.emptyFn,
    onBlur: Ext.emptyFn,
    adjustSize: Ext.BoxComponent.prototype.adjustSize,

	constructor: function(config) {
		var spinnerConfig = Ext.copyTo({}, config, 'incrementValue,alternateIncrementValue,accelerate,defaultValue,triggerClass,splitterClass');

		var spl = this.spinner = new Ext.ux.Spinner(spinnerConfig);

		var plugins = config.plugins
			? (Ext.isArray(config.plugins)
				? config.plugins.push(spl)
				: [config.plugins, spl])
			: spl;

		Ext.ux.form.SpinnerField.superclass.constructor.call(this, Ext.apply(config, {plugins: plugins}));
	},

    onShow: function(){
        if (this.wrap) {
            this.wrap.dom.style.display = '';
            this.wrap.dom.style.visibility = 'visible';
        }
    },

    onHide: function(){
        this.wrap.dom.style.display = 'none';
    },

    // private
    getResizeEl: function(){
        return this.wrap;
    },

    // private
    getPositionEl: function(){
        return this.wrap;
    },

    // private
    alignErrorIcon: function(){
        if (this.wrap) {
            this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
        }
    },

    validateBlur: function(){
        return true;
    }
});

Ext.reg('spinnerfield', Ext.ux.form.SpinnerField);

//backwards compat
Ext.form.SpinnerField = Ext.ux.form.SpinnerField;

Ext.ns('Ext.ux.grid');
Ext.ux.grid.RowEditor = Ext.extend(Ext.Panel, {
    floating: true,
    shadow: false,
    layout: 'hbox',
    cls: 'x-small-editor',
    buttonAlign: 'center',
    baseCls: 'x-row-editor',
    elements: 'header,footer,body',
    frameWidth: 5,
    buttonPad: 3,
    clicksToEdit: 'auto',
    monitorValid: true,
    focusDelay: 250,
    errorSummary: true,

    defaults: {
        normalWidth: true
    },

    initComponent: function(){
        Ext.ux.grid.RowEditor.superclass.initComponent.call(this);
        this.addEvents(
            'beforeedit',
            'validateedit',
            'afteredit'
        );
    },

    init: function(grid){
        this.grid = grid;
        this.ownerCt = grid;
        if(this.clicksToEdit === 2){
            grid.on('rowdblclick', this.onRowDblClick, this);
        }else{
            grid.on('rowclick', this.onRowClick, this);
            if(Ext.isIE){
                grid.on('rowdblclick', this.onRowDblClick, this);
            }
        }
        grid.getStore().on('remove', function() {
            this.stopEditing(false);
        },this);

        grid.on({
            scope: this,
            keydown: this.onGridKey,
            columnresize: this.verifyLayout,
            columnmove: this.refreshFields,
            reconfigure: this.refreshFields,
	    destroy : this.destroy,
            bodyscroll: {
                buffer: 250,
                fn: this.positionButtons
            }
        });
        grid.getColumnModel().on('hiddenchange', this.verifyLayout, this, {delay:1});
        grid.getView().on('refresh', this.stopEditing.createDelegate(this, []));
    },

    refreshFields: function(){
        this.initFields();
        this.verifyLayout();
    },

    isDirty: function(){
        var dirty;
        this.items.each(function(f){
            if(String(this.values[f.id]) !== String(f.getValue())){
                dirty = true;
                return false;
            }
        }, this);
        return dirty;
    },

    startEditing: function(rowIndex, doFocus){
        if(this.editing && this.isDirty()){
            this.showTooltip('You need to commit or cancel your changes');
            return;
        }
        this.editing = true;
        if(typeof rowIndex == 'object'){
            rowIndex = this.grid.getStore().indexOf(rowIndex);
        }
        if(this.fireEvent('beforeedit', this, rowIndex) !== false){
            var g = this.grid, view = g.getView();
            var row = view.getRow(rowIndex);
            var record = g.store.getAt(rowIndex);
            this.record = record;
            this.rowIndex = rowIndex;
            this.values = {};
            if(!this.rendered){
                this.render(view.getEditorParent());
            }
            var w = Ext.fly(row).getWidth();
            this.setSize(w);
            if(!this.initialized){
                this.initFields();
            }
            var cm = g.getColumnModel(), fields = this.items.items, f, val;
            for(var i = 0, len = cm.getColumnCount(); i < len; i++){
                val = this.preEditValue(record, cm.getDataIndex(i));
                f = fields[i];
                f.setValue(val);
                this.values[f.id] = val || '';
            }
            this.verifyLayout(true);
            if(!this.isVisible()){
                this.setPagePosition(Ext.fly(row).getXY());
            } else{
                this.el.setXY(Ext.fly(row).getXY(), {duration:0.15});
            }
            if(!this.isVisible()){
                this.show().doLayout();
            }
            if(doFocus !== false){
                this.doFocus.defer(this.focusDelay, this);
            }
        }
    },

    stopEditing : function(saveChanges){
        this.editing = false;
        if(!this.isVisible()){
            return;
        }
        if(saveChanges === false || !this.isValid()){
            this.hide();
            return;
        }
        var changes = {}, r = this.record, hasChange = false;
        var cm = this.grid.colModel, fields = this.items.items;
        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
            if(!cm.isHidden(i)){
                var dindex = cm.getDataIndex(i);
                if(!Ext.isEmpty(dindex)){
                    var oldValue = r.data[dindex];
                    var value = this.postEditValue(fields[i].getValue(), oldValue, r, dindex);
                    if(String(oldValue) !== String(value)){
                        changes[dindex] = value;
                        hasChange = true;
                    }
                }
            }
        }
        if(hasChange && this.fireEvent('validateedit', this, changes, r, this.rowIndex) !== false){
            r.beginEdit();
            for(var k in changes){
                if(changes.hasOwnProperty(k)){
                    r.set(k, changes[k]);
                }
            }
            r.endEdit();
            this.fireEvent('afteredit', this, changes, r, this.rowIndex);
        }
        this.hide();
    },

    verifyLayout: function(force){
        if(this.el && (this.isVisible() || force === true)){
            var row = this.grid.getView().getRow(this.rowIndex);
            this.setSize(Ext.fly(row).getWidth(), Ext.isIE ? Ext.fly(row).getHeight() + (Ext.isBorderBox ? 9 : 0) : undefined);
            var cm = this.grid.colModel, fields = this.items.items;
            for(var i = 0, len = cm.getColumnCount(); i < len; i++){
                if(!cm.isHidden(i)){
                    var adjust = 0;
                    if(i === 0){
                        adjust += 0; // outer padding
                    }
                    if(i === (len - 1)){
                        adjust += 3; // outer padding
                    } else{
                        adjust += 1;
                    }
                    fields[i].show();
                    fields[i].setWidth(cm.getColumnWidth(i) - adjust);
                } else{
                    fields[i].hide();
                }
            }
            this.doLayout();
            this.positionButtons();
        }
    },

    slideHide : function(){
        this.hide();
    },

    initFields: function(){
        var cm = this.grid.getColumnModel(), pm = Ext.layout.ContainerLayout.prototype.parseMargins;
        this.removeAll(false);
        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
            var c = cm.getColumnAt(i);
            var ed = c.getEditor();
            if(!ed){
                ed = c.displayEditor || new Ext.form.DisplayField();
            }
            if(i == 0){
                ed.margins = pm('0 1 2 1');
            } else if(i == len - 1){
                ed.margins = pm('0 0 2 1');
            } else{
                ed.margins = pm('0 1 2');
            }
            ed.setWidth(cm.getColumnWidth(i));
            ed.column = c;
            if(ed.ownerCt !== this){
                ed.on('focus', this.ensureVisible, this);
                ed.on('specialkey', this.onKey, this);
            }
            this.insert(i, ed);
        }
        this.initialized = true;
    },

    onKey: function(f, e){
        if(e.getKey() === e.ENTER){
            this.stopEditing(true);
            e.stopPropagation();
        }
    },

    onGridKey: function(e){
        if(e.getKey() === e.ENTER && !this.isVisible()){
            var r = this.grid.getSelectionModel().getSelected();
            if(r){
                var index = this.grid.store.indexOf(r);
                this.startEditing(index);
                e.stopPropagation();
            }
        }
    },

    ensureVisible: function(editor){
        if(this.isVisible()){
             this.grid.getView().ensureVisible(this.rowIndex, this.grid.colModel.getIndexById(editor.column.id), true);
        }
    },

    onRowClick: function(g, rowIndex, e){
        if(this.clicksToEdit == 'auto'){
            var li = this.lastClickIndex;
            this.lastClickIndex = rowIndex;
            if(li != rowIndex && !this.isVisible()){
                return;
            }
        }
        this.startEditing(rowIndex, false);
        this.doFocus.defer(this.focusDelay, this, [e.getPoint()]);
    },

    onRowDblClick: function(g, rowIndex, e){
        this.startEditing(rowIndex, false);
        this.doFocus.defer(this.focusDelay, this, [e.getPoint()]);
    },

    onRender: function(){
        Ext.ux.grid.RowEditor.superclass.onRender.apply(this, arguments);
        this.el.swallowEvent(['keydown', 'keyup', 'keypress']);
        this.btns = new Ext.Panel({
            baseCls: 'x-plain',
            cls: 'x-btns',
            elements:'body',
            layout: 'table',
            width: (this.minButtonWidth * 2) + (this.frameWidth * 2) + (this.buttonPad * 4), // width must be specified for IE
            items: [{
                ref: 'saveBtn',
                itemId: 'saveBtn',
                xtype: 'button',
                text: this.saveText || 'Save',
                width: this.minButtonWidth,
                handler: this.stopEditing.createDelegate(this, [true])
            }, {
                xtype: 'button',
                text: this.cancelText || 'Cancel',
                width: this.minButtonWidth,
                handler: this.stopEditing.createDelegate(this, [false])
            }]
        });
        this.btns.render(this.bwrap);
    },

    afterRender: function(){
        Ext.ux.grid.RowEditor.superclass.afterRender.apply(this, arguments);
        this.positionButtons();
        if(this.monitorValid){
            this.startMonitoring();
        }
    },

    onShow: function(){
        if(this.monitorValid){
            this.startMonitoring();
        }
        Ext.ux.grid.RowEditor.superclass.onShow.apply(this, arguments);
    },

    onHide: function(){
        Ext.ux.grid.RowEditor.superclass.onHide.apply(this, arguments);
        this.stopMonitoring();
        this.grid.getView().focusRow(this.rowIndex);
    },

    positionButtons: function(){
        if(this.btns){
            var h = this.el.dom.clientHeight;
            var view = this.grid.getView();
            var scroll = view.scroller.dom.scrollLeft;
            var width =  view.mainBody.getWidth();
            var bw = this.btns.getWidth();
            this.btns.el.shift({left: (width/2)-(bw/2)+scroll, top: h - 2, stopFx: true, duration:0.2});
        }
    },

    // private
    preEditValue : function(r, field){
        var value = r.data[field];
        return this.autoEncode && typeof value === 'string' ? Ext.util.Format.htmlDecode(value) : value;
    },

    // private
    postEditValue : function(value, originalValue, r, field){
        return this.autoEncode && typeof value == 'string' ? Ext.util.Format.htmlEncode(value) : value;
    },

    doFocus: function(pt){
        if(this.isVisible()){
            var index = 0;
            if(pt){
                index = this.getTargetColumnIndex(pt);
            }
            var cm = this.grid.getColumnModel();
            for(var i = index||0, len = cm.getColumnCount(); i < len; i++){
                var c = cm.getColumnAt(i);
                if(!c.hidden && c.getEditor()){
                    c.getEditor().focus();
                    break;
                }
            }
        }
    },

    getTargetColumnIndex: function(pt){
        var grid = this.grid, v = grid.view;
        var x = pt.left;
        var cms = grid.colModel.config;
        var i = 0, match = false;
        for(var len = cms.length, c; c = cms[i]; i++){
            if(!c.hidden){
                if(Ext.fly(v.getHeaderCell(i)).getRegion().right >= x){
                    match = i;
                    break;
                }
            }
        }
        return match;
    },

    startMonitoring : function(){
        if(!this.bound && this.monitorValid){
            this.bound = true;
            Ext.TaskMgr.start({
                run : this.bindHandler,
                interval : this.monitorPoll || 200,
                scope: this
            });
        }
    },

    stopMonitoring : function(){
        this.bound = false;
        if(this.tooltip){
            this.tooltip.hide();
        }
    },

    isValid: function(){
        var valid = true;
        this.items.each(function(f){
            if(!f.isValid(true)){
                valid = false;
                return false;
            }
        });
        return valid;
    },

    // private
    bindHandler : function(){
        if(!this.bound){
            return false; // stops binding
        }
        var valid = this.isValid();
        if(!valid && this.errorSummary){
            this.showTooltip(this.getErrorText().join(''));
        }
        this.btns.saveBtn.setDisabled(!valid);
        this.fireEvent('validation', this, valid);
    },

    showTooltip: function(msg){
        var t = this.tooltip;
        if(!t){
            t = this.tooltip = new Ext.ToolTip({
                maxWidth: 600,
                cls: 'errorTip',
                width: 300,
                title: 'Errors',
                autoHide: false,
                anchor: 'left',
                anchorToTarget: true,
                mouseOffset: [40,0]
            });
        }
        t.initTarget(this.items.last().getEl());
        if(!t.rendered){
            t.show();
            t.hide();
        }
        t.body.update(msg);
        t.doAutoWidth();
        t.show();
    },

    getErrorText: function(){
        var data = ['<ul>'];
        this.items.each(function(f){
            if(!f.isValid(true)){
                data.push('<li>', f.activeError, '</li>');
            }
        });
        data.push('</ul>');
        return data;
    }
});
Ext.preg('roweditor', Ext.ux.grid.RowEditor);

Ext.override(Ext.form.Field, {
    markInvalid : function(msg){
        if(!this.rendered || this.preventMark){ // not rendered
            return;
        }
        msg = msg || this.invalidText;

        var mt = this.getMessageHandler();
        if(mt){
            mt.mark(this, msg);
        }else if(this.msgTarget){
            this.el.addClass(this.invalidClass);
            var t = Ext.getDom(this.msgTarget);
            if(t){
                t.innerHTML = msg;
                t.style.display = this.msgDisplay;
            }
        }
        this.activeError = msg;
        this.fireEvent('invalid', this, msg);
    }
});

Ext.override(Ext.ToolTip, {
    doAutoWidth : function(){
        var bw = this.body.getTextWidth();
        if(this.title){
            bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));
        }
        bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr") + 20;
        this.setWidth(bw.constrain(this.minWidth, this.maxWidth));

        // IE7 repaint bug on initial show
        if(Ext.isIE7 && !this.repainted){
            this.el.repaint();
            this.repainted = true;
        }
    }
});


/*!
 * Ext JS Library 3.0.0
 * Copyright(c) 2006-2009 Ext JS, LLC
 * licensing@extjs.com
 * http://www.extjs.com/license
 */

/* Fix for Opera, which does not seem to include the map function on Array's */
if (!Array.prototype.map) {
    Array.prototype.map = function(fun){
        var len = this.length;
        if (typeof fun != 'function') {
            throw new TypeError();
        }
        var res = new Array(len);
        var thisp = arguments[1];
        for (var i = 0; i < len; i++) {
            if (i in this) {
                res[i] = fun.call(thisp, this[i], i, this);
            }
        }
        return res;
    };
}

Ext.ns('Ext.ux.data');

/**
 * @class Ext.ux.data.PagingMemoryProxy
 * @extends Ext.data.MemoryProxy
 * <p>Paging Memory Proxy, allows to use paging grid with in memory dataset</p>
 */
Ext.ux.data.PagingMemoryProxy = Ext.extend(Ext.data.MemoryProxy, {
    constructor : function(data){
        Ext.ux.data.PagingMemoryProxy.superclass.constructor.call(this);
        this.data = data;
    },
    doRequest : function(action, rs, params, reader, callback, scope, options){
        params = params ||
        {};
        var result;
        try {
            result = reader.readRecords(this.data);
        } 
        catch (e) {
            this.fireEvent('loadexception', this, options, null, e);
            callback.call(scope, null, options, false);
            return;
        }
        
        // filtering
        if (params.filter !== undefined) {
            result.records = result.records.filter(function(el){
                if (typeof(el) == 'object') {
                    var att = params.filterCol || 0;
                    return String(el.data[att]).match(params.filter) ? true : false;
                }
                else {
                    return String(el).match(params.filter) ? true : false;
                }
            });
            result.totalRecords = result.records.length;
        }
        
        // sorting
        if (params.sort !== undefined) {
            // use integer as params.sort to specify column, since arrays are not named
            // params.sort=0; would also match a array without columns
            var dir = String(params.dir).toUpperCase() == 'DESC' ? -1 : 1;
            var fn = function(r1, r2){
                return r1 < r2;
            };
            result.records.sort(function(a, b){
                var v = 0;
                if (typeof(a) == 'object') {
                    v = fn(a.data[params.sort], b.data[params.sort]) * dir;
                }
                else {
                    v = fn(a, b) * dir;
                }
                if (v == 0) {
                    v = (a.index < b.index ? -1 : 1);
                }
                return v;
            });
        }
        // paging (use undefined cause start can also be 0 (thus false))
        if (params.start !== undefined && params.limit !== undefined) {
            result.records = result.records.slice(params.start, params.start + params.limit);
        }
        callback.call(scope, result, options, true);
    }
});

//backwards compat.
Ext.data.PagingMemoryProxy = Ext.ux.data.PagingMemoryProxy;



/*--------------------------------------------------------------------------------------*/

Ext.ns('Ext.ux.grid');  
  
  
  
/** 
 
 * @class Ext.ux.grid.Search 
 
 * @extends Ext.util.Observable 
 
 * @param {Object} config configuration object 
 
 * @constructor 
 
 */  
  
Ext.ux.grid.Search = function(config) {  
  
    Ext.apply(this, config);  
  
    Ext.ux.grid.Search.superclass.constructor.call(this);  
  
}; // eo constructor  
  
  
  
Ext.extend(Ext.ux.grid.Search, Ext.util.Observable, {  
  
    /** 
 
     * @cfg {String} searchText Text to display on menu button 
 
     */  
  
     searchText:'Search'  
  
  
  
    /** 
 
     * @cfg {String} searchTipText Text to display as input tooltip. Set to '' for no tooltip 
 
     */   
  
    ,searchTipText:'Type a text to search and press Enter'  
  
  
  
    /** 
 
     * @cfg {String} selectAllText Text to display on menu item that selects all fields 
 
     */  
  
    ,selectAllText:'选择全部'  
  
  
  
    /** 
 
     * @cfg {String} position Where to display the search controls. Valid values are top and bottom (defaults to bottom) 
 
     * Corresponding toolbar has to exist at least with mimimum configuration tbar:[] for position:top or bbar:[] 
 
     * for position bottom. Plugin does NOT create any toolbar. 
 
     */  
  
    ,position:'bottom'  
  
  
  
    /** 
 
     * @cfg {String} iconCls Icon class for menu button (defaults to icon-magnifier) 
 
     */  
  
    ,iconCls:'icon-magnifier'  
  
  
  
    /** 
 
     * @cfg {String/Array} checkIndexes Which indexes to check by default. Can be either 'all' for all indexes 
 
     * or array of dataIndex names, e.g. ['persFirstName', 'persLastName'] 
 
     */  
  
    ,checkIndexes:'all'  
  
  
  
    /** 
 
     * @cfg {Array} disableIndexes Array of index names to disable (not show in the menu), e.g. ['persTitle', 'persTitle2'] 
 
     */  
  
    ,disableIndexes:[]  
  
  
  
    /** 
 
     * @cfg {String} dateFormat how to format date values. If undefined (the default)  
 
     * date is formatted as configured in colummn model 
 
     */  
  
    ,dateFormat:undefined  
  
  
  
    /** 
 
     * @cfg {Boolean} showSelectAll Select All item is shown in menu if true (defaults to true) 
 
     */  
  
    ,showSelectAll:true  
  
  
  
    /** 
 
     * @cfg {String} mode Use 'remote' for remote stores or 'local' for local stores. If mode is local 
 
     * no data requests are sent to server the grid's store is filtered instead (defaults to 'remote') 
 
     */  
  
    ,mode:'remote'  
  
  
  
    /** 
 
     * @cfg {Number} width Width of input field in pixels (defaults to 100) 
 
     */  
  
    ,width:500  
  
  
  
    /** 
 
     * @cfg {String} xtype xtype is usually not used to instantiate this plugin but you have a chance to identify it 
 
     */  
  
    ,xtype:'gridsearch'  
  
  
  
    /** 
 
     * @cfg {Object} paramNames Params name map (defaults to {fields:'fields', query:'query'} 
 
     */  
  
    ,paramNames: {  
  
         fields:'fields'  
  
        ,query:'query'  
  
    }  
  
  
  
    /** 
 
     * @cfg {String} shortcutKey Key to fucus the input field (defaults to r = Sea_r_ch). Empty string disables shortcut 
 
     */  
  
    ,shortcutKey:'r'  
  
  
  
    /** 
 
     * @cfg {String} shortcutModifier Modifier for shortcutKey. Valid values: alt, ctrl, shift (defaults to alt) 
 
     */  
  
    ,shortcutModifier:'alt'  
  
  
  
    /** 
 
     * @cfg {String} align 'left' or 'right' (defaults to 'left') 
 
     */  
  
  
  
    /** 
 
     * @cfg {Number} minLength force user to type this many character before he can make a search 
 
     */  
  
  
  
    /** 
 
     * @cfg {Ext.Panel/String} toolbarContainer Panel (or id of the panel) which contains toolbar we want to render 
 
     * search controls to (defaults to this.grid, the grid this plugin is plugged-in into) 
 
     */  
  
      
  
    // {{{  
  
    /** 
 
     * private 
 
     * @param {Ext.grid.GridPanel/Ext.grid.EditorGrid} grid reference to grid this plugin is used for 
 
     */  
  
    ,init:function(grid) {  
  
        this.grid = grid;  
  
  
  
        // setup toolbar container if id was given  
  
        if('string' === typeof this.toolbarContainer) {  
  
            this.toolbarContainer = Ext.getCmp(this.toolbarContainer);  
  
        }  
  
  
  
        // do our processing after grid render and reconfigure  
  
        grid.onRender = grid.onRender.createSequence(this.onRender, this);  
  
        grid.reconfigure = grid.reconfigure.createSequence(this.reconfigure, this);  
  
    } // eo function init  
  
    // }}}  
  
    // {{{  
  
    /** 
 
     * private add plugin controls to <b>existing</b> toolbar and calls reconfigure 
 
     */  
  
    ,onRender:function() {  
  
        var panel = this.toolbarContainer || this.grid;  
  
        var tb = 'bottom' === this.position ? panel.bottomToolbar : panel.topToolbar;  
  
  
  
        // add menu  
  
        this.menu = new Ext.menu.Menu();  
  
  
  
        // handle position  
  
        if('right' === this.align) {  
  
            tb.addFill();  
  
        }  
  
        else {  
  
            tb.addSeparator();  
  
        }  
  
  
  
        // add menu button  
  
        tb.add({  
  
             text:this.searchText  
  
            ,menu:this.menu  
  
            ,iconCls:this.iconCls  
  
        });  
  
  
  
        // add input field (TwinTriggerField in fact)  
  
        this.field = new Ext.form.TwinTriggerField({  
  
             width:this.width  
  
            ,selectOnFocus:undefined === this.selectOnFocus ? true : this.selectOnFocus  
  
            ,trigger1Class:'x-form-clear-trigger'  
  
            ,trigger2Class:'x-form-search-trigger'  
  
            ,onTrigger1Click:this.onTriggerClear.createDelegate(this)  
  
            ,onTrigger2Click:this.onTriggerSearch.createDelegate(this)  
  
            ,minLength:this.minLength  
  
        });  
  
  
  
        // install event handlers on input field  
  
        this.field.on('render', function() {  
  
            this.field.el.dom.qtip = this.searchTipText;  
  
  
  
            // install key map  
  
            var map = new Ext.KeyMap(this.field.el, [{  
  
                 key:Ext.EventObject.ENTER  
  
                ,scope:this  
  
                ,fn:this.onTriggerSearch  
  
            },{  
  
                 key:Ext.EventObject.ESC  
  
                ,scope:this  
  
                ,fn:this.onTriggerClear  
  
            }]);  
  
            map.stopEvent = true;  
  
        }, this, {single:true});  
  
  
  
        tb.add(this.field);  
  
  
  
        // reconfigure  
  
        this.reconfigure();  
  
  
  
        // keyMap  
  
        if(this.shortcutKey && this.shortcutModifier) {  
  
            var shortcutEl = this.grid.getEl();  
  
            var shortcutCfg = [{  
  
                 key:this.shortcutKey  
  
                ,scope:this  
  
                ,stopEvent:true  
  
                ,fn:function() {  
  
                    this.field.focus();  
  
                }  
  
            }];  
  
            shortcutCfg[0][this.shortcutModifier] = true;  
  
            this.keymap = new Ext.KeyMap(shortcutEl, shortcutCfg);  
  
        }  
  
    } // eo function onRender  
  
    // }}}  
  
    // {{{  
  
    /** 
 
     * private Clear Trigger click handler 
 
     */  
  
    ,onTriggerClear:function() {  
  
        this.field.setValue('');  
  
        this.field.focus();  
  
        this.onTriggerSearch();  
  
    } // eo function onTriggerClear  
  
    // }}}  
  
    // {{{  
  
    /** 
 
     * private Search Trigger click handler (executes the search, local or remote) 
 
     */  
  
    ,onTriggerSearch:function() {  
  
        if(!this.field.isValid()) {  
  
            return;  
  
        }  
  
        var val = this.field.getValue();

        var store = this.grid.store;  
        store.load({ params: { start: 0, limit: this.grid.store.totallength} });
        var cms=this.grid.getColumnModel();

        
        // grid's store filter  
  
        if('local' === this.mode) {  
  
            store.clearFilter();  
  
            if(val) {  
  
                store.filterBy(function(r,rid) {  
  
                    var retval = false;  
  
                    this.menu.items.each(function(item) {  
  
                        if(!item.checked || retval) {  
  
                            return;  
  
                        }  
                        
                        
  
                        var rv = r.get(item.dataIndex);  
                        
                        var index=cms.findColumnIndex(item.dataIndex);
                        if(index!=-1&&cms.getColumnById(index)&&cms.getColumnById(index).xtype=="combocolumn"){
                            var editorData=cms.getColumnById(index).editor.getStore().data.items;
                            for(var i=0;i<editorData.length;i++){
                                if(editorData[i].data.name.indexOf(val)>-1){
                                  val=editorData[i].data.val;
                                  break;
                               }
                            }
                        }
                        else{
                            rv = rv instanceof Date ? rv.format(this.dateFormat || r.fields.get(item.dataIndex).dateFormat) : rv;  
                        }
                        
                        var re = new RegExp(val, 'gi');  
  
                        retval = re.test(rv);  
  
                    }, this);  
  
                    if(retval) {  
  
                        return true;  
  
                    }  
  
                    return retval;  
  
                }, this);  
  
            }  
  
            else {  
  
            }  
  
        }  
  
        // ask server to filter records  
  
        else {  
  
            // clear start (necessary if we have paging)  
  
            if(store.lastOptions && store.lastOptions.params) {  
  
                store.lastOptions.params[store.paramNames.start] = 0;  
  
            }  
  
  
  
            // get fields to search array  
  
            var fields = [];  
  
            this.menu.items.each(function(item) {  
  
                if(item.checked) {  
  
                    fields.push(item.dataIndex);  
  
                }  
  
            });  
  
  
  
            // add fields and query to baseParams of store  
  
            delete(store.baseParams[this.paramNames.fields]);  
  
            delete(store.baseParams[this.paramNames.query]);  
  
            if (store.lastOptions && store.lastOptions.params) {  
  
                delete(store.lastOptions.params[this.paramNames.fields]);  
  
                delete(store.lastOptions.params[this.paramNames.query]);  
  
            }  
  
            if(fields.length) {  
  
                store.baseParams[this.paramNames.fields] = Ext.encode(fields);  
  
                store.baseParams[this.paramNames.query] = val;  
  
            }  
  
  
  
            // reload store  
  
            store.reload();  
  
        }

        //store.load({ params: { start: 0, limit: store.data.length > 30 ? 30 : store.data.length} });
  
    } // eo function onTriggerSearch  
  
    // }}}  
  
    // {{{  
  
    /** 
 
     * @param {Boolean} true to disable search (TwinTriggerField), false to enable 
 
     */  
  
    ,setDisabled:function() {  
  
        this.field.setDisabled.apply(this.field, arguments);  
  
    } // eo function setDisabled  
  
    // }}}  
  
    // {{{  
  
    /** 
 
     * Enable search (TwinTriggerField) 
 
     */  
  
    ,enable:function() {  
  
        this.setDisabled(false);  
  
    } // eo function enable  
  
    // }}}  
  
    // {{{  
  
    /** 
 
     * Enable search (TwinTriggerField) 
 
     */  
  
    ,disable:function() {  
  
        this.setDisabled(true);  
  
    } // eo function disable  
  
    // }}}  
  
    // {{{  
  
    /** 
 
     * private (re)configures the plugin, creates menu items from column model 
 
     */  
  
    ,reconfigure:function() {  
  
  
  
        // {{{  
  
        // remove old items  
  
        var menu = this.menu;  
  
        menu.removeAll();  
  
  
  
        // add Select All item plus separator  
  
        if(this.showSelectAll) {  
  
            menu.add(new Ext.menu.CheckItem({  
  
                 text:this.selectAllText  
  
                ,checked:!(this.checkIndexes instanceof Array)  
  
                ,hideOnClick:false  
  
                ,handler:function(item) {  
  
                    var checked = ! item.checked;  
  
                    item.parentMenu.items.each(function(i) {  
  
                        if(item !== i && i.setChecked) {  
  
                            i.setChecked(checked);  
  
                        }  
  
                    });  
  
                }  
  
            }),'-');  
  
        }  
  
  
  
        // }}}  
  
        // {{{  
  
        // add new items  
  
        var cm = this.grid.colModel;  
  
        Ext.each(cm.config, function(config) {  
  
            var disable = false;  
  
            if(config.header && config.dataIndex) {  
  
                Ext.each(this.disableIndexes, function(item) {  
  
                    disable = disable ? disable : item === config.dataIndex;  
  
                });  
  
                if(!disable) {  
  
                    menu.add(new Ext.menu.CheckItem({  
  
                         text:config.header  
  
                        ,hideOnClick:false  
  
                        ,checked:'all' === this.checkIndexes  
  
                        ,dataIndex:config.dataIndex  
  
                    }));  
  
                }  
  
            }  
  
        }, this);  
  
        // }}}  
  
        // {{{  
  
        // check items  
  
        if(this.checkIndexes instanceof Array) {  
  
            Ext.each(this.checkIndexes, function(di) {  
  
                var item = menu.items.find(function(itm) {  
  
                    return itm.dataIndex === di;  
  
                });  
  
                if(item) {  
  
                    item.setChecked(true, true);  
  
                }  
  
            }, this);  
  
        }  
  
        // }}}  
  
  
  
    } // eo function reconfigure  
  
    // }}}  
  
  
  
}); 