//MooTools More, <http://mootools.net/more>. Copyright (c) 2006-2009 Aaron Newton <http://clientcide.com/>, Valerio Proietti <http://mad4milk.net> & the MooTools team <http://mootools.net/developers>, MIT Style License.
MooTools.More = {
    version: "1.2.3.1"
};
(function() {
    var a = {
        language: "en-US",
        languages: {
            "en-US": {}
        },
        cascades: ["en-US"]
    };
    var b;
    MooTools.lang = new Events();
    $extend(MooTools.lang, {
        setLanguage: function(c) {
            if (!a.languages[c]) {
                return this;
            }
            a.language = c;
            this.load();
            this.fireEvent("langChange", c);
            return this;
        },
        load: function() {
            var c = this.cascade(this.getCurrentLanguage());
            b = {};
            $each(c, function(e, d) {
                b[d] = this.lambda(e);
            },
            this);
        },
        getCurrentLanguage: function() {
            return a.language;
        },
        addLanguage: function(c) {
            a.languages[c] = a.languages[c] || {};
            return this;
        },
        cascade: function(e) {
            var c = (a.languages[e] || {}).cascades || [];
            c.combine(a.cascades);
            c.erase(e).push(e);
            var d = c.map(function(g) {
                return a.languages[g];
            },
            this);
            return $merge.apply(this, d);
        },
        lambda: function(c) { (c || {}).get = function(e, d) {
                return $lambda(c[e]).apply(this, $splat(d));
            };
            return c;
        },
        get: function(e, d, c) {
            if (b && b[e]) {
                return (d ? b[e].get(d, c) : b[e]);
            }
        },
        set: function(d, e, c) {
            this.addLanguage(d);
            langData = a.languages[d];
            if (!langData[e]) {
                langData[e] = {};
            }
            $extend(langData[e], c);
            if (d == this.getCurrentLanguage()) {
                this.load();
                this.fireEvent("langChange", d);
            }
            return this;
        },
        list: function() {
            return Hash.getKeys(a.languages);
        }
    });
})();
Class.Mutators.Binds = function(a) {
    return a;
};
Class.Mutators.initialize = function(a) {
    return function() {
        $splat(this.Binds).each(function(b) {
            var c = this[b];
            if (c) {
                this[b] = c.bind(this);
            }
        },
        this);
        return a.apply(this, arguments);
    };
};
(function() {
    if (!Date.now) {
        Date.now = $time;
    }
    Date.Methods = {};
    ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds", "Time", "TimezoneOffset", "Week", "Timezone", "GMTOffset", "DayOfYear", "LastMonth", "LastDayOfMonth", "UTCDate", "UTCDay", "UTCFullYear", "AMPM", "Ordinal", "UTCHours", "UTCMilliseconds", "UTCMinutes", "UTCMonth", "UTCSeconds"].each(function(m) {
        Date.Methods[m.toLowerCase()] = m;
    });
    $each({
        ms: "Milliseconds",
        year: "FullYear",
        min: "Minutes",
        mo: "Month",
        sec: "Seconds",
        hr: "Hours"
    },
    function(n, m) {
        Date.Methods[m] = n;
    });
    var c = function(n, m) {
        return new Array(m - n.toString().length + 1).join("0") + n;
    };
    Date.implement({
        set: function(r, o) {
            switch ($type(r)) {
            case "object":
                for (var q in r) {
                    this.set(q, r[q]);
                }
                break;
            case "string":
                r = r.toLowerCase();
                var n = Date.Methods;
                if (n[r]) {
                    this["set" + n[r]](o);
                }
            }
            return this;
        },
        get: function(o) {
            o = o.toLowerCase();
            var n = Date.Methods;
            if (n[o]) {
                return this["get" + n[o]]();
            }
            return null;
        },
        clone: function() {
            return new Date(this.get("time"));
        },
        increment: function(m, o) {
            m = m || "day";
            o = $pick(o, 1);
            switch (m) {
            case "year":
                return this.increment("month", o * 12);
            case "month":
                var n = this.get("date");
                this.set("date", 1).set("mo", this.get("mo") + o);
                return this.set("date", n.min(this.get("lastdayofmonth")));
            case "week":
                return this.increment("day", o * 7);
            case "day":
                return this.set("date", this.get("date") + o);
            }
            if (!Date.units[m]) {
                throw new Error(m + " is not a supported interval");
            }
            return this.set("time", this.get("time") + o * Date.units[m]());
        },
        decrement: function(m, n) {
            return this.increment(m, -1 * $pick(n, 1));
        },
        isLeapYear: function() {
            return Date.isLeapYear(this.get("year"));
        },
        clearTime: function() {
            return this.set({
                hr: 0,
                min: 0,
                sec: 0,
                ms: 0
            });
        },
        diff: function(p, n) {
            n = n || "day";
            if ($type(p) == "string") {
                p = Date.parse(p);
            }
            switch (n) {
            case "year":
                return p.get("year") - this.get("year");
            case "month":
                var m = (p.get("year") - this.get("year")) * 12;
                return m + p.get("mo") - this.get("mo");
            default:
                var o = p.get("time") - this.get("time");
                if (Date.units[n]() > o.abs()) {
                    return 0;
                }
                return ((p.get("time") - this.get("time")) / Date.units[n]()).round();
            }
            return null;
        },
        getLastDayOfMonth: function() {
            return Date.daysInMonth(this.get("mo"), this.get("year"));
        },
        getDayOfYear: function() {
            return (Date.UTC(this.get("year"), this.get("mo"), this.get("date") + 1) - Date.UTC(this.get("year"), 0, 1)) / Date.units.day();
        },
        getWeek: function() {
            return (this.get("dayofyear") / 7).ceil();
        },
        getOrdinal: function(m) {
            return Date.getMsg("ordinal", m || this.get("date"));
        },
        getTimezone: function() {
            return this.toString().replace(/^.*? ([A-Z]{3}).[0-9]{4}.*$/, "$1").replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, "$1$2$3");
        },
        getGMTOffset: function() {
            var m = this.get("timezoneOffset");
            return ((m > 0) ? "-": "+") + c((m.abs() / 60).floor(), 2) + c(m % 60, 2);
        },
        setAMPM: function(m) {
            m = m.toUpperCase();
            var n = this.get("hr");
            if (n > 11 && m == "AM") {
                return this.decrement("hour", 12);
            } else {
                if (n < 12 && m == "PM") {
                    return this.increment("hour", 12);
                }
            }
            return this;
        },
        getAMPM: function() {
            return (this.get("hr") < 12) ? "AM": "PM";
        },
        parse: function(m) {
            this.set("time", Date.parse(m));
            return this;
        },
        isValid: function(m) {
            return !! (m || this).valueOf();
        },
        format: function(m) {
            if (!this.isValid()) {
                return "invalid date";
            }
            m = m || "%x %X";
            m = i[m.toLowerCase()] || m;
            var n = this;
            return m.replace(/%([a-z%])/gi, function(o, p) {
                switch (p) {
                case "a":
                    return Date.getMsg("days")[n.get("day")].substr(0, 3);
                case "A":
                    return Date.getMsg("days")[n.get("day")];
                case "b":
                    return Date.getMsg("months")[n.get("month")].substr(0, 3);
                case "B":
                    return Date.getMsg("months")[n.get("month")];
                case "c":
                    return n.toString();
                case "d":
                    return c(n.get("date"), 2);
                case "H":
                    return c(n.get("hr"), 2);
                case "I":
                    return ((n.get("hr") % 12) || 12);
                case "j":
                    return c(n.get("dayofyear"), 3);
                case "m":
                    return c((n.get("mo") + 1), 2);
                case "M":
                    return c(n.get("min"), 2);
                case "o":
                    return n.get("ordinal");
                case "p":
                    return Date.getMsg(n.get("ampm"));
                case "S":
                    return c(n.get("seconds"), 2);
                case "U":
                    return c(n.get("week"), 2);
                case "w":
                    return n.get("day");
                case "x":
                    return n.format(Date.getMsg("shortDate"));
                case "X":
                    return n.format(Date.getMsg("shortTime"));
                case "y":
                    return n.get("year").toString().substr(2);
                case "Y":
                    return n.get("year");
                case "T":
                    return n.get("GMTOffset");
                case "Z":
                    return n.get("Timezone");
                }
                return p;
            });
        },
        toISOString: function() {
            return this.format("iso8601");
        }
    });
    Date.alias("diff", "compare");
    Date.alias("format", "strftime");
    var i = {
        db: "%Y-%m-%d %H:%M:%S",
        compact: "%Y%m%dT%H%M%S",
        iso8601: "%Y-%m-%dT%H:%M:%S%T",
        rfc822: "%a, %d %b %Y %H:%M:%S %Z",
        "short": "%d %b %H:%M",
        "long": "%B %d, %Y %H:%M"
    };
    var e = Date.parse;
    var k = function(p, r, o) {
        var n = -1;
        var q = Date.getMsg(p + "s");
        switch ($type(r)) {
        case "object":
            n = q[r.get(p)];
            break;
        case "number":
            n = q[month - 1];
            if (!n) {
                throw new Error("Invalid " + p + " index: " + index);
            }
            break;
        case "string":
            var m = q.filter(function(s) {
                return this.test(s);
            },
            new RegExp("^" + r, "i"));
            if (!m.length) {
                throw new Error("Invalid " + p + " string");
            }
            if (m.length > 1) {
                throw new Error("Ambiguous " + p);
            }
            n = m[0];
        }
        return (o) ? q.indexOf(n) : n;
    };
    Date.extend({
        getMsg: function(n, m) {
            return MooTools.lang.get("Date", n, m);
        },
        units: {
            ms: $lambda(1),
            second: $lambda(1000),
            minute: $lambda(60000),
            hour: $lambda(3600000),
            day: $lambda(86400000),
            week: $lambda(608400000),
            month: function(n, m) {
                var o = new Date;
                return Date.daysInMonth($pick(n, o.get("mo")), $pick(m, o.get("year"))) * 86400000;
            },
            year: function(m) {
                m = m || new Date().get("year");
                return Date.isLeapYear(m) ? 31622400000 : 31536000000;
            }
        },
        daysInMonth: function(n, m) {
            return [31, Date.isLeapYear(m) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][n];
        },
        isLeapYear: function(m) {
            return new Date(m, 1, 29).get("date") == 29;
        },
        parse: function(o) {
            var n = $type(o);
            if (n == "number") {
                return new Date(o);
            }
            if (n != "string") {
                return o;
            }
            o = o.clean();
            if (!o.length) {
                return null;
            }
            var m;
            Date.parsePatterns.some(function(q) {
                var p = q.re.exec(o);
                return (p) ? (m = q.handler(p)) : false;
            });
            return m || new Date(e(o));
        },
        parseDay: function(m, n) {
            return k("day", m, n);
        },
        parseMonth: function(n, m) {
            return k("month", n, m);
        },
        parseUTC: function(n) {
            var m = new Date(n);
            var o = Date.UTC(m.get("year"), m.get("mo"), m.get("date"), m.get("hr"), m.get("min"), m.get("sec"));
            return new Date(o);
        },
        orderIndex: function(m) {
            return Date.getMsg("dateOrder").indexOf(m) + 1;
        },
        defineFormat: function(m, n) {
            i[m] = n;
        },
        defineFormats: function(m) {
            for (var n in m) {
                Date.defineFormat(n, m[f]);
            }
        },
        parsePatterns: [],
        defineParser: function(m) {
            Date.parsePatterns.push(m.re && m.handler ? m: j(m));
        },
        defineParsers: function() {
            Array.flatten(arguments).each(Date.defineParser);
        },
        define2DigitYearStart: function(m) {
            d = m % 100;
            g = m - d;
        }
    });
    var g = 1900;
    var d = 70;
    var a = function(m) {
        switch (m) {
        case "x":
            return (Date.orderIndex("month") == 1) ? "%m[.-/]%d([.-/]%y)?": "%d[.-/]%m([.-/]%y)?";
        case "X":
            return "%H([.:]%M)?([.:]%S([.:]%s)?)?\\s?%p?\\s?%T?";
        case "o":
            return "[^\\d\\s]*";
        }
        return null;
    };
    var l = {
        a: /[a-z]{3,}/,
        d: /[0-2]?[0-9]|3[01]/,
        H: /[01]?[0-9]|2[0-3]/,
        I: /0?[1-9]|1[0-2]/,
        M: /[0-5]?\d/,
        s: /\d+/,
        p: /[ap]\.?m\.?/,
        y: /\d{2}|\d{4}/,
        Y: /\d{4}/,
        T: /Z|[+-]\d{2}(?::?\d{2})?/
    };
    l.B = l.b = l.A = l.a;
    l.m = l.I;
    l.S = l.M;
    var b;
    var j = function(o) {
        if (!b) {
            return {
                format: o
            };
        }
        var m = [null];
        var n = (o.source || o).replace(/%([a-z])/gi, function(p, q) {
            return a(q) || p;
        }).replace(/\((?!\?)/g, "(?:").replace(/ (?!\?|\*)/g, ",? ").replace(/%([a-z%])/gi, function(q, s) {
            var r = l[s];
            if (!r) {
                return s;
            }
            m.push(s);
            return "(" + r.source + ")";
        });
        return {
            format: o,
            re: new RegExp("^" + n + "$", "i"),
            handler: function(r) {
                var p = new Date().clearTime();
                for (var q = 1; q < m.length; q++) {
                    p = h.call(p, m[q], r[q]);
                }
                return p;
            }
        };
    };
    var h = function(m, n) {
        if (!n) {
            if (m == "m" || m == "d") {
                n = 1;
            } else {
                return this;
            }
        }
        switch (m) {
        case "a":
        case "A":
            return this.set("day", Date.parseDay(n, true));
        case "b":
        case "B":
            return this.set("mo", Date.parseMonth(n, true));
        case "d":
            return this.set("date", n);
        case "H":
        case "I":
            return this.set("hr", n);
        case "m":
            return this.set("mo", n - 1);
        case "M":
            return this.set("min", n);
        case "p":
            return this.set("ampm", n.replace(/\./g, ""));
        case "S":
            return this.set("sec", n);
        case "s":
            return this.set("ms", ("0." + n) * 1000);
        case "w":
            return this.set("day", n);
        case "Y":
            return this.set("year", n);
        case "y":
            n = +n;
            if (n < 100) {
                n += g + (n < d ? 100 : 0);
            }
            return this.set("year", n);
        case "T":
            if (n == "Z") {
                n = "+00";
            }
            var o = n.match(/([+-])(\d{2}):?(\d{2})?/);
            o = (o[1] + "1") * (o[2] * 60 + ( + o[3] || 0)) + this.getTimezoneOffset();
            return this.set("time", (this * 1) - o * 60000);
        }
        return this;
    };
    Date.defineParsers("%Y([-./]%m([-./]%d((T| )%X)?)?)?", "%Y%m%d(T%H(%M%S?)?)?", "%x( %X)?", "%d%o( %b( %Y)?)?( %X)?", "%b %d%o?( %Y)?( %X)?", "%b %Y");
    MooTools.lang.addEvent("langChange", function(m) {
        if (!MooTools.lang.get("Date")) {
            return;
        }
        b = m;
        Date.parsePatterns.each(function(o, n) {
            if (o.format) {
                Date.parsePatterns[n] = j(o.format);
            }
        });
    }).fireEvent("langChange", MooTools.lang.getCurrentLanguage());
})();
Element.implement({
    tidy: function() {
        this.set("value", this.get("value").tidy());
    },
    getTextInRange: function(b, a) {
        return this.get("value").substring(b, a);
    },
    getSelectedText: function() {
        if (this.setSelectionRange) {
            return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd());
        }
        return document.selection.createRange().text;
    },
    getSelectedRange: function() {
        if ($defined(this.selectionStart)) {
            return {
                start: this.selectionStart,
                end: this.selectionEnd
            };
        }
        var e = {
            start: 0,
            end: 0
        };
        var a = this.getDocument().selection.createRange();
        if (!a || a.parentElement() != this) {
            return e;
        }
        var c = a.duplicate();
        if (this.type == "text") {
            e.start = 0 - c.moveStart("character", -100000);
            e.end = e.start + a.text.length;
        } else {
            var b = this.get("value");
            var d = b.length - b.match(/[\n\r]*$/)[0].length;
            c.moveToElementText(this);
            c.setEndPoint("StartToEnd", a);
            e.end = d - c.text.length;
            c.setEndPoint("StartToStart", a);
            e.start = d - c.text.length;
        }
        return e;
    },
    getSelectionStart: function() {
        return this.getSelectedRange().start;
    },
    getSelectionEnd: function() {
        return this.getSelectedRange().end;
    },
    setCaretPosition: function(a) {
        if (a == "end") {
            a = this.get("value").length;
        }
        this.selectRange(a, a);
        return this;
    },
    getCaretPosition: function() {
        return this.getSelectedRange().start;
    },
    selectRange: function(e, a) {
        if (this.setSelectionRange) {
            this.focus();
            this.setSelectionRange(e, a);
        } else {
            var c = this.get("value");
            var d = c.substr(e, a - e).replace(/\r/g, "").length;
            e = c.substr(0, e).replace(/\r/g, "").length;
            var b = this.createTextRange();
            b.collapse(true);
            b.moveEnd("character", e + d);
            b.moveStart("character", e);
            b.select();
        }
        return this;
    },
    insertAtCursor: function(b, a) {
        var d = this.getSelectedRange();
        var c = this.get("value");
        this.set("value", c.substring(0, d.start) + b + c.substring(d.end, c.length));
        if ($pick(a, true)) {
            this.selectRange(d.start, d.start + b.length);
        } else {
            this.setCaretPosition(d.start + b.length);
        }
        return this;
    },
    insertAroundCursor: function(b, a) {
        b = $extend({
            before: "",
            defaultMiddle: "",
            after: ""
        },
        b);
        var c = this.getSelectedText() || b.defaultMiddle;
        var h = this.getSelectedRange();
        var g = this.get("value");
        if (h.start == h.end) {
            this.set("value", g.substring(0, h.start) + b.before + c + b.after + g.substring(h.end, g.length));
            this.selectRange(h.start + b.before.length, h.end + b.before.length + c.length);
        } else {
            var d = g.substring(h.start, h.end);
            this.set("value", g.substring(0, h.start) + b.before + d + b.after + g.substring(h.end, g.length));
            var e = h.start + b.before.length;
            if ($pick(a, true)) {
                this.selectRange(e, e + d.length);
            } else {
                this.setCaretPosition(e + g.length);
            }
        }
        return this;
    }
});
var InputValidator = new Class({
    Implements: [Options],
    options: {
        errorMsg: "Validation failed.",
        test: function(a) {
            return true;
        }
    },
    initialize: function(b, a) {
        this.setOptions(a);
        this.className = b;
    },
    test: function(b, a) {
        if (document.id(b)) {
            return this.options.test(document.id(b), a || this.getProps(b));
        } else {
            return false;
        }
    },
    getError: function(c, a) {
        var b = this.options.errorMsg;
        if ($type(b) == "function") {
            b = b(document.id(c), a || this.getProps(c));
        }
        return b;
    },
    getProps: function(a) {
        if (!document.id(a)) {
            return {};
        }
        return a.get("validatorProps");
    }
});
Element.Properties.validatorProps = {
    set: function(a) {
        return this.eliminate("validatorProps").store("validatorProps", a);
    },
    get: function(a) {
        if (a) {
            this.set(a);
        }
        if (this.retrieve("validatorProps")) {
            return this.retrieve("validatorProps");
        }
        if (this.getProperty("validatorProps")) {
            try {
                this.store("validatorProps", JSON.decode(this.getProperty("validatorProps")));
            } catch(c) {
                return {};
            }
        } else {
            var b = this.get("class").split(" ").filter(function(d) {
                return d.test(":");
            });
            if (!b.length) {
                this.store("validatorProps", {});
            } else {
                a = {};
                b.each(function(d) {
                    var g = d.split(":");
                    if (g[1]) {
                        try {
                            a[g[0]] = JSON.decode(g[1]);
                        } catch(h) {}
                    }
                });
                this.store("validatorProps", a);
            }
        }
        return this.retrieve("validatorProps");
    }
};
var FormValidator = new Class({
    Implements: [Options, Events],
    Binds: ["onSubmit"],
    options: {
        fieldSelectors: "input, select, textarea",
        ignoreHidden: true,
        useTitles: false,
        evaluateOnSubmit: true,
        evaluateFieldsOnBlur: true,
        evaluateFieldsOnChange: true,
        serial: true,
        stopOnFailure: true,
        warningPrefix: function() {
            return FormValidator.getMsg("warningPrefix") || "Warning: ";
        },
        errorPrefix: function() {
            return FormValidator.getMsg("errorPrefix") || "Error: ";
        }
    },
    initialize: function(b, a) {
        this.setOptions(a);
        this.element = document.id(b);
        this.element.store("validator", this);
        this.warningPrefix = $lambda(this.options.warningPrefix)();
        this.errorPrefix = $lambda(this.options.errorPrefix)();
        if (this.options.evaluateOnSubmit) {
            this.element.addEvent("submit", this.onSubmit);
        }
        if (this.options.evaluateFieldsOnBlur || this.options.evaluateFieldsOnChange) {
            this.watchFields(this.getFields());
        }
    },
    toElement: function() {
        return this.element;
    },
    getFields: function() {
        return (this.fields = this.element.getElements(this.options.fieldSelectors));
    },
    watchFields: function(a) {
        a.each(function(b) {
            if (this.options.evaluateFieldsOnBlur) {
                b.addEvent("blur", this.validationMonitor.pass([b, false], this));
            }
            if (this.options.evaluateFieldsOnChange) {
                b.addEvent("change", this.validationMonitor.pass([b, true], this));
            }
        },
        this);
    },
    validationMonitor: function() {
        $clear(this.timer);
        this.timer = this.validateField.delay(50, this, arguments);
    },
    onSubmit: function(a) {
        if (!this.validate(a) && a) {
            a.preventDefault();
        } else {
            this.reset();
        }
    },
    reset: function() {
        this.getFields().each(this.resetField, this);
        return this;
    },
    validate: function(b) {
        var a = this.getFields().map(function(c) {
            return this.validateField(c, true);
        },
        this).every(function(c) {
            return c;
        });
        this.fireEvent("formValidate", [a, this.element, b]);
        if (this.options.stopOnFailure && !a && b) {
            b.preventDefault();
        }
        return a;
    },
    validateField: function(j, a) {
        if (this.paused) {
            return true;
        }
        j = document.id(j);
        var d = !j.hasClass("validation-failed");
        var g, i;
        if (this.options.serial && !a) {
            g = this.element.getElement(".validation-failed");
            i = this.element.getElement(".warning");
        }
        if (j && (!g || a || j.hasClass("validation-failed") || (g && !this.options.serial))) {
            var c = j.className.split(" ").some(function(k) {
                return this.getValidator(k);
            },
            this);
            var h = [];
            j.className.split(" ").each(function(k) {
                if (k && !this.test(k, j)) {
                    h.include(k);
                }
            },
            this);
            d = h.length === 0;
            if (c && !j.hasClass("warnOnly")) {
                if (d) {
                    j.addClass("validation-passed").removeClass("validation-failed");
                    this.fireEvent("elementPass", j);
                } else {
                    j.addClass("validation-failed").removeClass("validation-passed");
                    this.fireEvent("elementFail", [j, h]);
                }
            }
            if (!i) {
                var e = j.className.split(" ").some(function(k) {
                    if (k.test("^warn-") || j.hasClass("warnOnly")) {
                        return this.getValidator(k.replace(/^warn-/, ""));
                    } else {
                        return null;
                    }
                },
                this);
                j.removeClass("warning");
                var b = j.className.split(" ").map(function(k) {
                    if (k.test("^warn-") || j.hasClass("warnOnly")) {
                        return this.test(k.replace(/^warn-/, ""), j, true);
                    } else {
                        return null;
                    }
                },
                this);
            }
        }
        return d;
    },
    test: function(b, d, e) {
        var a = this.getValidator(b);
        d = document.id(d);
        if (d.hasClass("ignoreValidation")) {
            return true;
        }
        e = $pick(e, false);
        if (d.hasClass("warnOnly")) {
            e = true;
        }
        var c = a ? a.test(d) : true;
        if (a && this.isVisible(d)) {
            this.fireEvent("elementValidate", [c, d, b, e]);
        }
        if (e) {
            return true;
        }
        return c;
    },
    isVisible: function(a) {
        if (!this.options.ignoreHidden) {
            return true;
        }
        while (a != document.body) {
            if (document.id(a).getStyle("display") == "none") {
                return false;
            }
            a = a.getParent();
        }
        return true;
    },
    resetField: function(a) {
        a = document.id(a);
        if (a) {
            a.className.split(" ").each(function(b) {
                if (b.test("^warn-")) {
                    b = b.replace(/^warn-/, "");
                }
                a.removeClass("validation-failed");
                a.removeClass("warning");
                a.removeClass("validation-passed");
            },
            this);
        }
        return this;
    },
    stop: function() {
        this.paused = true;
        return this;
    },
    start: function() {
        this.paused = false;
        return this;
    },
    ignoreField: function(a, b) {
        a = document.id(a);
        if (a) {
            this.enforceField(a);
            if (b) {
                a.addClass("warnOnly");
            } else {
                a.addClass("ignoreValidation");
            }
        }
        return this;
    },
    enforceField: function(a) {
        a = document.id(a);
        if (a) {
            a.removeClass("warnOnly").removeClass("ignoreValidation");
        }
        return this;
    }
});
FormValidator.getMsg = function(a) {
    return MooTools.lang.get("FormValidator", a);
};
FormValidator.adders = {
    validators: {},
    add: function(b, a) {
        this.validators[b] = new InputValidator(b, a);
        if (!this.initialize) {
            this.implement({
                validators: this.validators
            });
        }
    },
    addAllThese: function(a) {
        $A(a).each(function(b) {
            this.add(b[0], b[1]);
        },
        this);
    },
    getValidator: function(a) {
        return this.validators[a.split(":")[0]];
    }
};
$extend(FormValidator, FormValidator.adders);
FormValidator.implement(FormValidator.adders);
FormValidator.add("IsEmpty", {
    errorMsg: false,
    test: function(a) {
        if (a.type == "select-one" || a.type == "select") {
            return ! (a.selectedIndex >= 0 && a.options[a.selectedIndex].value != "");
        } else {
            return ((a.get("value") == null) || (a.get("value").length == 0));
        }
    }
});
FormValidator.addAllThese([["required", {
    errorMsg: function() {
        return FormValidator.getMsg("required");
    },
    test: function(a) {
        return ! FormValidator.getValidator("IsEmpty").test(a);
    }
}], ["minLength", {
    errorMsg: function(a, b) {
        if ($type(b.minLength)) {
            return FormValidator.getMsg("minLength").substitute({
                minLength: b.minLength,
                length: a.get("value").length
            });
        } else {
            return "";
        }
    },
    test: function(a, b) {
        if ($type(b.minLength)) {
            return (a.get("value").length >= $pick(b.minLength, 0));
        } else {
            return true;
        }
    }
}], ["maxLength", {
    errorMsg: function(a, b) {
        if ($type(b.maxLength)) {
            return FormValidator.getMsg("maxLength").substitute({
                maxLength: b.maxLength,
                length: a.get("value").length
            });
        } else {
            return "";
        }
    },
    test: function(a, b) {
        return (a.get("value").length <= $pick(b.maxLength, 10000));
    }
}], ["validate-integer", {
    errorMsg: FormValidator.getMsg.pass("integer"),
    test: function(a) {
        return FormValidator.getValidator("IsEmpty").test(a) || (/^(-?[1-9]\d*|0)$/).test(a.get("value"));
    }
}], ["validate-numeric", {
    errorMsg: FormValidator.getMsg.pass("numeric"),
    test: function(a) {
        return FormValidator.getValidator("IsEmpty").test(a) || (/^-?(?:0$0(?=\d*\.)|[1-9]|0)\d*(\.\d+)?$/).test(a.get("value"));
    }
}], ["validate-digits", {
    errorMsg: FormValidator.getMsg.pass("digits"),
    test: function(a) {
        return FormValidator.getValidator("IsEmpty").test(a) || (/^[\d() .:\-\+#]+$/.test(a.get("value")));
    }
}], ["validate-alpha", {
    errorMsg: FormValidator.getMsg.pass("alpha"),
    test: function(a) {
        return FormValidator.getValidator("IsEmpty").test(a) || (/^[a-zA-Z]+$/).test(a.get("value"));
    }
}], ["validate-alphanum", {
    errorMsg: FormValidator.getMsg.pass("alphanum"),
    test: function(a) {
        return FormValidator.getValidator("IsEmpty").test(a) || !(/\W/).test(a.get("value"));
    }
}], ["validate-date", {
    errorMsg: function(a, b) {
        if (Date.parse) {
            var c = b.dateFormat || "%x";
            return FormValidator.getMsg("dateSuchAs").substitute({
                date: new Date().format(c)
            });
        } else {
            return FormValidator.getMsg("dateInFormatMDY");
        }
    },
    test: function(a, b) {
        if (FormValidator.getValidator("IsEmpty").test(a)) {
            return true;
        }
        var h;
        if (Date.parse) {
            var g = b.dateFormat || "%x";
            h = Date.parse(a.get("value"));
            var e = h.format(g);
            if (e != "invalid date") {
                a.set("value", e);
            }
            return ! isNaN(h);
        } else {
            var c = /^(\d{2})\/(\d{2})\/(\d{4})$/;
            if (!c.test(a.get("value"))) {
                return false;
            }
            h = new Date(a.get("value").replace(c, "$1/$2/$3"));
            return (parseInt(RegExp.$1, 10) == (1 + h.getMonth())) && (parseInt(RegExp.$2, 10) == h.getDate()) && (parseInt(RegExp.$3, 10) == h.getFullYear());
        }
    }
}], ["validate-email", {
    errorMsg: FormValidator.getMsg.pass("email"),
    test: function(a) {
        return FormValidator.getValidator("IsEmpty").test(a) || (/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i).test(a.get("value"));
    }
}], ["validate-url", {
    errorMsg: FormValidator.getMsg.pass("url"),
    test: function(a) {
        return FormValidator.getValidator("IsEmpty").test(a) || (/^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i).test(a.get("value"));
    }
}], ["validate-currency-dollar", {
    errorMsg: FormValidator.getMsg.pass("currencyDollar"),
    test: function(a) {
        return FormValidator.getValidator("IsEmpty").test(a) || (/^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(a.get("value"));
    }
}], ["validate-one-required", {
    errorMsg: FormValidator.getMsg.pass("oneRequired"),
    test: function(a, b) {
        var c = document.id(b["validate-one-required"]) || a.parentNode;
        return c.getElements("input").some(function(d) {
            if (["checkbox", "radio"].contains(d.get("type"))) {
                return d.get("checked");
            }
            return d.get("value");
        });
    }
}]]);
Element.Properties.validator = {
    set: function(a) {
        var b = this.retrieve("validator");
        if (b) {
            b.setOptions(a);
        }
        return this.store("validator:options");
    },
    get: function(a) {
        if (a || !this.retrieve("validator")) {
            if (a || !this.retrieve("validator:options")) {
                this.set("validator", a);
            }
            this.store("validator", new FormValidator(this, this.retrieve("validator:options")));
        }
        return this.retrieve("validator");
    }
};
Element.implement({
    validate: function(a) {
        this.set("validator", a);
        return this.get("validator", a).validate();
    }
});
FormValidator.Inline = new Class({
    Extends: FormValidator,
    options: {
        scrollToErrorsOnSubmit: true,
        scrollFxOptions: {
            transition: "quad:out",
            offset: {
                y: -20
            }
        }
    },
    initialize: function(b, a) {
        this.parent(b, a);
        this.addEvent("onElementValidate", function(h, g, e, i) {
            var d = this.getValidator(e);
            if (!h && d.getError(g)) {
                if (i) {
                    g.addClass("warning");
                }
                var c = this.makeAdvice(e, g, d.getError(g), i);
                this.insertAdvice(c, g);
                this.showAdvice(e, g);
            } else {
                this.hideAdvice(e, g);
            }
        });
    },
    makeAdvice: function(d, g, c, h) {
        var e = (h) ? this.warningPrefix: this.errorPrefix;
        e += (this.options.useTitles) ? g.title || c: c;
        var a = (h) ? "warning-advice": "validation-advice";
        var b = this.getAdvice(d, g);
        if (b) {
            b = b.clone(true, true).set("html", e).replaces(b);
        } else {
            b = new Element("div", {
                html: e,
                styles: {
                    display: "none"
                },
                id: "advice-" + d + "-" + this.getFieldId(g)
            }).addClass(a);
        }
        g.store("advice-" + d, b);
        return b;
    },
    getFieldId: function(a) {
        return a.id ? a.id: a.id = "input_" + a.name;
    },
    showAdvice: function(b, c) {
        var a = this.getAdvice(b, c);
        if (a && !c.retrieve(this.getPropName(b)) && (a.getStyle("display") == "none" || a.getStyle("visiblity") == "hidden" || a.getStyle("opacity") == 0)) {
            c.store(this.getPropName(b), true);
            if (a.reveal) {
                a.reveal();
            } else {
                a.setStyle("display", "block");
            }
        }
    },
    hideAdvice: function(b, c) {
        var a = this.getAdvice(b, c);
        if (a && c.retrieve(this.getPropName(b))) {
            c.store(this.getPropName(b), false);
            if (a.dissolve) {
                a.dissolve();
            } else {
                a.setStyle("display", "none");
            }
        }
    },
    getPropName: function(a) {
        return "advice" + a;
    },
    resetField: function(a) {
        a = document.id(a);
        if (!a) {
            return this;
        }
        this.parent(a);
        a.className.split(" ").each(function(b) {
            this.hideAdvice(b, a);
        },
        this);
        return this;
    },
    getAllAdviceMessages: function(d, c) {
        var b = [];
        if (d.hasClass("ignoreValidation") && !c) {
            return b;
        }
        var a = d.className.split(" ").some(function(h) {
            var e = h.test("^warn-") || d.hasClass("warnOnly");
            if (e) {
                h = h.replace(/^warn-/, "");
            }
            var g = this.getValidator(h);
            if (!g) {
                return;
            }
            b.push({
                message: g.getError(d),
                warnOnly: e,
                passed: g.test(),
                validator: g
            });
        },
        this);
        return b;
    },
    getAdvice: function(a, b) {
        return b.retrieve("advice-" + a);
    },
    insertAdvice: function(a, c) {
        var b = c.get("validatorProps");
        if (!b.msgPos || !document.id(b.msgPos)) {
            if (c.type.toLowerCase() == "radio") {
                c.getParent().adopt(a);
            } else {
                a.inject(document.id(c), "after");
            }
        } else {
            document.id(b.msgPos).grab(a);
        }
    },
    validateField: function(g, e) {
        var a = this.parent(g, e);
        if (this.options.scrollToErrorsOnSubmit && !a) {
            var b = document.id(this).getElement(".validation-failed");
            var c = document.id(this).getParent();
            while (c != document.body && c.getScrollSize().y == c.getSize().y) {
                c = c.getParent();
            }
            var d = c.retrieve("fvScroller");
            if (!d && window.Fx && Fx.Scroll) {
                d = new Fx.Scroll(c, this.options.scrollFxOptions);
                c.store("fvScroller", d);
            }
            if (b) {
                if (d) {
                    d.toElement(b);
                } else {
                    c.scrollTo(c.getScroll().x, b.getPosition(c).y - 20);
                }
            }
        }
        return a;
    }
});
FormValidator.addAllThese([["validate-enforce-oncheck", {
    test: function(a, b) {
        if (a.checked) {
            var c = a.getParent("form").retrieve("validator");
            if (!c) {
                return true;
            } (b.toEnforce || document.id(b.enforceChildrenOf).getElements("input, select, textarea")).map(function(d) {
                c.enforceField(d);
            });
        }
        return true;
    }
}], ["validate-ignore-oncheck", {
    test: function(a, b) {
        if (a.checked) {
            var c = a.getParent("form").retrieve("validator");
            if (!c) {
                return true;
            } (b.toIgnore || document.id(b.ignoreChildrenOf).getElements("input, select, textarea")).each(function(d) {
                c.ignoreField(d);
                c.resetField(d);
            });
        }
        return true;
    }
}], ["validate-nospace", {
    errorMsg: function() {
        return FormValidator.getMsg("noSpace");
    },
    test: function(a, b) {
        return ! a.get("value").test(/\s/);
    }
}], ["validate-toggle-oncheck", {
    test: function(b, c) {
        var d = b.getParent("form").retrieve("validator");
        if (!d) {
            return true;
        }
        var a = c.toToggle || document.id(c.toToggleChildrenOf).getElements("input, select, textarea");
        if (!b.checked) {
            a.each(function(e) {
                d.ignoreField(e);
                d.resetField(e);
            });
        } else {
            a.each(function(e) {
                d.enforceField(e);
            });
        }
        return true;
    }
}], ["validate-reqchk-bynode", {
    errorMsg: function() {
        return FormValidator.getMsg("reqChkByNode");
    },
    test: function(a, b) {
        return (document.id(b.nodeId).getElements(b.selector || "input[type=checkbox], input[type=radio]")).some(function(c) {
            return c.checked;
        });
    }
}], ["validate-required-check", {
    errorMsg: function(a, b) {
        return b.useTitle ? a.get("title") : FormValidator.getMsg("requiredChk");
    },
    test: function(a, b) {
        return !! a.checked;
    }
}], ["validate-reqchk-byname", {
    errorMsg: function(a, b) {
        return FormValidator.getMsg("reqChkByName").substitute({
            label: b.label || a.get("type")
        });
    },
    test: function(b, d) {
        var c = d.groupName || b.get("name");
        var a = $$(document.getElementsByName(c)).some(function(h, g) {
            return h.checked;
        });
        var e = b.getParent("form").retrieve("validator");
        if (a && e) {
            e.resetField(b);
        }
        return a;
    }
}], ["validate-match", {
    errorMsg: function(a, b) {
        return FormValidator.getMsg("match").substitute({
            matchName: b.matchName || document.id(b.matchInput).get("name")
        });
    },
    test: function(b, c) {
        var d = b.get("value");
        var a = document.id(c.matchInput) && document.id(c.matchInput).get("value");
        return d && a ? d == a: true;
    }
}], ["validate-after-date", {
    errorMsg: function(a, b) {
        return FormValidator.getMsg("afterDate").substitute({
            label: b.afterLabel || (b.afterElement ? FormValidator.getMsg("startDate") : FormValidator.getMsg("currentDate"))
        });
    },
    test: function(b, c) {
        var d = document.id(c.afterElement) ? Date.parse(document.id(c.afterElement).get("value")) : new Date();
        var a = Date.parse(b.get("value"));
        return a && d ? a >= d: true;
    }
}], ["validate-before-date", {
    errorMsg: function(a, b) {
        return FormValidator.getMsg("beforeDate").substitute({
            label: b.beforeLabel || (b.beforeElement ? FormValidator.getMsg("endDate") : FormValidator.getMsg("currentDate"))
        });
    },
    test: function(b, c) {
        var d = Date.parse(b.get("value"));
        var a = document.id(c.beforeElement) ? Date.parse(document.id(c.beforeElement).get("value")) : new Date();
        return a && d ? a >= d: true;
    }
}], ["validate-custom-required", {
    errorMsg: function() {
        return FormValidator.getMsg("required");
    },
    test: function(a, b) {
        return a.get("value") != b.emptyValue;
    }
}], ["validate-same-month", {
    errorMsg: function(a, b) {
        var c = document.id(b.sameMonthAs) && document.id(b.sameMonthAs).get("value");
        var d = a.get("value");
        if (d != "") {
            return FormValidator.getMsg(c ? "sameMonth": "startMonth");
        }
    },
    test: function(a, b) {
        var d = Date.parse(a.get("value"));
        var c = Date.parse(document.id(b.sameMonthAs) && document.id(b.sameMonthAs).get("value"));
        return d && c ? d.format("%B") == c.format("%B") : true;
    }
}]]);
MooTools.lang.set("en-US", "Date", {
    months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
    days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
    dateOrder: ["month", "date", "year"],
    shortDate: "%m/%d/%Y",
    shortTime: "%I:%M%p",
    AM: "AM",
    PM: "PM",
    ordinal: function(a) {
        return (a > 3 && a < 21) ? "th": ["th", "st", "nd", "rd", "th"][Math.min(a % 10, 4)];
    },
    lessThanMinuteAgo: "less than a minute ago",
    minuteAgo: "about a minute ago",
    minutesAgo: "{delta} minutes ago",
    hourAgo: "about an hour ago",
    hoursAgo: "about {delta} hours ago",
    dayAgo: "1 day ago",
    daysAgo: "{delta} days ago",
    lessThanMinuteUntil: "less than a minute from now",
    minuteUntil: "about a minute from now",
    minutesUntil: "{delta} minutes from now",
    hourUntil: "about an hour from now",
    hoursUntil: "about {delta} hours from now",
    dayUntil: "1 day from now",
    daysUntil: "{delta} days from now"
});
MooTools.lang.set("en-US", "FormValidator", {
    required: "This field is required.",
    minLength: "Please enter at least {minLength} characters (you entered {length} characters).",
    maxLength: "Please enter no more than {maxLength} characters (you entered {length} characters).",
    integer: "Please enter an integer in this field. Numbers with decimals (e.g. 1.25) are not permitted.",
    numeric: 'Please enter only numeric values in this field (i.e. "1" or "1.1" or "-1" or "-1.1").',
    digits: "Please use numbers and punctuation only in this field (for example, a phone number with dashes or dots is permitted).",
    alpha: "Please use letters only (a-z) with in this field. No spaces or other characters are allowed.",
    alphanum: "Please use only letters (a-z) or numbers (0-9) only in this field. No spaces or other characters are allowed.",
    dateSuchAs: "Please enter a valid date such as {date}",
    dateInFormatMDY: 'Please enter a valid date such as MM/DD/YYYY (i.e. "12/31/1999")',
    email: 'Please enter a valid email address. For example "fred@domain.com".',
    url: "Please enter a valid URL such as http://www.google.com.",
    currencyDollar: "Please enter a valid $ amount. For example $100.00 .",
    oneRequired: "Please enter something for at least one of these inputs.",
    errorPrefix: "Error: ",
    warningPrefix: "Warning: ",
    noSpace: "There can be no spaces in this input.",
    reqChkByNode: "No items are selected.",
    requiredChk: "This field is required.",
    reqChkByName: "Please select a {label}.",
    match: "This field needs to match the {matchName} field",
    startDate: "the start date",
    endDate: "the end date",
    currendDate: "the current date",
    afterDate: "The date should be the same or after {label}.",
    beforeDate: "The date should be the same or before {label}.",
    startMonth: "Please select a start month",
    sameMonth: "These two dates must be in the same month - you must change one or the other."
});
