/*<script>
    */
    var NumberField = Ext.extend(Ext.form.NumberField, {
        setValue: function(v) {
            v = typeof v == 'number' ? v : String(v).replace(this.decimalSeparator, ".");
            v = isNaN(v) ? '' : String(v).replace(".", this.decimalSeparator);
            //  if you want to ensure that the values being set on the field is also forced to the required number of decimal places.
            // (not extensively tested)
            // v = isNaN(v) ? '' : this.fixPrecision(String(v).replace(".", this.decimalSeparator));
            return Ext.form.NumberField.superclass.setValue.call(this, v);
        },
        fixPrecision: function(value) {
            var nan = isNaN(value);
            if (!this.allowDecimals || this.decimalPrecision == -1 || nan || !value) {
                return nan ? '' : value;
            }
            return parseFloat(value).toFixed(this.decimalPrecision);
        }
    });

    function f_clientWidth() {
        return f_filterResults(
            window.innerWidth ? window.innerWidth : 0,
            document.documentElement ? document.documentElement.clientWidth : 0,
            document.body ? document.body.clientWidth : 0
        );
    }

    function f_clientHeight() {
        return f_filterResults(
            window.innerHeight ? window.innerHeight : 0,
            document.documentElement ? document.documentElement.clientHeight : 0,
            document.body ? document.body.clientHeight : 0
        );
    }

    function f_scrollLeft() {
        return f_filterResults(
            window.pageXOffset ? window.pageXOffset : 0,
            document.documentElement ? document.documentElement.scrollLeft : 0,
            document.body ? document.body.scrollLeft : 0
        );
    }

    function f_scrollTop() {
        return f_filterResults(
            window.pageYOffset ? window.pageYOffset : 0,
            document.documentElement ? document.documentElement.scrollTop : 0,
            document.body ? document.body.scrollTop : 0
        );
    }

    function f_filterResults(n_win, n_docel, n_body) {
        var n_result = n_win ? n_win : 0;
        if (n_docel && (!n_result || (n_result > n_docel)))
            n_result = n_docel;
        return n_body && (!n_result || (n_result > n_body)) ? n_body : n_result;
    }

    Ext.ns('Ext.ux.grid');

    /**
     * @class Ext.ux.grid.RowEditor
     * @extends Ext.Panel
     * Plugin (ptype = 'roweditor') that adds the ability to rapidly edit full rows in a grid.
     * A validation mode may be enabled which uses AnchorTips to notify the user of all
     * validation errors at once.
     *
     * @ptype roweditor
     */
    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(
                /**
                 * @event beforeedit
                 * Fired before the row editor is activated.
                 * If the listener returns <tt>false</tt> the editor will not be activated.
                 * @param {Ext.ux.grid.RowEditor} roweditor This object
                 * @param {Number} rowIndex The rowIndex of the row just edited
                 */
                'beforeedit',
                /**
                 * @event validateedit
                 * Fired after a row is edited and passes validation.
                 * If the listener returns <tt>false</tt> changes to the record will not be set.
                 * @param {Ext.ux.grid.RowEditor} roweditor This object
                 * @param {Object} changes Object with changes made to the record.
                 * @param {Ext.data.Record} r The Record that was edited.
                 * @param {Number} rowIndex The rowIndex of the row just edited
                 */
                'validateedit',
                /**
                 * @event afteredit
                 * Fired after a row is edited and passes validation.  This event is fired
                 * after the store's update event is fired with this edit.
                 * @param {Ext.ux.grid.RowEditor} roweditor This object
                 * @param {Object} changes Object with changes made to the record.
                 * @param {Ext.data.Record} r The Record that was edited.
                 * @param {Number} rowIndex The rowIndex of the row just edited
                 */
                'afteredit',
                /**
                 * @event canceledit
                 * chamado ao clicar no botao cancel.
                 * If the listener returns <tt>false</tt> the editor will not be activated.
                 * @param {Ext.ux.grid.RowEditor} roweditor This object
                 */
                'canceledit'
            );
        },

        init: function(grid) {
            this.grid = grid;
            this.ownerCt = grid;
            if (this.clicksToEdit !== 0) {
                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);
                    }
                }
            }
            // stopEditing without saving when a record is removed from Store.
            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('Você precisa confirmar ou cancelar suas alterações');
                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.fireEvent('canceledit', this.rowIndex);
                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);
                this.setSize(Ext.fly(row).getWidth(), Ext.isIE ? (Ext.fly(row).getHeight() + 10) : 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;
            }
        }
    });

    /**
     * InputTextMask script used for mask/regexp operations.
     * Mask Individual Character Usage:
     * 9 - designates only numeric values
     * L - designates only uppercase letter values
     * l - designates only lowercase letter values
     * A - designates only alphanumeric values
     * X - denotes that a custom client script regular expression is specified</li>
     * All other characters are assumed to be "special" characters used to mask
     * the input component
     * Example 1:
     * (999)999-9999 only numeric values can be entered where the the character
     * position value is 9. Parenthesis and dash are non-editable/mask characters.
     * Example 2:
     * 99L-ll-X[^A-C]X only numeric values for the first two characters,
     * uppercase values for the third character, lowercase letters for the
     * fifth/sixth characters, and the last character X[^A-C]X together counts
     * as the eighth character regular expression that would allow all characters
     * but "A", "B", and "C". Dashes outside the regular expression are
     * non-editable/mask characters.
     */

    Ext.namespace('Ext.ux');

    Ext.ux.InputTextMask = function(mask, clearWhenInvalid) {

        this.clearWhenInvalid = clearWhenInvalid;
        this.rawMask = mask;

        this.viewMask = '';
        this.maskArray = new Array();
        var mai = 0;
        var regexp = '';
        for (var i = 0; i < mask.length; i++) {
            if (regexp) {
                if (regexp == 'X') {
                    regexp = '';
                }
                if (mask.charAt(i) == 'X') {
                    this.maskArray[mai] = regexp;
                    mai++;
                    regexp = null;
                } else {
                    regexp += mask.charAt(i);
                }
            } else if (mask.charAt(i) == 'X') {
                regexp += 'X';
                this.viewMask += '_';
            } else if (mask.charAt(i) == '9' || mask.charAt(i) == 'L' || mask.charAt(i) == 'l' || mask.charAt(i) == 'A') {
                this.viewMask += '_';
                this.maskArray[mai] = mask.charAt(i);
                mai++;
            } else {
                this.viewMask += mask.charAt(i);
                this.maskArray[mai] = RegExp.escape(mask.charAt(i));
                mai++;
            }
        }

        this.specialChars = this.viewMask.replace(/(L|l|9|A|_|X)/g, '');
    };

    Ext.ux.InputTextMask.prototype = {

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

            if (field.rendered) {
                this.assignEl();
            } else {
                field.on('render', this.assignEl, this);
            }

            field.on('blur', this.removeValueWhenInvalid, this);
            field.on('focus', this.processMaskFocus, this);
        },

        assignEl: function() {
            this.inputTextElement = this.field.getEl().dom;
            this.field.getEl().on('keypress', this.processKeyPress, this);
            this.field.getEl().on('keydown', this.processKeyDown, this);
            if (Ext.isSafari || Ext.isIE) {
                this.field.getEl().on('paste', this.startTask, this);
                this.field.getEl().on('cut', this.startTask, this);
            }
            if (Ext.isGecko) {
                this.field.getEl().on('mousedown', this.setPreviousValue, this);
                this.field.getEl().on('input', this.onInput, this);
            }
        },
        onInput: function() {
            this.startTask(false);
        },

        setPreviousValue: function(event) {
            this.oldCursorPos = this.getCursorPosition();
        },

        getValidatedKey: function(keyCode, cursorPosition) {
            var maskKey = this.maskArray[cursorPosition.start];
            if (maskKey == '9') {
                return keyCode.pressedKey.match(/[0-9]/);
            } else if (maskKey == 'L') {
                return (keyCode.pressedKey.match(/[A-Za-z]/)) ? keyCode.pressedKey.toUpperCase() : null;
            } else if (maskKey == 'l') {
                return (keyCode.pressedKey.match(/[A-Za-z]/)) ? keyCode.pressedKey.toLowerCase() : null;
            } else if (maskKey == 'A') {
                return keyCode.pressedKey.match(/[A-Za-z0-9]/);
            } else if (maskKey) {
                return (keyCode.pressedKey.match(new RegExp(maskKey)));
            }
            return (null);
        },

        removeValueWhenInvalid: function() {
            if (this.inputTextElement.value.indexOf('_') > -1) {
                this.inputTextElement.value = '';
            }
        },

        managePaste: function() {
            if (this.oldCursorPos == null) {
                return;
            }
            var valuePasted = this.inputTextElement.value.substring(this.oldCursorPos.start, this.inputTextElement.value.length - (this.oldCursorPos.previousValue.length - this.oldCursorPos.end));
            if (this.oldCursorPos.start < this.oldCursorPos.end) { //there is selection...
                this.oldCursorPos.previousValue =
                    this.oldCursorPos.previousValue.substring(0, this.oldCursorPos.start) +
                    this.viewMask.substring(this.oldCursorPos.start, this.oldCursorPos.end) +
                    this.oldCursorPos.previousValue.substring(this.oldCursorPos.end, this.oldCursorPos.previousValue.length);
                valuePasted = valuePasted.substr(0, this.oldCursorPos.end - this.oldCursorPos.start);
            }
            this.inputTextElement.value = this.oldCursorPos.previousValue;
            keycode = {
                unicode: '',
                isShiftPressed: false,
                isTab: false,
                isBackspace: false,
                isLeftOrRightArrow: false,
                isDelete: false,
                pressedKey: ''
            }
            var charOk = false;
            for (var i = 0; i < valuePasted.length; i++) {
                keycode.pressedKey = valuePasted.substr(i, 1);
                keycode.unicode = valuePasted.charCodeAt(i);
                this.oldCursorPos = this.skipMaskCharacters(keycode, this.oldCursorPos);
                if (this.oldCursorPos === false) {
                    break;
                }
                if (this.injectValue(keycode, this.oldCursorPos)) {
                    charOk = true;
                    this.moveCursorToPosition(keycode, this.oldCursorPos);
                    this.oldCursorPos.previousValue = this.inputTextElement.value;
                    this.oldCursorPos.start = this.oldCursorPos.start + 1;
                }
            }
            if (!charOk && this.oldCursorPos !== false) {
                this.moveCursorToPosition(null, this.oldCursorPos);
            }
            this.oldCursorPos = null;
        },

        processKeyDown: function(e) {
            this.processMaskFormatting(e, 'keydown');
        },

        processKeyPress: function(e) {
            this.processMaskFormatting(e, 'keypress');
        },

        startTask: function(setOldCursor) {
            if (this.task == undefined) {
                this.task = new Ext.util.DelayedTask(this.managePaste, this);
            }
            if (setOldCursor !== false) {
                this.oldCursorPos = this.getCursorPosition();
            }
            this.task.delay(0);
        },

        skipMaskCharacters: function(keyCode, cursorPos) {
            if (cursorPos.start != cursorPos.end && (keyCode.isDelete || keyCode.isBackspace))
                return (cursorPos);
            while (this.specialChars.match(RegExp.escape(cursorPos.previousValue.charAt(((keyCode.isBackspace) ? cursorPos.start - 1 : cursorPos.start))))) {
                if (keyCode.isBackspace) {
                    cursorPos.dec();
                } else {
                    cursorPos.inc();
                }
                if (cursorPos.start >= cursorPos.previousValue.length || cursorPos.start < 0) {
                    return false;
                }
            }
            return (cursorPos);
        },

        isManagedByKeyDown: function(keyCode) {
            if (keyCode.unicode == Ext.EventObject.BACKSPACE ||
                keyCode.unicode == Ext.EventObject.DELETE) {
                return (true);
            }
            return (false);
        },

        processMaskFormatting: function(e, type) {
            this.oldCursorPos = null;
            var cursorPos = this.getCursorPosition();
            var keyCode = this.getKeyCode(e, cursorPos);
            if (keyCode.unicode == 0) { //?? sometimes on Safari
                return;
            }
            if ((keyCode.unicode == 67 || keyCode.unicode == 99) && e.ctrlKey) { //Ctrl+c, let's the browser manage it!
                return;
            }
            if ((keyCode.unicode == 88 || keyCode.unicode == 120) && e.ctrlKey) { //Ctrl+x, manage paste
                this.startTask();
                return;
            }

            if ((keyCode.unicode == 86 || keyCode.unicode == 118) && e.ctrlKey) { //Ctrl+v, manage paste....
                this.startTask();
                return;
            }
            if (type == 'keydown' && !this.isManagedByKeyDown(keyCode)) {
                return true;
            }
            if (type == 'keypress' && this.isManagedByKeyDown(keyCode)) {
                return true;
            }
            if (this.handleEventBubble(e, keyCode)) {
                return true;
            }
            if (this.inputTextElement.value.length === 0) {
                this.inputTextElement.value = this.viewMask;
            }
            cursorPos = this.skipMaskCharacters(keyCode, cursorPos);
            if (cursorPos === false) {
                return (false);
            }
            if (this.injectValue(keyCode, cursorPos)) {
                this.moveCursorToPosition(keyCode, cursorPos);
            }
            return false;
        },

        processMaskFocus: function() {
            if (this.inputTextElement.value.length == 0) {
                var cursorPos = this.getCursorPosition();
                this.inputTextElement.value = this.viewMask;
                this.moveCursorToPosition(null, cursorPos);
            }
        },

        isManagedByBrowser: function(keyCode) {
            if (!keyCode.isShiftPressed && (keyCode.unicode == Ext.EventObject.TAB ||
                    keyCode.unicode == Ext.EventObject.RETURN ||
                    keyCode.unicode == Ext.EventObject.ENTER ||
                    keyCode.unicode == Ext.EventObject.SHIFT ||
                    keyCode.unicode == Ext.EventObject.CONTROL ||
                    keyCode.unicode == Ext.EventObject.ESC ||
                    keyCode.unicode == Ext.EventObject.PAGEUP ||
                    keyCode.unicode == Ext.EventObject.PAGEDOWN ||
                    keyCode.unicode == Ext.EventObject.END ||
                    keyCode.unicode == Ext.EventObject.HOME ||
                    keyCode.unicode == Ext.EventObject.LEFT ||
                    keyCode.unicode == Ext.EventObject.UP ||
                    keyCode.unicode == Ext.EventObject.RIGHT ||
                    keyCode.unicode == Ext.EventObject.DOWN ||
                    keyCode.unicode == Ext.EventObject.F5)) {
                return (true);
            }
            return (false);
        },

        handleEventBubble: function(keyEvent, keyCode) {
            try {
                if (keyCode && this.isManagedByBrowser(keyCode)) {
                    return true;
                }
                keyEvent.stopEvent();
                return false;
            } catch (e) {
                alert(e.message);
            }
        },

        getCursorPosition: function() {
            var s, e, r;
            if (this.inputTextElement.createTextRange) {
                r = document.selection.createRange().duplicate();
                r.moveEnd('character', this.inputTextElement.value.length);
                if (r.text === '') {
                    s = this.inputTextElement.value.length;
                } else {
                    s = this.inputTextElement.value.lastIndexOf(r.text);
                }
                r = document.selection.createRange().duplicate();
                r.moveStart('character', -this.inputTextElement.value.length);
                e = r.text.length;
            } else {
                s = this.inputTextElement.selectionStart;
                e = this.inputTextElement.selectionEnd;
            }
            return this.CursorPosition(s, e, r, this.inputTextElement.value);
        },

        moveCursorToPosition: function(keyCode, cursorPosition) {
            var p = (!keyCode || (keyCode && keyCode.isBackspace)) ? cursorPosition.start : cursorPosition.start + 1;
            if (this.inputTextElement.createTextRange) {
                cursorPosition.range.move('character', p);
                cursorPosition.range.select();
            } else {
                this.inputTextElement.selectionStart = p;
                this.inputTextElement.selectionEnd = p;
            }
        },

        injectValue: function(keyCode, cursorPosition) {
            if (keyCode.unicode == cursorPosition.previousValue.charCodeAt(cursorPosition.start))
                return true;
            var key;
            if (!keyCode.isDelete && !keyCode.isBackspace) {
                key = this.getValidatedKey(keyCode, cursorPosition);
            } else {
                if (cursorPosition.start == cursorPosition.end) {
                    key = '_';
                    if (keyCode.isBackspace) {
                        cursorPosition.dec();
                    }
                } else {
                    key = this.viewMask.substring(cursorPosition.start, cursorPosition.end);
                }
            }
            if (key) {
                this.inputTextElement.value = cursorPosition.previousValue.substring(0, cursorPosition.start) +
                    key +
                    cursorPosition.previousValue.substring(cursorPosition.start + key.length, cursorPosition.previousValue.length);
                return true;
            }
            return false;
        },

        getKeyCode: function(onKeyDownEvent) {
            var keycode = {};
            keycode.unicode = onKeyDownEvent.getKey();
            keycode.isShiftPressed = onKeyDownEvent.shiftKey;
            keycode.isDelete = (onKeyDownEvent.getKey() == Ext.EventObject.DELETE) ? true : false;
            keycode.isTab = (onKeyDownEvent.getCharCode() == Ext.EventObject.TAB) ? true : false;
            keycode.isBackspace = (onKeyDownEvent.getCharCode() == Ext.EventObject.BACKSPACE) ? true : false;
            keycode.isLeftOrRightArrow = (onKeyDownEvent.getCharCode() == Ext.EventObject.LEFT || onKeyDownEvent.getCharCode() == Ext.EventObject.RIGHT) ? true : false;
            keycode.pressedKey = String.fromCharCode(keycode.unicode);

            return (keycode);
        },

        CursorPosition: function(start, end, range, previousValue) {
            var cursorPosition = {};
            cursorPosition.start = isNaN(start) ? 0 : start;
            cursorPosition.end = isNaN(end) ? 0 : end;
            cursorPosition.range = range;
            cursorPosition.previousValue = previousValue;
            cursorPosition.inc = function() {
                cursorPosition.start++;
                cursorPosition.end++;
            };
            cursorPosition.dec = function() {
                cursorPosition.start--;
                cursorPosition.end--;
            };
            return (cursorPosition);
        }

    };

    // Add escape prototype feature to RegExp object
    // text.replace(/[.*+?^${}()|[\]\/\\]/g, '\\$0');
    // text.replace(/([\\\^\$*+[\]?{}.=!:(|)])/g, '\\$1');

    if (!RegExp.escape) {
        RegExp.escape = function(text) {
            var sp;
            if (!arguments.callee.sRE) {
                sp = ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'];
                arguments.callee.sRE = new RegExp('(\\' + sp.join('|\\') + ')', 'g');
            }
            return text.replace(arguments.callee.sRE, '\\$1');
        };
    };

    /*
     * Ext JS Library 2.2.1
     * Copyright(c) 2006-2009, Ext JS, LLC.
     * licensing@extjs.com
     *
     * http://extjs.com/license
     */

    Ext.app.SearchField = Ext.extend(Ext.form.TwinTriggerField, {
        initComponent: function() {
            Ext.app.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',
        paramNames: {
            start: 'start',
            limit: 'limit'
        },
        pageSize: 0,

        onTrigger1Click: function() {
            if (this.hasSearch) {
                this.el.dom.value = '';
                var o = {};
                o[this.paramNames.start] = 0;
                if (this.pageSize > 0)
                    o[this.paramNames.limit] = this.pageSize;
                this.store.baseParams = this.store.baseParams || {};
                this.store.baseParams[this.paramName] = '';
                this.store.reload({
                    params: o
                });
                this.triggers[0].hide();
                this.hasSearch = false;
            }
        },

        onTrigger2Click: function() {
            var v = this.getRawValue();
            if (v.length < 1) {
                this.onTrigger1Click();
                return;
            }
            var o = {};
            o[this.paramNames.start] = 0;
            if (this.pageSize > 0)
                o[this.paramNames.limit] = this.pageSize;
            this.store.baseParams = this.store.baseParams || {};
            this.store.baseParams[this.paramName] = v;
            this.store.reload({
                params: o
            });
            this.hasSearch = true;
            this.triggers[0].show();
        }
    });

    /*
     * Ext JS Library 2.2.1
     * Copyright(c) 2006-2009, Ext JS, LLC.
     * licensing@extjs.com
     *
     * http://extjs.com/license
     */

    Ext.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, {
        lines: false,
        borderWidth: Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
        cls: 'x-column-tree',

        onRender: function() {
            Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments);
            this.headers = this.body.createChild({
                cls: 'x-tree-headers'
            }, this.innerCt.dom);

            var cols = this.columns,
                c;
            var totalWidth = 0;

            for (var i = 0, len = cols.length; i < len; i++) {
                c = cols[i];
                totalWidth += c.width;
                this.headers.createChild({
                    cls: 'x-tree-hd ' + (c.cls ? c.cls + '-hd' : ''),
                    cn: {
                        cls: 'x-tree-hd-text',
                        html: c.header
                    },
                    style: 'width:' + (c.width - this.borderWidth) + 'px;'
                });
            }
            this.headers.createChild({
                cls: 'x-clear'
            });
            // prevent floats from wrapping when clipped
            this.headers.setWidth(totalWidth);
            this.innerCt.setWidth(totalWidth);
        }
    });

    Ext.tree.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
        focus: Ext.emptyFn, // prevent odd scrolling behavior

        renderElements: function(n, a, targetNode, bulkRender) {
            this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';

            var t = n.getOwnerTree();
            var cols = t.columns;
            var bw = t.borderWidth;
            var c = cols[0];

            var buf = [
                '<li class="x-tree-node"><div ext:tree-node-id="', n.id, '" class="x-tree-node-el x-tree-node-leaf ', a.cls, '">',
                '<div class="x-tree-col" style="width:', c.width - bw, 'px;">',
                '<span class="x-tree-node-indent">', this.indentMarkup, "</span>",
                '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">',
                '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon', (a.icon ? " x-tree-node-inline-icon" : ""), (a.iconCls ? " " + a.iconCls : ""), '" unselectable="on">',
                '<a hidefocus="on" class="x-tree-node-anchor" href="', a.href ? a.href : "#", '" tabIndex="1" ',
                a.hrefTarget ? ' target="' + a.hrefTarget + '"' : "", '>',
                '<span unselectable="on">', n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]), "</span></a>",
                "</div>"
            ];
            for (var i = 1, len = cols.length; i < len; i++) {
                c = cols[i];

                buf.push('<div class="x-tree-col ', (c.cls ? c.cls : ''), '" style="width:', c.width - bw, 'px;">',
                    '<div class="x-tree-col-text">', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]), "</div>",
                    "</div>");
            }
            buf.push(
                '<div class="x-clear"></div></div>',
                '<ul class="x-tree-node-ct" style="display:none;"></ul>',
                "</li>");

            if (bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()) {
                this.wrap = Ext.DomHelper.insertHtml("beforeBegin",
                    n.nextSibling.ui.getEl(), buf.join(""));
            } else {
                this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
            }

            this.elNode = this.wrap.childNodes[0];
            this.ctNode = this.wrap.childNodes[1];
            var cs = this.elNode.firstChild.childNodes;
            this.indentNode = cs[0];
            this.ecNode = cs[1];
            this.iconNode = cs[2];
            this.anchor = cs[3];
            this.textNode = cs[3].firstChild;
        }
    });

    /*
     * allows for downloading of grid data (store) directly into excel
     * Method: extracts data of gridPanel store, uses columnModel to construct XML excel document,
     * converts to Base64, then loads everything into a data URL link.
     *
     * @author        Animal        <extjs support team>
     *
     */

    /**
     * base64 encode / decode
     *
     * @location     http://www.webtoolkit.info/
     *
     */

    var Base64 = {

        // private property
        _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

        // public method for encoding
        encode: function(input) {
            var output = "";
            var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
            var i = 0;

            //input = Base64._utf8_encode(input);

            while (i < input.length) {

                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }

                output = output +
                    this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
                    this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

            }

            return output;
        },

        // public method for decoding
        decode: function(input) {
            var output = "";
            var chr1, chr2, chr3;
            var enc1, enc2, enc3, enc4;
            var i = 0;

            input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

            while (i < input.length) {

                enc1 = this._keyStr.indexOf(input.charAt(i++));
                enc2 = this._keyStr.indexOf(input.charAt(i++));
                enc3 = this._keyStr.indexOf(input.charAt(i++));
                enc4 = this._keyStr.indexOf(input.charAt(i++));

                chr1 = (enc1 << 2) | (enc2 >> 4);
                chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
                chr3 = ((enc3 & 3) << 6) | enc4;

                output = output + String.fromCharCode(chr1);

                if (enc3 != 64) {
                    output = output + String.fromCharCode(chr2);
                }
                if (enc4 != 64) {
                    output = output + String.fromCharCode(chr3);
                }

            }

            //output = Base64._utf8_decode(output);

            return output;

        }
    }

    Ext.override(Ext.grid.GridPanel, {
        getExcelXml: function(includeHidden) {
            var worksheet = this.createWorksheet(includeHidden);
            return worksheet;

        },

        createWorksheet: function(includeHidden) {

            var cm = this.getColumnModel();

            var headerXml = '';

            for (var i = 0; i < cm.getColumnCount(); i++) {
                if (includeHidden || !cm.isHidden(i)) {
                    var w = cm.getColumnWidth(i)
                    headerXml += '<td width=' + w + ' bgcolor=#BFD4F0 style="color:#15428B;"><b>' + cm.getColumnHeader(i) + '</b></td>';

                }
            }

            // Generate worksheet header details.
            var t = '<table border=1 bordercolor="#666666">';
            t += '<tr>' + headerXml + '</tr>';


            // Generate the data rows from the data in the Store
            for (var i = 0, it = this.store.data.items, l = it.length; i < l; i++) {
                t += '<tr>';
                r = it[i].data;
                var k = 0;
                for (var j = 0; j < cm.getColumnCount(); j++) {
                    if (includeHidden || !cm.isHidden(j)) {
                        //debugger;
                        var type = '';
                        if (cm.config[j].type) {
                            type = cm.config[j].type;
                        }

                        var v = r[cm.getDataIndex(j)];

                        if (type == 'float') {
                            var txt = '' + v;
                            txt = txt.replace(',', '');
                            txt = txt.replace('.', ',');
                            v = txt;
                        } else if (type == 'date') {
                            var dt = new Date(v);
                            var data = dt.format('d/m/Y');
                            v = data;
                        } else if (type == 'boolean') {
                            if (!v) {
                                v = '<span style="color:#ff0000;">Não</span>';
                            } else if (v) {
                                v = '<span style="color:#008000;">Sim</span>';
                            }
                        }

                        if (!v) {
                            t += '<td></td>';
                        } else {
                            t += '<td>';
                            t += v;
                            t += '</td>';
                        }
                        k++;
                    }
                }
                t += '</tr>';
            }

            xml = t + '</table>';

            return xml;
        }
    });

    Ext.apply(Ext.form.VTypes, {
        urlhash: function(v) {
            return /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)|^#\w+$/i.test(v);
        },
        urlhashText: 'Este campo deve possuir valor de URL no formato "http:/' + '/www.dominio.com" ou "#hash"',
        urlhttps: function(v) {
            return /^(((https)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i.test(v);
        },
        urlhttpsText: 'Este campo deve possuir valor de URL com protocolo HTTPS.'

    });