I couldn't find a plugin to mask a TextField the way I need it. The only one that did what I was looking for was this one http://digitalbush.com/projects/masked-input-plugin/ but it was for jQuery. So I decided to "port it" over to ExtJS.
First I tried to make it a plugin but I decided to overwrite the TextField class so subclasses will inherit it too.

This is how to use it:

Code:
{
    xtype: 'textfield',
    mask: '(999) 999-9999'
}
or

Code:
{
    xtype: 'textfield',
    mask: { text: '(999) 999-9999', placeholder:'#', includeInValue: false }
}
includeInValue will include/exclude the mask when getValue() is called. (default: true)

Here is the full source code:

Code:
Ext.form.MaskDefinitions = {
    '9': '[0-9]',
    'a': '[A-Za-z]',
    '*': '[A-Za-z0-9]'
};
Ext.applyIf(Array.prototype, {
    map: function (fn, scope) {
        var r = [],
            i = 0,
            len = this.length,
            v;
        for (; i < len; i++) {
            v = fn.call(scope, this[i], i, this);
            if (v != null)
                r.push(v);
        }
        return r;
    }
});
(function () {
    var _initComponent = Ext.form.TextField.prototype.initComponent,
        _initEvents = Ext.form.TextField.prototype.initEvents,
        _getValue = Ext.form.TextField.prototype.getValue,
        _setValue = Ext.form.TextField.prototype.setValue,
        _preFocus = Ext.form.TextField.prototype.preFocus;

    Ext.override(Ext.form.TextField, {
        initComponent: function () {
            if (this.initMask()) {
                Ext.apply(this, {
                    disableKeyFilter: true,
                    maskRe: null
                });
                this.addEvents('maskComplete');
            }
            _initComponent.apply(this);
        },
        initEvents: function () {
            _initEvents.apply(this);
            if (this.mask) {
                if (!this.enableKeyEvents && this.mask) {
                    this.mon(this.el, {
                        scope: this,
                        keydown: this.onKeyDown,
                        keypress: this.onKeyPress
                    });
                }
                this.mon(this.el, 'paste', this.onPaste, this);
            }
        },
        initMask: function () {
            if (!Ext.isDefined(this.mask)) {
                return false;
            }
            this.mask = Ext.isString(this.mask) ? { text: this.mask} : this.mask;
            Ext.applyIf(this.mask, { placeholder: '_', includeInValue: true });
            if (!this.mask.text) {
                return false;
            }

            Ext.apply(this.mask, {
                regexp: [],
                partialPosition: this.mask.text.length,
                firstNonMaskPos: null,
                length: this.mask.text.length,
                buffer: this.mask.text.split('').map(function (c) {
                    return (c === '?') ? null : Ext.form.MaskDefinitions[c] ? this.mask.placeholder : c;
                }, this),
                seekNext: function (pos) {
                    while (++pos <= this.length && !this.regexp[pos]);
                    return pos;
                },
                seekPrev: function (pos) {
                    while (--pos >= 0 && !this.regexp[pos]);
                    return pos;
                },
                shiftL: function (start, end) {
                    if (start < 0) {
                        return;
                    }
                    for (var i = start, j = this.seekNext(end); i < this.length; i++) {
                        if (this.regexp[i]) {
                            if (j < this.length && this.regexp[i].test(this.buffer[j])) {
                                this.buffer[i] = this.buffer[j];
                                this.buffer[j] = this.placeholder;
                            } else {
                                break;
                            }
                            j = this.seekNext(j);
                        }
                    }
                },
                shiftR: function (pos) {
                    for (var i = pos, c = this.placeholder; i < this.length; i++) {
                        if (this.regexp[i]) {
                            var j = this.seekNext(i);
                            var t = this.buffer[i];
                            this.buffer[i] = c;
                            if (j < this.length && this.regexp[j].test(t)) {
                                c = t;
                            }
                            else {
                                break;
                            }
                        }
                    }
                },
                clearBuffer: function (start, end) {
                    for (var i = start; i < end && i < this.length; i++) {
                        if (this.regexp[i]) {
                            this.buffer[i] = this.placeholder;
                        }
                    }
                },
                getBufferValue: function (removeMask) {
                    return removeMask ?
                            this.buffer.map(function (c, i) {
                                return (this.regexp[i] && c != this.placeholder) ? c : null; 
                            }, this).join('') :
                           this.buffer.join('');
                }
            });

            Ext.each(this.mask.text.split(''), function (c, i) {
                if (c === '?') {
                    this.mask.length--;
                    this.mask.partialPosition = i;
                } else if (Ext.form.MaskDefinitions[c]) {
                    this.mask.regexp.push(new RegExp(Ext.form.MaskDefinitions[c]));
                    if (this.mask.firstNonMaskPos == null) {
                        this.mask.firstNonMaskPos = this.mask.regexp.length - 1;
                    }
                } else {
                    this.mask.regexp.push(null);
                }
            }, this);

            return true;
        },

        //private
        _cursor: function (start, end) {
            var s, e, r, d = this.el.dom;
            if (typeof start == 'number') {
                end = (typeof end == 'number') ? end : start;
                if (d.setSelectionRange) {
                    d.setSelectionRange(start, end);
                } else if (d.createTextRange) {
                    r = d.createTextRange();
                    r.collapse(true);
                    r.moveEnd('character', end);
                    r.moveStart('character', start);
                    r.select();
                }
            }
            else {
                if (d.setSelectionRange) {
                    s = d.selectionStart;
                    e = d.selectionEnd;
                }
                else if (document.selection && document.selection.createRange) {
                    r = document.selection.createRange();
                    s = 0 - r.duplicate().moveStart('character', -d.value.length);
                    e = s + r.text.length;
                }
                return { start: isNaN(s) ? 0 : s, end: isNaN(e) ? 0 : e };
            }
        },
        _checkValueMask: function (allow) {
            var test = this.getRawValue();
            var lastMatch = -1;
            for (var i = 0, pos = 0; i < this.mask.length; i++) {
                if (this.mask.regexp[i]) {
                    this.mask.buffer[i] = this.mask.placeholder;
                    while (pos++ < test.length) {
                        var c = test.charAt(pos - 1);
                        if (this.mask.regexp[i].test(c)) {
                            this.mask.buffer[i] = c;
                            lastMatch = i;
                            break;
                        }
                    }
                    if (pos > test.length)
                        break;
                } else if (this.mask.buffer[i] == test.charAt(pos) && i != this.mask.partialPosition) {
                    pos++;
                    lastMatch = i;
                }
            }
            if (!allow && lastMatch + 1 < this.mask.partialPosition) {
                this.setRawValue('');
                this.mask.clearBuffer(0, this.mask.length);
            } else if (allow || lastMatch + 1 >= this.mask.partialPosition) {
                this._writeMaskBuffer();
                if (!allow) this.setRawValue(this.getRawValue().substring(0, lastMatch + 1));
            }
            return (this.mask.partialPosition ? i : this.mask.firstNonMaskPos);
        },
        _writeMaskBuffer: function () {
            this.setRawValue(this.mask.getBufferValue());
        },

        //public
        getValue: function () {
            var v = _getValue.apply(this);
            if (this.mask && !Ext.isEmpty(v)) {
                v = this.mask.includeInValue ? v.replace(this.mask.placeholder,'') : this.mask.getBufferValue(true);
            }
            return v;
        },
        setValue: function (v) {
            _setValue.call(this, v);
            if (this.mask && !Ext.isEmpty(v)) {
                this._checkValueMask();
            }
        },

        //protected
        preFocus: function () {
            _preFocus.apply(this);
            if (this.mask && !this.hasFocus) {
                this.startValue = this.getValue();
                var pos = this._checkValueMask();
                this._writeMaskBuffer();
                (function () {
                    if (pos == this.mask.length) {
                        this._cursor(0, pos);
                    }
                    else {
                        this._cursor(pos);
                    }
                }).defer(0, this);
            }
        },
        beforeBlur: function () {
            if (this.mask && !this.readOnly) {
                this._checkValueMask();
            }
        },
        onKeyDown: function (e) {
            this.fireEvent('keydown', this, e);
            if (this.mask && !this.readOnly) {
                var k = e.getKey();
                if (k == e.BACKSPACE || k == e.DELETE) {
                    var pos = this._cursor(),
                        start = pos.start,
                        end = pos.end;
                    if (end - start == 0) {
                        start = k != e.DELETE ? this.mask.seekPrev(start) : (end = this.mask.seekNext(start - 1));
                        end = k == e.DELETE ? this.mask.seekNext(end) : end;
                    }
                    this.mask.clearBuffer(start, end);
                    this.mask.shiftL(start, end - 1);
                    this._writeMaskBuffer();
                    this._cursor(Math.max(this.mask.firstNonMaskPos, start));
                    e.stopEvent();
                } else if (k == e.ESC) {
                    this.setValue(this.startValue);
                    this._cursor(0, this._checkValueMask());
                    e.stopEvent();
                }
            }
        },
        onKeyPress: function (e) {
            this.fireEvent('keypress', this, e);
            if (this.mask && !this.readOnly) {
                var k = e.getKey(),
                    pos = this._cursor(),
                    start = pos.start,
                    end = pos.end;
                    if (!e.ctrlKey && !e.altKey && !e.isNavKeyPress() && k != e.CONTEXT_MENU && k >= 32) {
                        if (end - start != 0) {
                            this.mask.clearBuffer(start, end);
                            this.mask.shiftL(start, end - 1);
                            this._writeMaskBuffer();
                            this._cursor(Math.max(this.mask.firstNonMaskPos, start));
                        }
                        var p = this.mask.seekNext(start - 1);
                        if (p < this.mask.length) {
                            var c = String.fromCharCode(e.getCharCode());
                            if (this.mask.regexp[p].test(c)) {
                                this.mask.shiftR(p);
                                this.mask.buffer[p] = c;
                                this._writeMaskBuffer();
                                var next = this.mask.seekNext(p);
                                this._cursor(next);
                                if (next >= this.mask.length) {
                                    this.fireEvent('maskComplete', this, this.mask);
                                }
                            }
                        }
                        e.stopEvent();
                    }
            }
        },
        onPaste: function (e) {
            if (this.mask && !this.readOnly) {
                (function () { this._cursor(this._checkValueMask(true)); }).defer(10, this);
            }
        }
    });
})();
I'm new to Ext and the forums so please any like/dislikes will be appreciate it
Thanks