[rhmessaging-commits] rhmessaging commits: r3679 - in mgmt/trunk/wooly: resources and 1 other directory.

rhmessaging-commits at lists.jboss.org rhmessaging-commits at lists.jboss.org
Thu Oct 22 10:37:50 EDT 2009


Author: eallen
Date: 2009-10-22 10:37:50 -0400 (Thu, 22 Oct 2009)
New Revision: 3679

Modified:
   mgmt/trunk/wooly/python/wooly/pages.strings
   mgmt/trunk/wooly/resources/mootools.js
Log:
Combining mootools.js and mootools-more.js to prevent mootools-more.js from being loaded before mootools.js. This was causing an error and the page would render without and styles applied.

Modified: mgmt/trunk/wooly/python/wooly/pages.strings
===================================================================
--- mgmt/trunk/wooly/python/wooly/pages.strings	2009-10-22 14:27:01 UTC (rev 3678)
+++ mgmt/trunk/wooly/python/wooly/pages.strings	2009-10-22 14:37:50 UTC (rev 3679)
@@ -14,7 +14,6 @@
     <!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="resource?name=ielte7.css"/><![endif]-->
 
     <script type="text/javascript" src="resource?name=mootools.js"> </script>
-    <script type="text/javascript" src="resource?name=mootools-more.js"> </script>
     <script type="text/javascript" src="resource?name=wooly.js"> </script>
     <script type="text/javascript" src="resource?name=app.js"> </script>
     <script type="text/javascript" src="{base_name}.js"> </script>

Modified: mgmt/trunk/wooly/resources/mootools.js
===================================================================
--- mgmt/trunk/wooly/resources/mootools.js	2009-10-22 14:27:01 UTC (rev 3678)
+++ mgmt/trunk/wooly/resources/mootools.js	2009-10-22 14:37:50 UTC (rev 3679)
@@ -3902,4 +3902,3010 @@
         return this;
     }
 
+});
+
+//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'
+};
+
+/*
+Script: MooTools.Lang.js
+    Provides methods for localization.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+*/
+
+(function(){
+
+    var data = {
+        language: 'en-US',
+        languages: {
+            'en-US': {}
+        },
+        cascades: ['en-US']
+    };
+    
+    var cascaded;
+
+    MooTools.lang = new Events();
+
+    $extend(MooTools.lang, {
+
+        setLanguage: function(lang){
+            if (!data.languages[lang]) return this;
+            data.language = lang;
+            this.load();
+            this.fireEvent('langChange', lang);
+            return this;
+        },
+
+        load: function() {
+            var langs = this.cascade(this.getCurrentLanguage());
+            cascaded = {};
+            $each(langs, function(set, setName){
+                cascaded[setName] = this.lambda(set);
+            }, this);
+        },
+
+        getCurrentLanguage: function(){
+            return data.language;
+        },
+
+        addLanguage: function(lang){
+            data.languages[lang] = data.languages[lang] || {};
+            return this;
+        },
+
+        cascade: function(lang){
+            var cascades = (data.languages[lang] || {}).cascades || [];
+            cascades.combine(data.cascades);
+            cascades.erase(lang).push(lang);
+            var langs = cascades.map(function(lng){
+                return data.languages[lng];
+            }, this);
+            return $merge.apply(this, langs);
+        },
+
+        lambda: function(set) {
+            (set || {}).get = function(key, args){
+                return $lambda(set[key]).apply(this, $splat(args));
+            };
+            return set;
+        },
+
+        get: function(set, key, args){
+            if (cascaded && cascaded[set]) return (key ? cascaded[set].get(key, args) : cascaded[set]);
+        },
+
+        set: function(lang, set, members){
+            this.addLanguage(lang);
+            langData = data.languages[lang];
+            if (!langData[set]) langData[set] = {};
+            $extend(langData[set], members);
+            if (lang == this.getCurrentLanguage()){
+                this.load();
+                this.fireEvent('langChange', lang);
+            }
+            return this;
+        },
+
+        list: function(){
+            return Hash.getKeys(data.languages);
+        }
+
+    });
+
+})();
+
+/*
+Script: Log.js
+    Provides basic logging functionality for plugins to implement.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Guillermo Rauch
+*/
+
+var Log = new Class({
+    
+    log: function(){
+        Log.logger.call(this, arguments);
+    }
+    
+});
+
+Log.logged = [];
+
+Log.logger = function(){
+    if(window.console && console.log) console.log.apply(console, arguments);
+    else Log.logged.push(arguments);
+};
+
+/*
+Script: Class.Binds.js
+    Automagically binds specified methods in a class to the instance of the class.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+*/
+
+Class.Mutators.Binds = function(binds){
+    return binds;
+};
+
+Class.Mutators.initialize = function(initialize){
+    return function(){
+        $splat(this.Binds).each(function(name){
+            var original = this[name];
+            if (original) this[name] = original.bind(this);
+        }, this);
+        return initialize.apply(this, arguments);
+    };
+};
+
+/*
+Script: Class.Occlude.js
+    Prevents a class from being applied to a DOM element twice.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+*/
+
+Class.Occlude = new Class({
+
+    occlude: function(property, element){
+        element = document.id(element || this.element);
+        var instance = element.retrieve(property || this.property);
+        if (instance && !$defined(this.occluded)){
+            this.occluded = instance;
+        } else {
+            this.occluded = false;
+            element.store(property || this.property, this);
+        }
+        return this.occluded;
+    }
+
+});
+
+/*
+Script: Date.js
+    Extends the Date native object to include methods useful in managing dates.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+        Nicholas Barthelemy - https://svn.nbarthelemy.com/date-js/
+        Harald Kirshner - mail [at] digitarald.de; http://digitarald.de
+        Scott Kyle - scott [at] appden.com; http://appden.com
+
+*/
+
+(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(method){
+    Date.Methods[method.toLowerCase()] = method;
+});
+
+$each({
+    ms: 'Milliseconds',
+    year: 'FullYear',
+    min: 'Minutes',
+    mo: 'Month',
+    sec: 'Seconds',
+    hr: 'Hours'
+}, function(value, key){
+    Date.Methods[key] = value;
+});
+
+var zeroize = function(what, length){
+    return new Array(length - what.toString().length + 1).join('0') + what;
+};
+
+Date.implement({
+
+    set: function(prop, value){
+        switch ($type(prop)){
+            case 'object':
+                for (var p in prop) this.set(p, prop[p]);
+                break;
+            case 'string':
+                prop = prop.toLowerCase();
+                var m = Date.Methods;
+                if (m[prop]) this['set' + m[prop]](value);
+        }
+        return this;
+    },
+
+    get: function(prop){
+        prop = prop.toLowerCase();
+        var m = Date.Methods;
+        if (m[prop]) return this['get' + m[prop]]();
+        return null;
+    },
+
+    clone: function(){
+        return new Date(this.get('time'));
+    },
+
+    increment: function(interval, times){
+        interval = interval || 'day';
+        times = $pick(times, 1);
+
+        switch (interval){
+            case 'year':
+                return this.increment('month', times * 12);
+            case 'month':
+                var d = this.get('date');
+                this.set('date', 1).set('mo', this.get('mo') + times);
+                return this.set('date', d.min(this.get('lastdayofmonth')));
+            case 'week':
+                return this.increment('day', times * 7);
+            case 'day':
+                return this.set('date', this.get('date') + times);
+        }
+
+        if (!Date.units[interval]) throw new Error(interval + ' is not a supported interval');
+
+        return this.set('time', this.get('time') + times * Date.units[interval]());
+    },
+
+    decrement: function(interval, times){
+        return this.increment(interval, -1 * $pick(times, 1));
+    },
+
+    isLeapYear: function(){
+        return Date.isLeapYear(this.get('year'));
+    },
+
+    clearTime: function(){
+        return this.set({hr: 0, min: 0, sec: 0, ms: 0});
+    },
+
+    diff: function(d, resolution){
+        resolution = resolution || 'day';
+        if ($type(d) == 'string') d = Date.parse(d);
+
+        switch (resolution){
+            case 'year':
+                return d.get('year') - this.get('year');
+            case 'month':
+                var months = (d.get('year') - this.get('year')) * 12;
+                return months + d.get('mo') - this.get('mo');
+            default:
+                var diff = d.get('time') - this.get('time');
+                if (Date.units[resolution]() > diff.abs()) return 0;
+                return ((d.get('time') - this.get('time')) / Date.units[resolution]()).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(day){
+        return Date.getMsg('ordinal', day || 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 off = this.get('timezoneOffset');
+        return ((off > 0) ? '-' : '+') + zeroize((off.abs() / 60).floor(), 2) + zeroize(off % 60, 2);
+    },
+
+    setAMPM: function(ampm){
+        ampm = ampm.toUpperCase();
+        var hr = this.get('hr');
+        if (hr > 11 && ampm == 'AM') return this.decrement('hour', 12);
+        else if (hr < 12 && ampm == 'PM') return this.increment('hour', 12);
+        return this;
+    },
+
+    getAMPM: function(){
+        return (this.get('hr') < 12) ? 'AM' : 'PM';
+    },
+
+    parse: function(str){
+        this.set('time', Date.parse(str));
+        return this;
+    },
+
+    isValid: function(date) {
+        return !!(date || this).valueOf();
+    },
+
+    format: function(f){
+        if (!this.isValid()) return 'invalid date';
+        f = f || '%x %X';
+        f = formats[f.toLowerCase()] || f; // replace short-hand with actual format
+        var d = this;
+        return f.replace(/%([a-z%])/gi,
+            function($1, $2){
+                switch ($2){
+                    case 'a': return Date.getMsg('days')[d.get('day')].substr(0, 3);
+                    case 'A': return Date.getMsg('days')[d.get('day')];
+                    case 'b': return Date.getMsg('months')[d.get('month')].substr(0, 3);
+                    case 'B': return Date.getMsg('months')[d.get('month')];
+                    case 'c': return d.toString();
+                    case 'd': return zeroize(d.get('date'), 2);
+                    case 'H': return zeroize(d.get('hr'), 2);
+                    case 'I': return ((d.get('hr') % 12) || 12);
+                    case 'j': return zeroize(d.get('dayofyear'), 3);
+                    case 'm': return zeroize((d.get('mo') + 1), 2);
+                    case 'M': return zeroize(d.get('min'), 2);
+                    case 'o': return d.get('ordinal');
+                    case 'p': return Date.getMsg(d.get('ampm'));
+                    case 'S': return zeroize(d.get('seconds'), 2);
+                    case 'U': return zeroize(d.get('week'), 2);
+                    case 'w': return d.get('day');
+                    case 'x': return d.format(Date.getMsg('shortDate'));
+                    case 'X': return d.format(Date.getMsg('shortTime'));
+                    case 'y': return d.get('year').toString().substr(2);
+                    case 'Y': return d.get('year');
+                    case 'T': return d.get('GMTOffset');
+                    case 'Z': return d.get('Timezone');
+                }
+                return $2;
+            }
+        );
+    },
+
+    toISOString: function(){
+        return this.format('iso8601');
+    }
+
+});
+
+Date.alias('diff', 'compare');
+Date.alias('format', 'strftime');
+
+var formats = {
+    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 nativeParse = Date.parse;
+
+var parseWord = function(type, word, num){
+    var ret = -1;
+    var translated = Date.getMsg(type + 's');
+
+    switch ($type(word)){
+        case 'object':
+            ret = translated[word.get(type)];
+            break;
+        case 'number':
+            ret = translated[month - 1];
+            if (!ret) throw new Error('Invalid ' + type + ' index: ' + index);
+            break;
+        case 'string':
+            var match = translated.filter(function(name){
+                return this.test(name);
+            }, new RegExp('^' + word, 'i'));
+            if (!match.length)    throw new Error('Invalid ' + type + ' string');
+            if (match.length > 1) throw new Error('Ambiguous ' + type);
+            ret = match[0];
+    }
+
+    return (num) ? translated.indexOf(ret) : ret;
+};
+
+
+Date.extend({
+
+    getMsg: function(key, args) {
+        return MooTools.lang.get('Date', key, args);
+    },
+
+    units: {
+        ms: $lambda(1),
+        second: $lambda(1000),
+        minute: $lambda(60000),
+        hour: $lambda(3600000),
+        day: $lambda(86400000),
+        week: $lambda(608400000),
+        month: function(month, year){
+            var d = new Date;
+            return Date.daysInMonth($pick(month, d.get('mo')), $pick(year, d.get('year'))) * 86400000;
+        },
+        year: function(year){
+            year = year || new Date().get('year');
+            return Date.isLeapYear(year) ? 31622400000 : 31536000000;
+        }
+    },
+
+    daysInMonth: function(month, year){
+        return [31, Date.isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
+    },
+
+    isLeapYear: function(year){
+        return new Date(year, 1, 29).get('date') == 29;
+    },
+
+    parse: function(from){
+        var t = $type(from);
+        if (t == 'number') return new Date(from);
+        if (t != 'string') return from;
+        from = from.clean();
+        if (!from.length) return null;
+
+        var parsed;
+        Date.parsePatterns.some(function(pattern){
+            var r = pattern.re.exec(from);
+            return (r) ? (parsed = pattern.handler(r)) : false;
+        });
+
+        return parsed || new Date(nativeParse(from));
+    },
+
+    parseDay: function(day, num){
+        return parseWord('day', day, num);
+    },
+
+    parseMonth: function(month, num){
+        return parseWord('month', month, num);
+    },
+
+    parseUTC: function(value){
+        var localDate = new Date(value);
+        var utcSeconds = Date.UTC(localDate.get('year'), 
+        localDate.get('mo'),
+        localDate.get('date'), 
+        localDate.get('hr'), 
+        localDate.get('min'), 
+        localDate.get('sec'));
+        return new Date(utcSeconds);
+    },
+
+    orderIndex: function(unit){
+        return Date.getMsg('dateOrder').indexOf(unit) + 1;
+    },
+
+    defineFormat: function(name, format){
+        formats[name] = format;
+    },
+
+    defineFormats: function(formats){
+        for (var name in formats) Date.defineFormat(name, formats[f]);
+    },
+
+    parsePatterns: [],
+    
+    defineParser: function(pattern){
+        Date.parsePatterns.push( pattern.re && pattern.handler ? pattern : build(pattern) );
+    },
+    
+    defineParsers: function(){
+        Array.flatten(arguments).each(Date.defineParser);
+    },
+    
+    define2DigitYearStart: function(year){
+        yr_start = year % 100;
+        yr_base = year - yr_start;
+    }
+
+});
+
+var yr_base = 1900;
+var yr_start = 70;
+
+var replacers = function(key){
+    switch(key){
+        case 'x': // iso8601 covers yyyy-mm-dd, so just check if month is first
+            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 keys = {
+    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})?/
+};
+
+keys.B = keys.b = keys.A = keys.a;
+keys.m = keys.I;
+keys.S = keys.M;
+
+var lang;
+
+var build = function(format){
+    if (!lang) return {format: format}; // wait until language is set
+    
+    var parsed = [null];
+
+    var re = (format.source || format) // allow format to be regex
+     .replace(/%([a-z])/gi,
+        function($1, $2){
+            return replacers($2) || $1;
+        }
+    ).replace(/\((?!\?)/g, '(?:') // make all groups non-capturing
+     .replace(/ (?!\?|\*)/g, ',? ') // be forgiving with spaces and commas
+     .replace(/%([a-z%])/gi,
+        function($1, $2){
+            var p = keys[$2];
+            if (!p) return $2;
+            parsed.push($2);
+            return '(' + p.source + ')';
+        }
+    );
+
+    return {
+        format: format,
+        re: new RegExp('^' + re + '$', 'i'),
+        handler: function(bits){
+            var date = new Date().clearTime();
+            for (var i = 1; i < parsed.length; i++)
+                date = handle.call(date, parsed[i], bits[i]);
+            return date;
+        }
+    };
+};
+
+var handle = function(key, value){
+    if (!value){
+        if (key == 'm' || key == 'd') value = 1;
+        else return this;
+    }
+
+    switch(key){
+        case 'a': case 'A': return this.set('day', Date.parseDay(value, true));
+        case 'b': case 'B': return this.set('mo', Date.parseMonth(value, true));
+        case 'd': return this.set('date', value);
+        case 'H': case 'I': return this.set('hr', value);
+        case 'm': return this.set('mo', value - 1);
+        case 'M': return this.set('min', value);
+        case 'p': return this.set('ampm', value.replace(/\./g, ''));
+        case 'S': return this.set('sec', value);
+        case 's': return this.set('ms', ('0.' + value) * 1000);
+        case 'w': return this.set('day', value);
+        case 'Y': return this.set('year', value);
+        case 'y':
+            value = +value;
+            if (value < 100) value += yr_base + (value < yr_start ? 100 : 0);
+            return this.set('year', value);
+        case 'T':
+            if (value == 'Z') value = '+00';
+            var offset = value.match(/([+-])(\d{2}):?(\d{2})?/);
+            offset = (offset[1] + '1') * (offset[2] * 60 + (+offset[3] || 0)) + this.getTimezoneOffset();
+            return this.set('time', (this * 1) - offset * 60000);
+    }
+
+    return this;
+};
+
+Date.defineParsers(
+    '%Y([-./]%m([-./]%d((T| )%X)?)?)?', // "1999-12-31", "1999-12-31 11:59pm", "1999-12-31 23:59:59", ISO8601
+    '%Y%m%d(T%H(%M%S?)?)?', // "19991231", "19991231T1159", compact
+    '%x( %X)?', // "12/31", "12.31.99", "12-31-1999", "12/31/2008 11:59 PM"
+    '%d%o( %b( %Y)?)?( %X)?', // "31st", "31st December", "31 Dec 1999", "31 Dec 1999 11:59pm"
+    '%b %d%o?( %Y)?( %X)?', // Same as above with month and day switched
+    '%b %Y' // "December 1999"
+);
+
+MooTools.lang.addEvent('langChange', function(language){
+    if (!MooTools.lang.get('Date')) return;
+
+    lang = language;
+    Date.parsePatterns.each(function(pattern, i){
+        if (pattern.format) Date.parsePatterns[i] = build(pattern.format);
+    });
+
+}).fireEvent('langChange', MooTools.lang.getCurrentLanguage());
+
+})();
+
+/*
+Script: Element.Forms.js
+    Extends the Element native object to include methods useful in managing inputs.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+
+*/
+
+Element.implement({
+
+    tidy: function(){
+        this.set('value', this.get('value').tidy());
+    },
+
+    getTextInRange: function(start, end){
+        return this.get('value').substring(start, end);
+    },
+
+    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 pos = {start: 0, end: 0};
+        var range = this.getDocument().selection.createRange();
+        if (!range || range.parentElement() != this) return pos;
+        var dup = range.duplicate();
+        if (this.type == 'text') {
+            pos.start = 0 - dup.moveStart('character', -100000);
+            pos.end = pos.start + range.text.length;
+        } else {
+            var value = this.get('value');
+            var offset = value.length - value.match(/[\n\r]*$/)[0].length;
+            dup.moveToElementText(this);
+            dup.setEndPoint('StartToEnd', range);
+            pos.end = offset - dup.text.length;
+            dup.setEndPoint('StartToStart', range);
+            pos.start = offset - dup.text.length;
+        }
+        return pos;
+    },
+
+    getSelectionStart: function(){
+        return this.getSelectedRange().start;
+    },
+
+    getSelectionEnd: function(){
+        return this.getSelectedRange().end;
+    },
+
+    setCaretPosition: function(pos){
+        if (pos == 'end') pos = this.get('value').length;
+        this.selectRange(pos, pos);
+        return this;
+    },
+
+    getCaretPosition: function(){
+        return this.getSelectedRange().start;
+    },
+
+    selectRange: function(start, end){
+        if (this.setSelectionRange) {
+            this.focus();
+            this.setSelectionRange(start, end);
+        } else {
+            var value = this.get('value');
+            var diff = value.substr(start, end - start).replace(/\r/g, '').length;
+            start = value.substr(0, start).replace(/\r/g, '').length;
+            var range = this.createTextRange();
+            range.collapse(true);
+            range.moveEnd('character', start + diff);
+            range.moveStart('character', start);
+            range.select();
+        }
+        return this;
+    },
+
+    insertAtCursor: function(value, select){
+        var pos = this.getSelectedRange();
+        var text = this.get('value');
+        this.set('value', text.substring(0, pos.start) + value + text.substring(pos.end, text.length));
+        if ($pick(select, true)) this.selectRange(pos.start, pos.start + value.length);
+        else this.setCaretPosition(pos.start + value.length);
+        return this;
+    },
+
+    insertAroundCursor: function(options, select){
+        options = $extend({
+            before: '',
+            defaultMiddle: '',
+            after: ''
+        }, options);
+        var value = this.getSelectedText() || options.defaultMiddle;
+        var pos = this.getSelectedRange();
+        var text = this.get('value');
+        if (pos.start == pos.end){
+            this.set('value', text.substring(0, pos.start) + options.before + value + options.after + text.substring(pos.end, text.length));
+            this.selectRange(pos.start + options.before.length, pos.end + options.before.length + value.length);
+        } else {
+            var current = text.substring(pos.start, pos.end);
+            this.set('value', text.substring(0, pos.start) + options.before + current + options.after + text.substring(pos.end, text.length));
+            var selStart = pos.start + options.before.length;
+            if ($pick(select, true)) this.selectRange(selStart, selStart + current.length);
+            else this.setCaretPosition(selStart + text.length);
+        }
+        return this;
+    }
+
+});
+
+/*
+Script: Element.Measure.js
+    Extends the Element native object to include methods useful in measuring dimensions.
+
+    Element.measure / .expose methods by Daniel Steigerwald
+    License: MIT-style license.
+    Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+
+*/
+
+Element.implement({
+
+    measure: function(fn){
+        var vis = function(el) {
+            return !!(!el || el.offsetHeight || el.offsetWidth);
+        };
+        if (vis(this)) return fn.apply(this);
+        var parent = this.getParent(),
+            toMeasure = [], 
+            restorers = [];
+        while (!vis(parent) && parent != document.body) {
+            toMeasure.push(parent.expose());
+            parent = parent.getParent();
+        }
+        var restore = this.expose();
+        var result = fn.apply(this);
+        restore();
+        toMeasure.each(function(restore){
+            restore();
+        });
+        return result;
+    },
+
+    expose: function(){
+        if (this.getStyle('display') != 'none') return $empty;
+        var before = this.style.cssText;
+        this.setStyles({
+            display: 'block',
+            position: 'absolute',
+            visibility: 'hidden'
+        });
+        return function(){
+            this.style.cssText = before;
+        }.bind(this);
+    },
+
+    getDimensions: function(options){
+        options = $merge({computeSize: false},options);
+        var dim = {};
+        var getSize = function(el, options){
+            return (options.computeSize)?el.getComputedSize(options):el.getSize();
+        };
+        if (this.getStyle('display') == 'none'){
+            dim = this.measure(function(){
+                return getSize(this, options);
+            });
+        } else {
+            try { //safari sometimes crashes here, so catch it
+                dim = getSize(this, options);
+            }catch(e){}
+        }
+        return $chk(dim.x) ? $extend(dim, {width: dim.x, height: dim.y}) : $extend(dim, {x: dim.width, y: dim.height});
+    },
+
+    getComputedSize: function(options){
+        options = $merge({
+            styles: ['padding','border'],
+            plains: {
+                height: ['top','bottom'],
+                width: ['left','right']
+            },
+            mode: 'both'
+        }, options);
+        var size = {width: 0,height: 0};
+        switch (options.mode){
+            case 'vertical':
+                delete size.width;
+                delete options.plains.width;
+                break;
+            case 'horizontal':
+                delete size.height;
+                delete options.plains.height;
+                break;
+        }
+        var getStyles = [];
+        //this function might be useful in other places; perhaps it should be outside this function?
+        $each(options.plains, function(plain, key){
+            plain.each(function(edge){
+                options.styles.each(function(style){
+                    getStyles.push((style == 'border') ? style + '-' + edge + '-' + 'width' : style + '-' + edge);
+                });
+            });
+        });
+        var styles = {};
+        getStyles.each(function(style){ styles[style] = this.getComputedStyle(style); }, this);
+        var subtracted = [];
+        $each(options.plains, function(plain, key){ //keys: width, height, plains: ['left', 'right'], ['top','bottom']
+            var capitalized = key.capitalize();
+            size['total' + capitalized] = 0;
+            size['computed' + capitalized] = 0;
+            plain.each(function(edge){ //top, left, right, bottom
+                size['computed' + edge.capitalize()] = 0;
+                getStyles.each(function(style, i){ //padding, border, etc.
+                    //'padding-left'.test('left') size['totalWidth'] = size['width'] + [padding-left]
+                    if (style.test(edge)){
+                        styles[style] = styles[style].toInt() || 0; //styles['padding-left'] = 5;
+                        size['total' + capitalized] = size['total' + capitalized] + styles[style];
+                        size['computed' + edge.capitalize()] = size['computed' + edge.capitalize()] + styles[style];
+                    }
+                    //if width != width (so, padding-left, for instance), then subtract that from the total
+                    if (style.test(edge) && key != style &&
+                        (style.test('border') || style.test('padding')) && !subtracted.contains(style)){
+                        subtracted.push(style);
+                        size['computed' + capitalized] = size['computed' + capitalized]-styles[style];
+                    }
+                });
+            });
+        });
+
+        ['Width', 'Height'].each(function(value){
+            var lower = value.toLowerCase();
+            if(!$chk(size[lower])) return;
+
+            size[lower] = size[lower] + this['offset' + value] + size['computed' + value];
+            size['total' + value] = size[lower] + size['total' + value];
+            delete size['computed' + value];
+        }, this);
+
+        return $extend(styles, size);
+    }
+
+});
+
+/*
+Script: Element.Pin.js
+    Extends the Element native object to include the pin method useful for fixed positioning for elements.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+*/
+
+(function(){
+    var supportsPositionFixed = false;
+    window.addEvent('domready', function(){
+        var test = new Element('div').setStyles({
+            position: 'fixed',
+            top: 0,
+            right: 0
+        }).inject(document.body);
+        supportsPositionFixed = (test.offsetTop === 0);
+        test.dispose();
+    });
+
+    Element.implement({
+
+        pin: function(enable){
+            if (this.getStyle('display') == 'none') return null;
+            
+            var p;
+            if (enable !== false){
+                p = this.getPosition();
+                if (!this.retrieve('pinned')){
+                    var pos = {
+                        top: p.y - window.getScroll().y,
+                        left: p.x - window.getScroll().x
+                    };
+                    if (supportsPositionFixed){
+                        this.setStyle('position', 'fixed').setStyles(pos);
+                    } else {
+                        this.store('pinnedByJS', true);
+                        this.setStyles({
+                            position: 'absolute',
+                            top: p.y,
+                            left: p.x
+                        });
+                        this.store('scrollFixer', (function(){
+                            if (this.retrieve('pinned'))
+                                this.setStyles({
+                                    top: pos.top.toInt() + window.getScroll().y,
+                                    left: pos.left.toInt() + window.getScroll().x
+                                });
+                        }).bind(this));
+                        window.addEvent('scroll', this.retrieve('scrollFixer'));
+                    }
+                    this.store('pinned', true);
+                }
+            } else {
+                var op;
+                if (!Browser.Engine.trident){
+                    if (this.getParent().getComputedStyle('position') != 'static') op = this.getParent();
+                    else op = this.getParent().getOffsetParent();
+                }
+                p = this.getPosition(op);
+                this.store('pinned', false);
+                var reposition;
+                if (supportsPositionFixed && !this.retrieve('pinnedByJS')){
+                    reposition = {
+                        top: p.y + window.getScroll().y,
+                        left: p.x + window.getScroll().x
+                    };
+                } else {
+                    this.store('pinnedByJS', false);
+                    window.removeEvent('scroll', this.retrieve('scrollFixer'));
+                    reposition = {
+                        top: p.y,
+                        left: p.x
+                    };
+                }
+                this.setStyles($merge(reposition, {position: 'absolute'}));
+            }
+            return this.addClass('isPinned');
+        },
+
+        unpin: function(){
+            return this.pin(false).removeClass('isPinned');
+        },
+
+        togglepin: function(){
+            this.pin(!this.retrieve('pinned'));
+        }
+
+    });
+
+})();
+
+/*
+Script: Element.Position.js
+    Extends the Element native object to include methods useful positioning elements relative to others.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+*/
+
+(function(){
+
+var original = Element.prototype.position;
+
+Element.implement({
+
+    position: function(options){
+        //call original position if the options are x/y values
+        if (options && ($defined(options.x) || $defined(options.y))) return original ? original.apply(this, arguments) : this;
+        $each(options||{}, function(v, k){ if (!$defined(v)) delete options[k]; });
+        options = $merge({
+            relativeTo: document.body,
+            position: {
+                x: 'center', //left, center, right
+                y: 'center' //top, center, bottom
+            },
+            edge: false,
+            offset: {x: 0, y: 0},
+            returnPos: false,
+            relFixedPosition: false,
+            ignoreMargins: false,
+            allowNegative: false
+        }, options);
+        //compute the offset of the parent positioned element if this element is in one
+        var parentOffset = {x: 0, y: 0};
+        var parentPositioned = false;
+        /* dollar around getOffsetParent should not be necessary, but as it does not return
+         * a mootools extended element in IE, an error occurs on the call to expose. See:
+         * http://mootools.lighthouseapp.com/projects/2706/tickets/333-element-getoffsetparent-inconsistency-between-ie-and-other-browsers */
+        var offsetParent = this.measure(function(){
+            return document.id(this.getOffsetParent());
+        });
+        if (offsetParent && offsetParent != this.getDocument().body){
+            parentOffset = offsetParent.measure(function(){
+                return this.getPosition();
+            });
+            parentPositioned = true;
+            options.offset.x = options.offset.x - parentOffset.x;
+            options.offset.y = options.offset.y - parentOffset.y;
+        }
+        //upperRight, bottomRight, centerRight, upperLeft, bottomLeft, centerLeft
+        //topRight, topLeft, centerTop, centerBottom, center
+        var fixValue = function(option){
+            if ($type(option) != 'string') return option;
+            option = option.toLowerCase();
+            var val = {};
+            if (option.test('left')) val.x = 'left';
+            else if (option.test('right')) val.x = 'right';
+            else val.x = 'center';
+            if (option.test('upper') || option.test('top')) val.y = 'top';
+            else if (option.test('bottom')) val.y = 'bottom';
+            else val.y = 'center';
+            return val;
+        };
+        options.edge = fixValue(options.edge);
+        options.position = fixValue(options.position);
+        if (!options.edge){
+            if (options.position.x == 'center' && options.position.y == 'center') options.edge = {x:'center', y:'center'};
+            else options.edge = {x:'left', y:'top'};
+        }
+
+        this.setStyle('position', 'absolute');
+        var rel = document.id(options.relativeTo) || document.body;
+        var calc = rel == document.body ? window.getScroll() : rel.getPosition();
+        var top = calc.y;
+        var left = calc.x;
+
+        if (Browser.Engine.trident){
+            var scrolls = rel.getScrolls();
+            top += scrolls.y;
+            left += scrolls.x;
+        }
+
+        var dim = this.getDimensions({computeSize: true, styles:['padding', 'border','margin']});
+        if (options.ignoreMargins){
+            options.offset.x = options.offset.x - dim['margin-left'];
+            options.offset.y = options.offset.y - dim['margin-top'];
+        }
+        var pos = {};
+        var prefY = options.offset.y;
+        var prefX = options.offset.x;
+        var winSize = window.getSize();
+        switch(options.position.x){
+            case 'left':
+                pos.x = left + prefX;
+                break;
+            case 'right':
+                pos.x = left + prefX + rel.offsetWidth;
+                break;
+            default: //center
+                pos.x = left + ((rel == document.body ? winSize.x : rel.offsetWidth)/2) + prefX;
+                break;
+        }
+        switch(options.position.y){
+            case 'top':
+                pos.y = top + prefY;
+                break;
+            case 'bottom':
+                pos.y = top + prefY + rel.offsetHeight;
+                break;
+            default: //center
+                pos.y = top + ((rel == document.body ? winSize.y : rel.offsetHeight)/2) + prefY;
+                break;
+        }
+
+        if (options.edge){
+            var edgeOffset = {};
+
+            switch(options.edge.x){
+                case 'left':
+                    edgeOffset.x = 0;
+                    break;
+                case 'right':
+                    edgeOffset.x = -dim.x-dim.computedRight-dim.computedLeft;
+                    break;
+                default: //center
+                    edgeOffset.x = -(dim.x/2);
+                    break;
+            }
+            switch(options.edge.y){
+                case 'top':
+                    edgeOffset.y = 0;
+                    break;
+                case 'bottom':
+                    edgeOffset.y = -dim.y-dim.computedTop-dim.computedBottom;
+                    break;
+                default: //center
+                    edgeOffset.y = -(dim.y/2);
+                    break;
+            }
+            pos.x = pos.x + edgeOffset.x;
+            pos.y = pos.y + edgeOffset.y;
+        }
+        pos = {
+            left: ((pos.x >= 0 || parentPositioned || options.allowNegative) ? pos.x : 0).toInt(),
+            top: ((pos.y >= 0 || parentPositioned || options.allowNegative) ? pos.y : 0).toInt()
+        };
+        if (rel.getStyle('position') == 'fixed' || options.relFixedPosition){
+            var winScroll = window.getScroll();
+            pos.top = pos.top.toInt() + winScroll.y;
+            pos.left = pos.left.toInt() + winScroll.x;
+        }
+
+        if (options.returnPos) return pos;
+        else this.setStyles(pos);
+        return this;
+    }
+
+});
+
+})();
+
+/*
+Script: Element.Shortcuts.js
+    Extends the Element native object to include some shortcut methods.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+
+*/
+
+Element.implement({
+
+    isDisplayed: function(){
+        return this.getStyle('display') != 'none';
+    },
+
+    toggle: function(){
+        return this[this.isDisplayed() ? 'hide' : 'show']();
+    },
+
+    hide: function(){
+        var d;
+        try {
+            //IE fails here if the element is not in the dom
+            if ('none' != this.getStyle('display')) d = this.getStyle('display');
+        } catch(e){}
+
+        return this.store('originalDisplay', d || 'block').setStyle('display', 'none');
+    },
+
+    show: function(display){
+        return this.setStyle('display', display || this.retrieve('originalDisplay') || 'block');
+    },
+
+    swapClass: function(remove, add){
+        return this.removeClass(remove).addClass(add);
+    }
+
+});
+
+
+/*
+Script: FormValidator.js
+    A css-class based form validation system.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+*/
+var InputValidator = new Class({
+
+    Implements: [Options],
+
+    options: {
+        errorMsg: 'Validation failed.',
+        test: function(field){return true;}
+    },
+
+    initialize: function(className, options){
+        this.setOptions(options);
+        this.className = className;
+    },
+
+    test: function(field, props){
+        if (document.id(field)) return this.options.test(document.id(field), props||this.getProps(field));
+        else return false;
+    },
+
+    getError: function(field, props){
+        var err = this.options.errorMsg;
+        if ($type(err) == 'function') err = err(document.id(field), props||this.getProps(field));
+        return err;
+    },
+
+    getProps: function(field){
+        if (!document.id(field)) return {};
+        return field.get('validatorProps');
+    }
+
+});
+
+Element.Properties.validatorProps = {
+
+    set: function(props){
+        return this.eliminate('validatorProps').store('validatorProps', props);
+    },
+
+    get: function(props){
+        if (props) this.set(props);
+        if (this.retrieve('validatorProps')) return this.retrieve('validatorProps');
+        if (this.getProperty('validatorProps')){
+            try {
+                this.store('validatorProps', JSON.decode(this.getProperty('validatorProps')));
+            }catch(e){
+                return {};
+            }
+        } else {
+            var vals = this.get('class').split(' ').filter(function(cls){
+                return cls.test(':');
+            });
+            if (!vals.length){
+                this.store('validatorProps', {});
+            } else {
+                props = {};
+                vals.each(function(cls){
+                    var split = cls.split(':');
+                    if (split[1]) {
+                        try {
+                            props[split[0]] = JSON.decode(split[1]);
+                        } catch(e) {}
+                    }
+                });
+                this.store('validatorProps', props);
+            }
+        }
+        return this.retrieve('validatorProps');
+    }
+
+};
+
+var FormValidator = new Class({
+
+    Implements:[Options, Events],
+
+    Binds: ['onSubmit'],
+
+    options: {/*
+        onFormValidate: $empty(isValid, form, event),
+        onElementValidate: $empty(isValid, field, className, warn),
+        onElementPass: $empty(field),
+        onElementFail: $empty(field, validatorsFailed) */
+        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(form, options){
+        this.setOptions(options);
+        this.element = document.id(form);
+        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(fields){
+        fields.each(function(el){
+            if (this.options.evaluateFieldsOnBlur)
+                el.addEvent('blur', this.validationMonitor.pass([el, false], this));
+            if (this.options.evaluateFieldsOnChange)
+                el.addEvent('change', this.validationMonitor.pass([el, true], this));
+        }, this);
+    },
+
+    validationMonitor: function(){
+        $clear(this.timer);
+        this.timer = this.validateField.delay(50, this, arguments);
+    },
+
+    onSubmit: function(event){
+        if (!this.validate(event) && event) event.preventDefault();
+        else this.reset();
+    },
+
+    reset: function(){
+        this.getFields().each(this.resetField, this);
+        return this;
+    },
+
+    validate: function(event){
+        var result = this.getFields().map(function(field){
+            return this.validateField(field, true);
+        }, this).every(function(v){ return v;});
+        this.fireEvent('formValidate', [result, this.element, event]);
+        if (this.options.stopOnFailure && !result && event) event.preventDefault();
+        return result;
+    },
+
+    validateField: function(field, force){
+        if (this.paused) return true;
+        field = document.id(field);
+        var passed = !field.hasClass('validation-failed');
+        var failed, warned;
+        if (this.options.serial && !force){
+            failed = this.element.getElement('.validation-failed');
+            warned = this.element.getElement('.warning');
+        }
+        if (field && (!failed || force || field.hasClass('validation-failed') || (failed && !this.options.serial))){
+            var validators = field.className.split(' ').some(function(cn){
+                return this.getValidator(cn);
+            }, this);
+            var validatorsFailed = [];
+            field.className.split(' ').each(function(className){
+                if (className && !this.test(className, field)) validatorsFailed.include(className);
+            }, this);
+            passed = validatorsFailed.length === 0;
+            if (validators && !field.hasClass('warnOnly')){
+                if (passed){
+                    field.addClass('validation-passed').removeClass('validation-failed');
+                    this.fireEvent('elementPass', field);
+                } else {
+                    field.addClass('validation-failed').removeClass('validation-passed');
+                    this.fireEvent('elementFail', [field, validatorsFailed]);
+                }
+            }
+            if (!warned){
+                var warnings = field.className.split(' ').some(function(cn){
+                    if (cn.test('^warn-') || field.hasClass('warnOnly'))
+                        return this.getValidator(cn.replace(/^warn-/,''));
+                    else return null;
+                }, this);
+                field.removeClass('warning');
+                var warnResult = field.className.split(' ').map(function(cn){
+                    if (cn.test('^warn-') || field.hasClass('warnOnly'))
+                        return this.test(cn.replace(/^warn-/,''), field, true);
+                    else return null;
+                }, this);
+            }
+        }
+        return passed;
+    },
+
+    test: function(className, field, warn){
+        var validator = this.getValidator(className);
+        field = document.id(field);
+        if (field.hasClass('ignoreValidation')) return true;
+        warn = $pick(warn, false);
+        if (field.hasClass('warnOnly')) warn = true;
+        var isValid = validator ? validator.test(field) : true;
+        if (validator && this.isVisible(field)) this.fireEvent('elementValidate', [isValid, field, className, warn]);
+        if (warn) return true;
+        return isValid;
+    },
+
+    isVisible : function(field){
+        if (!this.options.ignoreHidden) return true;
+        while(field != document.body){
+            if (document.id(field).getStyle('display') == 'none') return false;
+            field = field.getParent();
+        }
+        return true;
+    },
+
+    resetField: function(field){
+        field = document.id(field);
+        if (field){
+            field.className.split(' ').each(function(className){
+                if (className.test('^warn-')) className = className.replace(/^warn-/, '');
+                field.removeClass('validation-failed');
+                field.removeClass('warning');
+                field.removeClass('validation-passed');
+            }, this);
+        }
+        return this;
+    },
+
+    stop: function(){
+        this.paused = true;
+        return this;
+    },
+
+    start: function(){
+        this.paused = false;
+        return this;
+    },
+
+    ignoreField: function(field, warn){
+        field = document.id(field);
+        if (field){
+            this.enforceField(field);
+            if (warn) field.addClass('warnOnly');
+            else field.addClass('ignoreValidation');
+        }
+        return this;
+    },
+
+    enforceField: function(field){
+        field = document.id(field);
+        if (field) field.removeClass('warnOnly').removeClass('ignoreValidation');
+        return this;
+    }
+
+});
+
+FormValidator.getMsg = function(key){
+    return MooTools.lang.get('FormValidator', key);
+};
+
+FormValidator.adders = {
+
+    validators:{},
+
+    add : function(className, options){
+        this.validators[className] = new InputValidator(className, options);
+        //if this is a class (this method is used by instances of FormValidator and the FormValidator namespace)
+        //extend these validators into it
+        //this allows validators to be global and/or per instance
+        if (!this.initialize){
+            this.implement({
+                validators: this.validators
+            });
+        }
+    },
+
+    addAllThese : function(validators){
+        $A(validators).each(function(validator){
+            this.add(validator[0], validator[1]);
+        }, this);
+    },
+
+    getValidator: function(className){
+        return this.validators[className.split(':')[0]];
+    }
+
+};
+
+$extend(FormValidator, FormValidator.adders);
+
+FormValidator.implement(FormValidator.adders);
+
+FormValidator.add('IsEmpty', {
+
+    errorMsg: false,
+    test: function(element){
+        if (element.type == 'select-one' || element.type == 'select')
+            return !(element.selectedIndex >= 0 && element.options[element.selectedIndex].value != '');
+        else
+            return ((element.get('value') == null) || (element.get('value').length == 0));
+    }
+
+});
+
+FormValidator.addAllThese([
+
+    ['required', {
+        errorMsg: function(){
+            return FormValidator.getMsg('required');
+        },
+        test: function(element){
+            return !FormValidator.getValidator('IsEmpty').test(element);
+        }
+    }],
+
+    ['minLength', {
+        errorMsg: function(element, props){
+            if ($type(props.minLength))
+                return FormValidator.getMsg('minLength').substitute({minLength:props.minLength,length:element.get('value').length });
+            else return '';
+        },
+        test: function(element, props){
+            if ($type(props.minLength)) return (element.get('value').length >= $pick(props.minLength, 0));
+            else return true;
+        }
+    }],
+
+    ['maxLength', {
+        errorMsg: function(element, props){
+            //props is {maxLength:10}
+            if ($type(props.maxLength))
+                return FormValidator.getMsg('maxLength').substitute({maxLength:props.maxLength,length:element.get('value').length });
+            else return '';
+        },
+        test: function(element, props){
+            //if the value is <= than the maxLength value, element passes test
+            return (element.get('value').length <= $pick(props.maxLength, 10000));
+        }
+    }],
+
+    ['validate-integer', {
+        errorMsg: FormValidator.getMsg.pass('integer'),
+        test: function(element){
+            return FormValidator.getValidator('IsEmpty').test(element) || (/^(-?[1-9]\d*|0)$/).test(element.get('value'));
+        }
+    }],
+
+    ['validate-numeric', {
+        errorMsg: FormValidator.getMsg.pass('numeric'),
+        test: function(element){
+            return FormValidator.getValidator('IsEmpty').test(element) ||
+                (/^-?(?:0$0(?=\d*\.)|[1-9]|0)\d*(\.\d+)?$/).test(element.get('value'));
+        }
+    }],
+
+    ['validate-digits', {
+        errorMsg: FormValidator.getMsg.pass('digits'),
+        test: function(element){
+            return FormValidator.getValidator('IsEmpty').test(element) || (/^[\d() .:\-\+#]+$/.test(element.get('value')));
+        }
+    }],
+
+    ['validate-alpha', {
+        errorMsg: FormValidator.getMsg.pass('alpha'),
+        test: function(element){
+            return FormValidator.getValidator('IsEmpty').test(element) ||  (/^[a-zA-Z]+$/).test(element.get('value'));
+        }
+    }],
+
+    ['validate-alphanum', {
+        errorMsg: FormValidator.getMsg.pass('alphanum'),
+        test: function(element){
+            return FormValidator.getValidator('IsEmpty').test(element) || !(/\W/).test(element.get('value'));
+        }
+    }],
+
+    ['validate-date', {
+        errorMsg: function(element, props){
+            if (Date.parse){
+                var format = props.dateFormat || '%x';
+                return FormValidator.getMsg('dateSuchAs').substitute({date: new Date().format(format)});
+            } else {
+                return FormValidator.getMsg('dateInFormatMDY');
+            }
+        },
+        test: function(element, props){
+            if (FormValidator.getValidator('IsEmpty').test(element)) return true;
+            var d;
+            if (Date.parse){
+                var format = props.dateFormat || '%x';
+                d = Date.parse(element.get('value'));
+                var formatted = d.format(format);
+                if (formatted != 'invalid date') element.set('value', formatted);
+                return !isNaN(d);
+            } else {
+                var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
+                if (!regex.test(element.get('value'))) return false;
+                d = new Date(element.get('value').replace(regex, '$1/$2/$3'));
+                return (parseInt(RegExp.$1, 10) == (1 + d.getMonth())) &&
+                    (parseInt(RegExp.$2, 10) == d.getDate()) &&
+                    (parseInt(RegExp.$3, 10) == d.getFullYear());
+            }
+        }
+    }],
+
+    ['validate-email', {
+        errorMsg: FormValidator.getMsg.pass('email'),
+        test: function(element){
+            return FormValidator.getValidator('IsEmpty').test(element) || (/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i).test(element.get('value'));
+        }
+    }],
+
+    ['validate-url', {
+        errorMsg: FormValidator.getMsg.pass('url'),
+        test: function(element){
+            return FormValidator.getValidator('IsEmpty').test(element) || (/^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i).test(element.get('value'));
+        }
+    }],
+
+    ['validate-currency-dollar', {
+        errorMsg: FormValidator.getMsg.pass('currencyDollar'),
+        test: function(element){
+            // [$]1[##][,###]+[.##]
+            // [$]1###+[.##]
+            // [$]0.##
+            // [$].##
+            return FormValidator.getValidator('IsEmpty').test(element) ||  (/^\$?\-?([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(element.get('value'));
+        }
+    }],
+
+    ['validate-one-required', {
+        errorMsg: FormValidator.getMsg.pass('oneRequired'),
+        test: function(element, props){
+            var p = document.id(props['validate-one-required']) || element.parentNode;
+            return p.getElements('input').some(function(el){
+                if (['checkbox', 'radio'].contains(el.get('type'))) return el.get('checked');
+                return el.get('value');
+            });
+        }
+    }]
+
+]);
+
+Element.Properties.validator = {
+
+    set: function(options){
+        var validator = this.retrieve('validator');
+        if (validator) validator.setOptions(options);
+        return this.store('validator:options');
+    },
+
+    get: function(options){
+        if (options || !this.retrieve('validator')){
+            if (options || !this.retrieve('validator:options')) this.set('validator', options);
+            this.store('validator', new FormValidator(this, this.retrieve('validator:options')));
+        }
+        return this.retrieve('validator');
+    }
+
+};
+
+Element.implement({
+
+    validate: function(options){
+        this.set('validator', options);
+        return this.get('validator', options).validate();
+    }
+
+});
+
+/*
+Script: Fx.Elements.js
+    Effect to change any number of CSS properties of any number of Elements.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Valerio Proietti
+*/
+
+Fx.Elements = new Class({
+
+    Extends: Fx.CSS,
+
+    initialize: function(elements, options){
+        this.elements = this.subject = $$(elements);
+        this.parent(options);
+    },
+
+    compute: function(from, to, delta){
+        var now = {};
+        for (var i in from){
+            var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
+            for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
+        }
+        return now;
+    },
+
+    set: function(now){
+        for (var i in now){
+            var iNow = now[i];
+            for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
+        }
+        return this;
+    },
+
+    start: function(obj){
+        if (!this.check(obj)) return this;
+        var from = {}, to = {};
+        for (var i in obj){
+            var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
+            for (var p in iProps){
+                var parsed = this.prepare(this.elements[i], p, iProps[p]);
+                iFrom[p] = parsed.from;
+                iTo[p] = parsed.to;
+            }
+        }
+        return this.parent(from, to);
+    }
+
+});
+
+/*
+Script: Fx.Accordion.js
+    An Fx.Elements extension which allows you to easily create accordion type controls.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Valerio Proietti
+*/
+
+var Accordion = Fx.Accordion = new Class({
+
+    Extends: Fx.Elements,
+
+    options: {/*
+        onActive: $empty(toggler, section),
+        onBackground: $empty(toggler, section),*/
+        display: 0,
+        show: false,
+        height: true,
+        width: false,
+        opacity: true,
+        fixedHeight: false,
+        fixedWidth: false,
+        wait: false,
+        alwaysHide: false,
+        trigger: 'click',
+        initialDisplayFx: true
+    },
+
+    initialize: function(){
+        var params = Array.link(arguments, {'container': Element.type, 'options': Object.type, 'togglers': $defined, 'elements': $defined});
+        this.parent(params.elements, params.options);
+        this.togglers = $$(params.togglers);
+        this.container = document.id(params.container);
+        this.previous = -1;
+        if (this.options.alwaysHide) this.options.wait = true;
+        if ($chk(this.options.show)){
+            this.options.display = false;
+            this.previous = this.options.show;
+        }
+        if (this.options.start){
+            this.options.display = false;
+            this.options.show = false;
+        }
+        this.effects = {};
+        if (this.options.opacity) this.effects.opacity = 'fullOpacity';
+        if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';
+        if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';
+        for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);
+        this.elements.each(function(el, i){
+            if (this.options.show === i){
+                this.fireEvent('active', [this.togglers[i], el]);
+            } else {
+                for (var fx in this.effects) el.setStyle(fx, 0);
+            }
+        }, this);
+        if ($chk(this.options.display)) this.display(this.options.display, this.options.initialDisplayFx);
+    },
+
+    addSection: function(toggler, element){
+        toggler = document.id(toggler);
+        element = document.id(element);
+        var test = this.togglers.contains(toggler);
+        this.togglers.include(toggler);
+        this.elements.include(element);
+        var idx = this.togglers.indexOf(toggler);
+        toggler.addEvent(this.options.trigger, this.display.bind(this, idx));
+        if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
+        if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
+        element.fullOpacity = 1;
+        if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;
+        if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;
+        element.setStyle('overflow', 'hidden');
+        if (!test){
+            for (var fx in this.effects) element.setStyle(fx, 0);
+        }
+        return this;
+    },
+
+    display: function(index, useFx){
+        useFx = $pick(useFx, true);
+        index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
+        if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
+        this.previous = index;
+        var obj = {};
+        this.elements.each(function(el, i){
+            obj[i] = {};
+            var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight > 0));
+            this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
+            for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
+        }, this);
+        return useFx ? this.start(obj) : this.set(obj);
+    }
+
+});
+
+/*
+Script: Fx.Move.js
+    Defines Fx.Move, a class that works with Element.Position.js to transition an element from one location to another.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+
+*/
+
+Fx.Move = new Class({
+
+    Extends: Fx.Morph,
+
+    options: {
+        relativeTo: document.body,
+        position: 'center',
+        edge: false,
+        offset: {x: 0, y: 0}
+    },
+
+    start: function(destination){
+        return this.parent(this.element.position($merge(this.options, destination, {returnPos: true})));
+    }
+
+});
+
+Element.Properties.move = {
+
+    set: function(options){
+        var morph = this.retrieve('move');
+        if (morph) morph.cancel();
+        return this.eliminate('move').store('move:options', $extend({link: 'cancel'}, options));
+    },
+
+    get: function(options){
+        if (options || !this.retrieve('move')){
+            if (options || !this.retrieve('move:options')) this.set('move', options);
+            this.store('move', new Fx.Move(this, this.retrieve('move:options')));
+        }
+        return this.retrieve('move');
+    }
+
+};
+
+Element.implement({
+
+    move: function(options){
+        this.get('move').start(options);
+        return this;
+    }
+
+});
+
+
+/*
+Script: Fx.Reveal.js
+    Defines Fx.Reveal, a class that shows and hides elements with a transition.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+
+*/
+
+Fx.Reveal = new Class({
+
+    Extends: Fx.Morph,
+
+    options: {/*      
+        onShow: $empty(thisElement),
+        onHide: $empty(thisElement),
+        onComplete: $empty(thisElement),
+        heightOverride: null,
+        widthOverride: null, */
+        styles: ['padding', 'border', 'margin'],
+        transitionOpacity: !Browser.Engine.trident4,
+        mode: 'vertical',
+        display: 'block',
+        hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed' : false
+    },
+
+    dissolve: function(){
+        try {
+            if (!this.hiding && !this.showing){
+                if (this.element.getStyle('display') != 'none'){
+                    this.hiding = true;
+                    this.showing = false;
+                    this.hidden = true;
+                    var startStyles = this.element.getComputedSize({
+                        styles: this.options.styles,
+                        mode: this.options.mode
+                    });
+                    var setToAuto = (this.element.style.height === ''||this.element.style.height == 'auto');
+                    this.element.setStyle('display', 'block');
+                    if (this.options.transitionOpacity) startStyles.opacity = 1;
+                    var zero = {};
+                    $each(startStyles, function(style, name){
+                        zero[name] = [style, 0];
+                    }, this);
+                    var overflowBefore = this.element.getStyle('overflow');
+                    this.element.setStyle('overflow', 'hidden');
+                    var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
+                    this.$chain.unshift(function(){
+                        if (this.hidden){
+                            this.hiding = false;
+                            $each(startStyles, function(style, name){
+                                startStyles[name] = style;
+                            }, this);
+                            this.element.setStyles($merge({display: 'none', overflow: overflowBefore}, startStyles));
+                            if (setToAuto){
+                                if (['vertical', 'both'].contains(this.options.mode)) this.element.style.height = '';
+                                if (['width', 'both'].contains(this.options.mode)) this.element.style.width = '';
+                            }
+                            if (hideThese) hideThese.setStyle('visibility', 'visible');
+                        }
+                        this.fireEvent('hide', this.element);
+                        this.callChain();
+                    }.bind(this));
+                    if (hideThese) hideThese.setStyle('visibility', 'hidden');
+                    this.start(zero);
+                } else {
+                    this.callChain.delay(10, this);
+                    this.fireEvent('complete', this.element);
+                    this.fireEvent('hide', this.element);
+                }
+            } else if (this.options.link == 'chain'){
+                this.chain(this.dissolve.bind(this));
+            } else if (this.options.link == 'cancel' && !this.hiding){
+                this.cancel();
+                this.dissolve();
+            }
+        } catch(e){
+            this.hiding = false;
+            this.element.setStyle('display', 'none');
+            this.callChain.delay(10, this);
+            this.fireEvent('complete', this.element);
+            this.fireEvent('hide', this.element);
+        }
+        return this;
+    },
+
+    reveal: function(){
+        try {
+            if (!this.showing && !this.hiding){
+                if (this.element.getStyle('display') == 'none' ||
+                     this.element.getStyle('visiblity') == 'hidden' ||
+                     this.element.getStyle('opacity') == 0){
+                    this.showing = true;
+                    this.hiding = false;
+                    this.hidden = false;
+                    var setToAuto, startStyles;
+                    //toggle display, but hide it
+                    this.element.measure(function(){
+                        setToAuto = (this.element.style.height === '' || this.element.style.height == 'auto');
+                        //create the styles for the opened/visible state
+                        startStyles = this.element.getComputedSize({
+                            styles: this.options.styles,
+                            mode: this.options.mode
+                        });
+                    }.bind(this));
+                    $each(startStyles, function(style, name){
+                        startStyles[name] = style;
+                    });
+                    //if we're overridding height/width
+                    if ($chk(this.options.heightOverride)) startStyles.height = this.options.heightOverride.toInt();
+                    if ($chk(this.options.widthOverride)) startStyles.width = this.options.widthOverride.toInt();
+                    if (this.options.transitionOpacity) {
+                        this.element.setStyle('opacity', 0);
+                        startStyles.opacity = 1;
+                    }
+                    //create the zero state for the beginning of the transition
+                    var zero = {
+                        height: 0,
+                        display: this.options.display
+                    };
+                    $each(startStyles, function(style, name){ zero[name] = 0; });
+                    var overflowBefore = this.element.getStyle('overflow');
+                    //set to zero
+                    this.element.setStyles($merge(zero, {overflow: 'hidden'}));
+                    //hide inputs
+                    var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
+                    if (hideThese) hideThese.setStyle('visibility', 'hidden');
+                    //start the effect
+                    this.start(startStyles);
+                    this.$chain.unshift(function(){
+                        this.element.setStyle('overflow', overflowBefore);
+                        if (!this.options.heightOverride && setToAuto){
+                            if (['vertical', 'both'].contains(this.options.mode)) this.element.style.height = '';
+                            if (['width', 'both'].contains(this.options.mode)) this.element.style.width = '';
+                        }
+                        if (!this.hidden) this.showing = false;
+                        if (hideThese) hideThese.setStyle('visibility', 'visible');
+                        this.callChain();
+                        this.fireEvent('show', this.element);
+                    }.bind(this));
+                } else {
+                    this.callChain();
+                    this.fireEvent('complete', this.element);
+                    this.fireEvent('show', this.element);
+                }
+            } else if (this.options.link == 'chain'){
+                this.chain(this.reveal.bind(this));
+            } else if (this.options.link == 'cancel' && !this.showing){
+                this.cancel();
+                this.reveal();
+            }
+        } catch(e){
+            this.element.setStyles({
+                display: this.options.display,
+                visiblity: 'visible',
+                opacity: 1
+            });
+            this.showing = false;
+            this.callChain.delay(10, this);
+            this.fireEvent('complete', this.element);
+            this.fireEvent('show', this.element);
+        }
+        return this;
+    },
+
+    toggle: function(){
+        if (this.element.getStyle('display') == 'none' ||
+             this.element.getStyle('visiblity') == 'hidden' ||
+             this.element.getStyle('opacity') == 0){
+            this.reveal();
+        } else {
+            this.dissolve();
+        }
+        return this;
+    }
+
+});
+
+Element.Properties.reveal = {
+
+    set: function(options){
+        var reveal = this.retrieve('reveal');
+        if (reveal) reveal.cancel();
+        return this.eliminate('reveal').store('reveal:options', $extend({link: 'cancel'}, options));
+    },
+
+    get: function(options){
+        if (options || !this.retrieve('reveal')){
+            if (options || !this.retrieve('reveal:options')) this.set('reveal', options);
+            this.store('reveal', new Fx.Reveal(this, this.retrieve('reveal:options')));
+        }
+        return this.retrieve('reveal');
+    }
+
+};
+
+Element.Properties.dissolve = Element.Properties.reveal;
+
+Element.implement({
+
+    reveal: function(options){
+        this.get('reveal', options).reveal();
+        return this;
+    },
+
+    dissolve: function(options){
+        this.get('reveal', options).dissolve();
+        return this;
+    },
+
+    nix: function(){
+        var params = Array.link(arguments, {destroy: Boolean.type, options: Object.type});
+        this.get('reveal', params.options).dissolve().chain(function(){
+            this[params.destroy ? 'destroy' : 'dispose']();
+        }.bind(this));
+        return this;
+    },
+
+    wink: function(){
+        var params = Array.link(arguments, {duration: Number.type, options: Object.type});
+        var reveal = this.get('reveal', params.options);
+        reveal.reveal().chain(function(){
+            (function(){
+                reveal.dissolve();
+            }).delay(params.duration || 2000);
+        });
+    }
+
+
+});
+
+/*
+Script: Fx.Scroll.js
+    Effect to smoothly scroll any element, including the window.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Valerio Proietti
+*/
+
+Fx.Scroll = new Class({
+
+    Extends: Fx,
+
+    options: {
+        offset: {x: 0, y: 0},
+        wheelStops: true
+    },
+
+    initialize: function(element, options){
+        this.element = this.subject = document.id(element);
+        this.parent(options);
+        var cancel = this.cancel.bind(this, false);
+
+        if ($type(this.element) != 'element') this.element = document.id(this.element.getDocument().body);
+
+        var stopper = this.element;
+
+        if (this.options.wheelStops){
+            this.addEvent('start', function(){
+                stopper.addEvent('mousewheel', cancel);
+            }, true);
+            this.addEvent('complete', function(){
+                stopper.removeEvent('mousewheel', cancel);
+            }, true);
+        }
+    },
+
+    set: function(){
+        var now = Array.flatten(arguments);
+        this.element.scrollTo(now[0], now[1]);
+    },
+
+    compute: function(from, to, delta){
+        return [0, 1].map(function(i){
+            return Fx.compute(from[i], to[i], delta);
+        });
+    },
+
+    start: function(x, y){
+        if (!this.check(x, y)) return this;
+        var offsetSize = this.element.getSize(), scrollSize = this.element.getScrollSize();
+        var scroll = this.element.getScroll(), values = {x: x, y: y};
+        for (var z in values){
+            var max = scrollSize[z] - offsetSize[z];
+            if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z].limit(0, max) : max;
+            else values[z] = scroll[z];
+            values[z] += this.options.offset[z];
+        }
+        return this.parent([scroll.x, scroll.y], [values.x, values.y]);
+    },
+
+    toTop: function(){
+        return this.start(false, 0);
+    },
+
+    toLeft: function(){
+        return this.start(0, false);
+    },
+
+    toRight: function(){
+        return this.start('right', false);
+    },
+
+    toBottom: function(){
+        return this.start(false, 'bottom');
+    },
+
+    toElement: function(el){
+        var position = document.id(el).getPosition(this.element);
+        return this.start(position.x, position.y);
+    },
+
+    scrollIntoView: function(el, axes, offset){
+        axes = axes ? $splat(axes) : ['x','y'];
+        var to = {};
+        el = document.id(el);
+        var pos = el.getPosition(this.element);
+        var size = el.getSize();
+        var scroll = this.element.getScroll();
+        var containerSize = this.element.getSize();
+        var edge = {
+            x: pos.x + size.x,
+            y: pos.y + size.y
+        };
+        ['x','y'].each(function(axis) {
+            if (axes.contains(axis)) {
+                if (edge[axis] > scroll[axis] + containerSize[axis]) to[axis] = edge[axis] - containerSize[axis];
+                if (pos[axis] < scroll[axis]) to[axis] = pos[axis];
+            }
+            if (to[axis] == null) to[axis] = scroll[axis];
+            if (offset && offset[axis]) to[axis] = to[axis] + offset[axis];
+        }, this);
+        if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y);
+        return this;
+    }
+
+});
+
+
+/*
+Script: Fx.Slide.js
+    Effect to slide an element in and out of view.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Valerio Proietti
+*/
+
+Fx.Slide = new Class({
+
+    Extends: Fx,
+
+    options: {
+        mode: 'vertical'
+    },
+
+    initialize: function(element, options){
+        this.addEvent('complete', function(){
+            this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);
+            if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper);
+        }, true);
+        this.element = this.subject = document.id(element);
+        this.parent(options);
+        var wrapper = this.element.retrieve('wrapper');
+        this.wrapper = wrapper || new Element('div', {
+            styles: $extend(this.element.getStyles('margin', 'position'), {overflow: 'hidden'})
+        }).wraps(this.element);
+        this.element.store('wrapper', this.wrapper).setStyle('margin', 0);
+        this.now = [];
+        this.open = true;
+    },
+
+    vertical: function(){
+        this.margin = 'margin-top';
+        this.layout = 'height';
+        this.offset = this.element.offsetHeight;
+    },
+
+    horizontal: function(){
+        this.margin = 'margin-left';
+        this.layout = 'width';
+        this.offset = this.element.offsetWidth;
+    },
+
+    set: function(now){
+        this.element.setStyle(this.margin, now[0]);
+        this.wrapper.setStyle(this.layout, now[1]);
+        return this;
+    },
+
+    compute: function(from, to, delta){
+        return [0, 1].map(function(i){
+            return Fx.compute(from[i], to[i], delta);
+        });
+    },
+
+    start: function(how, mode){
+        if (!this.check(how, mode)) return this;
+        this[mode || this.options.mode]();
+        var margin = this.element.getStyle(this.margin).toInt();
+        var layout = this.wrapper.getStyle(this.layout).toInt();
+        var caseIn = [[margin, layout], [0, this.offset]];
+        var caseOut = [[margin, layout], [-this.offset, 0]];
+        var start;
+        switch (how){
+            case 'in': start = caseIn; break;
+            case 'out': start = caseOut; break;
+            case 'toggle': start = (layout == 0) ? caseIn : caseOut;
+        }
+        return this.parent(start[0], start[1]);
+    },
+
+    slideIn: function(mode){
+        return this.start('in', mode);
+    },
+
+    slideOut: function(mode){
+        return this.start('out', mode);
+    },
+
+    hide: function(mode){
+        this[mode || this.options.mode]();
+        this.open = false;
+        return this.set([-this.offset, 0]);
+    },
+
+    show: function(mode){
+        this[mode || this.options.mode]();
+        this.open = true;
+        return this.set([0, this.offset]);
+    },
+
+    toggle: function(mode){
+        return this.start('toggle', mode);
+    }
+
+});
+
+Element.Properties.slide = {
+
+    set: function(options){
+        var slide = this.retrieve('slide');
+        if (slide) slide.cancel();
+        return this.eliminate('slide').store('slide:options', $extend({link: 'cancel'}, options));
+    },
+
+    get: function(options){
+        if (options || !this.retrieve('slide')){
+            if (options || !this.retrieve('slide:options')) this.set('slide', options);
+            this.store('slide', new Fx.Slide(this, this.retrieve('slide:options')));
+        }
+        return this.retrieve('slide');
+    }
+
+};
+
+Element.implement({
+
+    slide: function(how, mode){
+        how = how || 'toggle';
+        var slide = this.get('slide'), toggle;
+        switch (how){
+            case 'hide': slide.hide(mode); break;
+            case 'show': slide.show(mode); break;
+            case 'toggle':
+                var flag = this.retrieve('slide:flag', slide.open);
+                slide[flag ? 'slideOut' : 'slideIn'](mode);
+                this.store('slide:flag', !flag);
+                toggle = true;
+            break;
+            default: slide.start(how, mode);
+        }
+        if (!toggle) this.eliminate('slide:flag');
+        return this;
+    }
+
+});
+
+
+/*
+Script: Fx.SmoothScroll.js
+    Class for creating a smooth scrolling effect to all internal links on the page.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Valerio Proietti
+*/
+
+var SmoothScroll = Fx.SmoothScroll = new Class({
+
+    Extends: Fx.Scroll,
+
+    initialize: function(options, context){
+        context = context || document;
+        this.doc = context.getDocument();
+        var win = context.getWindow();
+        this.parent(this.doc, options);
+        this.links = this.options.links ? $$(this.options.links) : $$(this.doc.links);
+        var location = win.location.href.match(/^[^#]*/)[0] + '#';
+        this.links.each(function(link){
+            if (link.href.indexOf(location) != 0) {return;}
+            var anchor = link.href.substr(location.length);
+            if (anchor) this.useLink(link, anchor);
+        }, this);
+        if (!Browser.Engine.webkit419) {
+            this.addEvent('complete', function(){
+                win.location.hash = this.anchor;
+            }, true);
+        }
+    },
+
+    useLink: function(link, anchor){
+        var el;
+        link.addEvent('click', function(event){
+            if (el !== false && !el) el = document.id(anchor) || this.doc.getElement('a[name=' + anchor + ']');
+            if (el) {
+                event.preventDefault();
+                this.anchor = anchor;
+                this.toElement(el);
+                link.blur();
+            }
+        }.bind(this));
+    }
+
+});
+
+/*
+Script: Drag.js
+    The base Drag Class. Can be used to drag and resize Elements using mouse events.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Valerio Proietti
+        Tom Occhinno
+        Jan Kassens
+*/
+
+var Drag = new Class({
+
+    Implements: [Events, Options],
+
+    options: {/*
+        onBeforeStart: $empty(thisElement),
+        onStart: $empty(thisElement, event),
+        onSnap: $empty(thisElement)
+        onDrag: $empty(thisElement, event),
+        onCancel: $empty(thisElement),
+        onComplete: $empty(thisElement, event),*/
+        snap: 6,
+        unit: 'px',
+        grid: false,
+        style: true,
+        limit: false,
+        handle: false,
+        invert: false,
+        preventDefault: false,
+        modifiers: {x: 'left', y: 'top'}
+    },
+
+    initialize: function(){
+        var params = Array.link(arguments, {'options': Object.type, 'element': $defined});
+        this.element = document.id(params.element);
+        this.document = this.element.getDocument();
+        this.setOptions(params.options || {});
+        var htype = $type(this.options.handle);
+        this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element;
+        this.mouse = {'now': {}, 'pos': {}};
+        this.value = {'start': {}, 'now': {}};
+
+        this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';
+
+        this.bound = {
+            start: this.start.bind(this),
+            check: this.check.bind(this),
+            drag: this.drag.bind(this),
+            stop: this.stop.bind(this),
+            cancel: this.cancel.bind(this),
+            eventStop: $lambda(false)
+        };
+        this.attach();
+    },
+
+    attach: function(){
+        this.handles.addEvent('mousedown', this.bound.start);
+        return this;
+    },
+
+    detach: function(){
+        this.handles.removeEvent('mousedown', this.bound.start);
+        return this;
+    },
+
+    start: function(event){
+        if (this.options.preventDefault) event.preventDefault();
+        this.mouse.start = event.page;
+        this.fireEvent('beforeStart', this.element);
+        var limit = this.options.limit;
+        this.limit = {x: [], y: []};
+        for (var z in this.options.modifiers){
+            if (!this.options.modifiers[z]) continue;
+            if (this.options.style) this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt();
+            else this.value.now[z] = this.element[this.options.modifiers[z]];
+            if (this.options.invert) this.value.now[z] *= -1;
+            this.mouse.pos[z] = event.page[z] - this.value.now[z];
+            if (limit && limit[z]){
+                for (var i = 2; i--; i){
+                    if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
+                }
+            }
+        }
+        if ($type(this.options.grid) == 'number') this.options.grid = {x: this.options.grid, y: this.options.grid};
+        this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
+        this.document.addEvent(this.selection, this.bound.eventStop);
+    },
+
+    check: function(event){
+        if (this.options.preventDefault) event.preventDefault();
+        var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
+        if (distance > this.options.snap){
+            this.cancel();
+            this.document.addEvents({
+                mousemove: this.bound.drag,
+                mouseup: this.bound.stop
+            });
+            this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element);
+        }
+    },
+
+    drag: function(event){
+        if (this.options.preventDefault) event.preventDefault();
+        this.mouse.now = event.page;
+        for (var z in this.options.modifiers){
+            if (!this.options.modifiers[z]) continue;
+            this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
+            if (this.options.invert) this.value.now[z] *= -1;
+            if (this.options.limit && this.limit[z]){
+                if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){
+                    this.value.now[z] = this.limit[z][1];
+                } else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){
+                    this.value.now[z] = this.limit[z][0];
+                }
+            }
+            if (this.options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % this.options.grid[z]);
+            if (this.options.style) this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
+            else this.element[this.options.modifiers[z]] = this.value.now[z];
+        }
+        this.fireEvent('drag', [this.element, event]);
+    },
+
+    cancel: function(event){
+        this.document.removeEvent('mousemove', this.bound.check);
+        this.document.removeEvent('mouseup', this.bound.cancel);
+        if (event){
+            this.document.removeEvent(this.selection, this.bound.eventStop);
+            this.fireEvent('cancel', this.element);
+        }
+    },
+
+    stop: function(event){
+        this.document.removeEvent(this.selection, this.bound.eventStop);
+        this.document.removeEvent('mousemove', this.bound.drag);
+        this.document.removeEvent('mouseup', this.bound.stop);
+        if (event) this.fireEvent('complete', [this.element, event]);
+    }
+
+});
+
+Element.implement({
+
+    makeResizable: function(options){
+        var drag = new Drag(this, $merge({modifiers: {x: 'width', y: 'height'}}, options));
+        this.store('resizer', drag);
+        return drag.addEvent('drag', function(){
+            this.fireEvent('resize', drag);
+        }.bind(this));
+    }
+
+});
+
+
+/*
+Script: Slider.js
+    Class for creating horizontal and vertical slider controls.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Valerio Proietti
+*/
+
+var Slider = new Class({
+
+    Implements: [Events, Options],
+
+    Binds: ['clickedElement', 'draggedKnob', 'scrolledElement'],
+
+    options: {
+        onTick: function(position){
+            if (this.options.snap) position = this.toPosition(this.step);
+            this.knob.setStyle(this.property, position);
+        },
+        snap: false,
+        offset: 0,
+        range: false,
+        wheel: false,
+        steps: 100,
+        mode: 'horizontal'
+    },
+
+    initialize: function(element, knob, options){
+        this.setOptions(options);
+        this.element = document.id(element);
+        this.knob = document.id(knob);
+        this.previousChange = this.previousEnd = this.step = -1;
+        var offset, limit = {}, modifiers = {'x': false, 'y': false};
+        switch (this.options.mode){
+            case 'vertical':
+                this.axis = 'y';
+                this.property = 'top';
+                offset = 'offsetHeight';
+                break;
+            case 'horizontal':
+                this.axis = 'x';
+                this.property = 'left';
+                offset = 'offsetWidth';
+        }
+        this.half = this.knob[offset] / 2;
+        this.full = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
+        this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
+        this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
+        this.range = this.max - this.min;
+        this.steps = this.options.steps || this.full;
+        this.stepSize = Math.abs(this.range) / this.steps;
+        this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;
+
+        this.knob.setStyle('position', 'relative').setStyle(this.property, - this.options.offset);
+        modifiers[this.axis] = this.property;
+        limit[this.axis] = [- this.options.offset, this.full - this.options.offset];
+
+        this.bound = {
+            clickedElement: this.clickedElement.bind(this),
+            scrolledElement: this.scrolledElement.bindWithEvent(this),
+            draggedKnob: this.draggedKnob.bind(this)
+        };
+
+        var dragOptions = {
+            snap: 0,
+            limit: limit,
+            modifiers: modifiers,
+            onDrag: this.bound.draggedKnob,
+            onStart: this.bound.draggedKnob,
+            onBeforeStart: (function(){
+                this.isDragging = true;
+            }).bind(this),
+            onComplete: function(){
+                this.isDragging = false;
+                this.draggedKnob();
+                this.end();
+            }.bind(this)
+        };
+        if (this.options.snap){
+            dragOptions.grid = Math.ceil(this.stepWidth);
+            dragOptions.limit[this.axis][1] = this.full;
+        }
+
+        this.drag = new Drag(this.knob, dragOptions);
+        this.attach();
+    },
+
+    attach: function(){
+        this.element.addEvent('mousedown', this.bound.clickedElement);
+
+        // keep track from firing the 'mousedown' event if the knob has been hovored
+        this.knob.addEvent('mouseenter',function(){
+        this.element.removeEvents('mousedown');
+        }.bind(this));
+        this.knob.addEvent('mouseleave',function(){
+        this.element.addEvent('mousedown', this.clickedElement.bind(this));
+        }.bind(this));
+
+        if (this.options.wheel) this.element.addEvent('mousewheel', this.bound.scrolledElement);
+        this.drag.attach();
+        return this;
+    },
+
+    detach: function(){
+        this.element.removeEvent('mousedown', this.bound.clickedElement);
+        this.element.removeEvent('mousewheel', this.bound.scrolledElement);
+        this.drag.detach();
+        return this;
+    },
+
+    set: function(step, check){
+        if (arguments.length == 1)
+            check = true;
+        if (!((this.range > 0) ^ (step < this.min))) step = this.min;
+        if (!((this.range > 0) ^ (step > this.max))) step = this.max;
+
+        this.step = Math.round(step);
+        if (check)
+            this.checkStep();
+        this.fireEvent('tick', this.toPosition(this.step));
+        this.end();
+        return this;
+    },
+
+    clickedElement: function(event){
+        if (this.isDragging || event.target == this.knob) return;
+
+        var dir = this.range < 0 ? -1 : 1;
+        var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
+        position = position.limit(-this.options.offset, this.full -this.options.offset);
+
+        this.step = Math.round(this.min + dir * this.toStep(position));
+        this.checkStep();
+        this.fireEvent('tick', position);
+        this.end();
+    },
+
+    scrolledElement: function(event){
+        var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
+        this.set(mode ? this.step - this.stepSize : this.step + this.stepSize);
+        event.stop();
+    },
+
+    draggedKnob: function(){
+        var dir = this.range < 0 ? -1 : 1;
+        var position = this.drag.value.now[this.axis];
+        position = position.limit(-this.options.offset, this.full -this.options.offset);
+        this.step = Math.round(this.min + dir * this.toStep(position));
+        this.checkStep();
+    },
+
+    checkStep: function(){
+        if (this.previousChange != this.step){
+            this.previousChange = this.step;
+            this.fireEvent('change', this.step);
+        }
+    },
+
+    end: function(){
+        if (this.previousEnd !== this.step){
+            this.previousEnd = this.step;
+            this.fireEvent('complete', this.step + '');
+        }
+    },
+
+    toStep: function(position){
+        var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
+        return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
+    },
+
+    toPosition: function(step){
+        return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
+    }
+
+});
+
+/*
+Script: Tips.js
+    Class for creating nice tips that follow the mouse cursor when hovering an element.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Valerio Proietti
+        Christoph Pojer
+*/
+
+var Tips = new Class({
+
+    Implements: [Events, Options],
+
+    options: {
+        onShow: function(tip){
+            tip.setStyle('visibility', 'visible');
+        },
+        onHide: function(tip){
+            tip.setStyle('visibility', 'hidden');
+        },
+        title: 'title',
+        text: function(el){
+            return el.get('rel') || el.get('href');
+        },
+        showDelay: 100,
+        hideDelay: 100,
+        className: null,
+        offset: {x: 16, y: 16},
+        fixed: false
+    },
+
+    initialize: function(){
+        var params = Array.link(arguments, {options: Object.type, elements: $defined});
+        if (params.options && params.options.offsets) params.options.offset = params.options.offsets;
+        this.setOptions(params.options);
+        this.container = new Element('div', {'class': 'tip'});
+        this.tip = this.getTip();
+        
+        if (params.elements) this.attach(params.elements);
+    },
+
+    getTip: function(){
+        return new Element('div', {
+            'class': this.options.className,
+            styles: {
+                visibility: 'hidden',
+                display: 'none',
+                position: 'absolute',
+                top: 0,
+                left: 0
+            }
+        }).adopt(
+            new Element('div', {'class': 'tip-top'}),
+            this.container,
+            new Element('div', {'class': 'tip-bottom'})
+        ).inject(document.body);
+    },
+
+    attach: function(elements){
+        var read = function(option, element){
+            if (option == null) return '';
+            return $type(option) == 'function' ? option(element) : element.get(option);
+        };
+        $$(elements).each(function(element){
+            var title = read(this.options.title, element);
+            element.erase('title').store('tip:native', title).retrieve('tip:title', title);
+            element.retrieve('tip:text', read(this.options.text, element));
+            
+            var events = ['enter', 'leave'];
+            if (!this.options.fixed) events.push('move');
+            
+            events.each(function(value){
+                element.addEvent('mouse' + value, element.retrieve('tip:' + value, this['element' + value.capitalize()].bindWithEvent(this, element)));
+            }, this);
+        }, this);
+        
+        return this;
+    },
+
+    detach: function(elements){
+        $$(elements).each(function(element){
+            ['enter', 'leave', 'move'].each(function(value){
+                element.removeEvent('mouse' + value, element.retrieve('tip:' + value) || $empty);
+            });
+            
+            element.eliminate('tip:enter').eliminate('tip:leave').eliminate('tip:move');
+            
+            if ($type(this.options.title) == 'string' && this.options.title == 'title'){
+                var original = element.retrieve('tip:native');
+                if (original) element.set('title', original);
+            }
+        }, this);
+        
+        return this;
+    },
+
+    elementEnter: function(event, element){
+        $A(this.container.childNodes).each(Element.dispose);
+        
+        ['title', 'text'].each(function(value){
+            var content = element.retrieve('tip:' + value);
+            if (!content) return;
+            
+            this[value + 'Element'] = new Element('div', {'class': 'tip-' + value}).inject(this.container);
+            this.fill(this[value + 'Element'], content);
+        }, this);
+        
+        this.timer = $clear(this.timer);
+        this.timer = this.show.delay(this.options.showDelay, this, element);
+        this.tip.setStyle('display', 'block');
+        this.position((!this.options.fixed) ? event : {page: element.getPosition()});
+    },
+
+    elementLeave: function(event, element){
+        $clear(this.timer);
+        this.tip.setStyle('display', 'none');
+        this.timer = this.hide.delay(this.options.hideDelay, this, element);
+    },
+
+    elementMove: function(event){
+        this.position(event);
+    },
+
+    position: function(event){
+        var size = window.getSize(), scroll = window.getScroll(),
+            tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight},
+            props = {x: 'left', y: 'top'},
+            obj = {};
+        
+        for (var z in props){
+            obj[props[z]] = event.page[z] + this.options.offset[z];
+            if ((obj[props[z]] + tip[z] - scroll[z]) > size[z]) obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z];
+        }
+        
+        this.tip.setStyles(obj);
+    },
+
+    fill: function(element, contents){
+        if(typeof contents == 'string') element.set('html', contents);
+        else element.adopt(contents);
+    },
+
+    show: function(el){
+        this.fireEvent('show', [this.tip, el]);
+    },
+
+    hide: function(el){
+        this.fireEvent('hide', [this.tip, el]);
+    }
+
+});
+
+/*
+Script: Date.English.US.js
+    Date messages for US English.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+
+*/
+
+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'],
+    //culture's date order: MM/DD/YYYY
+    dateOrder: ['month', 'date', 'year'],
+    shortDate: '%m/%d/%Y',
+    shortTime: '%I:%M%p',
+    AM: 'AM',
+    PM: 'PM',
+
+    /* Date.Extras */
+    ordinal: function(dayOfMonth){
+        //1st, 2nd, 3rd, etc.
+        return (dayOfMonth > 3 && dayOfMonth < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][Math.min(dayOfMonth % 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'
+
+});
+
+/*
+Script: FormValidator.English.js
+    Date messages for English.
+
+    License:
+        MIT-style license.
+
+    Authors:
+        Aaron Newton
+
+*/
+
+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 at 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: ',
+
+    //FormValidator.Extras
+
+    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.'
+
 });
\ No newline at end of file



More information about the rhmessaging-commits mailing list