(function ($) {
    $.fn.caret = function (b, d) {
        if (this.length == 0) return;
        if (typeof b == 'number') {
            d = (typeof d == 'number') ? d : b;
            return this.each(function () {
                if (this.setSelectionRange) {
                    this.focus();
                    this.setSelectionRange(b, d)
                } else if (this.createTextRange) {
                    var a = this.createTextRange();
                    a.collapse(true);
                    a.moveEnd('character', d);
                    a.moveStart('character', b);
                    a.select()
                }
            })
        } else {
            if (this[0].setSelectionRange) {
                b = this[0].selectionStart;
                d = this[0].selectionEnd
            } else if (document.selection && document.selection.createRange) {
                var c = document.selection.createRange();
                b = 0 - c.duplicate().moveStart('character', -100000);
                d = b + c.text.length
            }
            return {
                begin: b,
                end: d
            }
        }
    };
    var q = {
        '9': "[0-9]",
        'a': "[A-Za-z]",
        '*': "[A-Za-z0-9]"
    };
    $.mask = {
        addPlaceholder: function (c, r) {
            q[c] = r
        }
    };
    $.fn.unmask = function () {
        return this.trigger("unmask")
    };
    $.fn.mask = function (m, o) {
        o = $.extend({
            placeholder: "_",
            completed: null
        }, o);
        var n = new RegExp("^" + $.map(m.split(""), function (c, i) {
            return q[c] || ((/[A-Za-z0-9]/.test(c) ? "" : "\\") + c)
        }).join('') + "$");
        return this.each(function () {
            var f = $(this);
            var j = new Array(m.length);
            var h = new Array(m.length);
            var l = false;
            var g = false;
            var d = null;
            $.each(m.split(""), function (i, c) {
                h[i] = (q[c] == null);
                j[i] = h[i] ? c : o.placeholder;
                if (!h[i] && d == null) d = i
            });

            function focusEvent() {
                checkVal();
                writeBuffer();
                setTimeout(function () {
                    $(f[0]).caret(l ? m.length : d)
                }, 0)
            };

            function keydownEvent(e) {
                var a = $(this).caret();
                var k = e.keyCode;
                g = (k < 16 || (k > 16 && k < 32) || (k > 32 && k < 41));
                if ((a.begin - a.end) != 0 && (!g || k == 8 || k == 46)) {
                    clearBuffer(a.begin, a.end)
                }
                if (k == 8) {
                    while (a.begin-- >= 0) {
                        if (!h[a.begin]) {
                            j[a.begin] = o.placeholder;
                            if ($.browser.opera) {
                                s = writeBuffer();
                                f.val(s.substring(0, a.begin) + " " + s.substring(a.begin));
                                $(this).caret(a.begin + 1)
                            } else {
                                writeBuffer();
                                $(this).caret(Math.max(d, a.begin))
                            }
                            return false
                        }
                    }
                } else if (k == 46) {
                    clearBuffer(a.begin, a.begin + 1);
                    writeBuffer();
                    $(this).caret(Math.max(d, a.begin));
                    return false
                } else if (k == 27) {
                    clearBuffer(0, m.length);
                    writeBuffer();
                    $(this).caret(d);
                    return false
                }
            };

            function keypressEvent(e) {
                if (g) {
                    g = false;
                    return (e.keyCode == 8) ? false : null
                }
                e = e || window.event;
                var k = e.charCode || e.keyCode || e.which;
                var a = $(this).caret();
                if (e.ctrlKey || e.altKey) {
                    return true
                } else if ((k >= 41 && k <= 122) || k == 32 || k > 186) {
                    var p = seekNext(a.begin - 1);
                    if (p < m.length) {
                        if (new RegExp(q[m.charAt(p)]).test(String.fromCharCode(k))) {
                            j[p] = String.fromCharCode(k);
                            writeBuffer();
                            var b = seekNext(p);
                            $(this).caret(b);
                            if (o.completed && b == m.length) o.completed.call(f)
                        }
                    }
                }
                return false
            };

            function clearBuffer(b, a) {
                for (var i = b; i < a && i < m.length; i++) {
                    if (!h[i]) j[i] = o.placeholder
                }
            };

            function writeBuffer() {
                return f.val(j.join('')).val()
            };

            function checkVal() {
                var a = f.val();
                var b = 0;
                for (var i = 0; i < m.length; i++) {
                    if (!h[i]) {
                        while (b++ < a.length) {
                            var c = new RegExp(q[m.charAt(i)]);
                            if (a.charAt(b - 1).match(c)) {
                                j[i] = a.charAt(b - 1);
                                break
                            }
                        }
                    }
                }
                var s = writeBuffer();
                if (!s.match(n)) {
                    f.val("");
                    clearBuffer(0, m.length);
                    l = false
                } else l = true
            };

            function seekNext(a) {
                while (++a < m.length) {
                    if (!h[a]) return a
                }
                return m.length
            };
            f.one("unmask", function () {
                f.unbind("focus", focusEvent);
                f.unbind("blur", checkVal);
                f.unbind("keydown", keydownEvent);
                f.unbind("keypress", keypressEvent);
                if ($.browser.msie) this.onpaste = null;
                else if ($.browser.mozilla) this.removeEventListener('input', checkVal, false)
            });
            f.bind("focus", focusEvent);
            f.bind("blur", checkVal);
            f.bind("keydown", keydownEvent);
            f.bind("keypress", keypressEvent);
            if ($.browser.msie) this.onpaste = function () {
                setTimeout(checkVal, 0)
            };
            else if ($.browser.mozilla) this.addEventListener('input', checkVal, false);
            checkVal()
        })
    }
})(jQuery);
