        /*
        todo: 
            add events (pickerShown, pickerHidden)            
            make it so that you can click the previous/next month's days to jump there
            make it so you cant select text within it
            keyboard control
            make hidden by default and add show/hide
            fix transparent png issue
            cross browser it       
            
            monthYearPickerShown
            dayClick
            todayClick
            monthChanged
            monthYearPickerMonthClick
            monthYearPickerYearClick
            monthYearPickerPreviousYearClick
            monthYearPickerNextYearClick
            monthYearPickerHidden
            gridRedrawn
            closeClick
            inputUpdate
            shown
            hidden
            beforeshow
        */
        
        document.observe('datepicker:monthChanged',function(e){
            //alert('month changed');
        });
        
        Object.extend(Date.prototype, {
            daysInCurrentMonth: function() {
                var dd = new Date(this.getYear(), this.getMonth(), 0);
                return dd.getDate();
            },
            succ:function(){
                return this.clone().add(1,'day');
            }
            
        });
        Object.extend(String.prototype, {
            parseInt: function() {
                return parseInt(this, 10);
            }
        });
        Element.addMethods({
          scrollTo: function(element, left, top){
            var element = $(element);
            if (arguments.length == 1){
              var pos = element.cumulativeOffset();
              window.scrollTo(pos[0], pos[1]);
            } else {
              element.scrollLeft = left;
              element.scrollTop  = top;
            }
            return element;
          }
        });


        var SniperSystemsDatePicker = Class.create({
            defaultOptions: {
                closeOnChoice: false,
                closeOnDocumentClick: false,
                nohover: false,
                autoshow: false,
                addclass: '',
                timepickerinterval: 15,
                datetimeFormat: '%#d %b %Y %#I:%M %p',
                dateFormat: '%#d %b %Y',
                readonly: false,
                nodefault: false,
                timepickerinterval: 15,
                inputcontaineraddclass: '',
                relativepositioning: false,
                deferrender: false
            },
            setOptions: function (options) {
                this.options = Object.clone(this.defaultOptions);
                Object.extend(this.options, options || {});
                if (this.options.MaxDate) this.options.MaxDate = $D(this.options.MaxDate);
                if (this.options.MinDate) this.options.MinDate = $D(this.options.MinDate);
                return this.options;
            },
            setInputValue: function (fireEvent) {
                if (this.input) {
                    if (this.date.strftime) {
                        if (this.showTime)
                            if (this.input.readAttribute('format') != null)
                                this.input.value = this.date.strftime(this.input.readAttribute('format'));
                            else
                                this.input.value = this.date.strftime(this.options.datetimeFormat);
                        else
                            if (this.input.readAttribute('format') != null)
                                this.input.value = this.date.strftime(this.input.readAttribute('format'));
                            else
                                this.input.value = this.date.strftime(this.options.dateFormat);
                    }

                    this.input.next('input[type=hidden]').value = this.date.toJSON();
                    if (fireEvent) this.input.fire('datepicker:inputUpdate');
                }
            },
            dispose: function () {
                if (this.monthYearPicker) {
                    this.monthYearPicker.stopObserving('mouseMove').stopObserving('click');
                    this.monthYearPicker = null;
                    this.pickerMonthDiv = null;
                    this.pickerYearDiv = null;
                }
                if (this.timePickerDiv) {
                    this.timePickerDiv.select('div.label').each(function (button) { button.stopObserving('click'); });
                    this.timePickerDiv.stopObserving('mouseOver').stopObserving('mouseOut');
                    this.timePickerDiv.remove();
                    this.timePickerDiv = null;
                }
                this.div.stopObserving('click').stopObserving('mousemove');
                this.div.select('button').each(function (button) { button.stopObserving('click'); });
                this.div.remove();
                this.div = null;
                this.input.stopObserving('keydown').stopObserving('click');
                if (this.input.next() && this.input.next().down()) {
                    this.input.next().down().stopObserving('click')
                }
                this.input.datepicker = null;
                datepickers.unset(this.input)
                this.input = null;

            },
            initialize: function (input, options) {
                this.setOptions(options);
                this.monthNames = ['January', 'Febuary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
                this.dayNames = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
                var useToday = false;
                if (input) {
                    if (input.hasClassName('initialized')) return;
                    input.addClassName('initialized');
                    if (input.readAttribute("options") != null && !options) {
                        this.setOptions(input.readAttribute("options").evalJSON());
                    }
                    var disabled = (input.readAttribute('disabled') == 'disabled');
                    input.wrap(new Element('div', { className: 'datepickerInputContainer' }));
                    input.datepicker = this;
                    if (this.options.inputcontaineraddclass)
                        input.up('div').addClassName(this.options.inputcontaineraddclass);
                    datepickers.set(input.identify(), this);
                    this.input = input;
                    var name = input.name ? input.name : input.id;
                    var hiddenInput = new Element('input', { type: 'hidden', name: name + '_json' });
                    input.insert({ after: hiddenInput });
                    if (input.hasClassName('timepicker')) this.showTime = true;
                    if (input.value != '') {
                        var inputValue = input.value;
                        var date = $D(inputValue);
                        if (this.options.dateFormat == '%d/%m/%Y') {
                            date = inputValue.dateFromDMY();
                        }
                        if (!(date instanceof Date)) { alert('Cannot parse the date value in ' + (input.id ? input.id : input.name)); return; }
                        this.date = date;
                        //round the mins
                        var mins = this.date.getMinutes();
                        var mod = mins % 15;
                        if (mod != 0) {
                            if (mod >= 7) this.date.add(15 - mod, 'minute');
                            if (mod < 7) this.date.add(-mod, 'minute');
                        }
                        this.min = this.date.getMinutes();
                        this.hour = this.date.getHours();
                        this.month = date.getMonth();
                        this.year = date.getFullYear();
                        this.day = date.getDate();
                        this.date = new Date(this.year, this.month, this.day, this.hour, this.min, 0);
                        this.setInputValue(false);
                    } else {
                        useToday = true;
                    }
                    input.observe('keydown', function (e) {
                        if (!([37, 39, 36, 35, 45, 46, 16, 8, 9].include(e.keyCode))) {
                            e.stop(); //disable everything except backspace, delete, arrows, shift, home/end, insert, tab
                        }
                    }).observe('blur', function (e) { //on blur, if input is empty, clear the postback json
                        if (this.value == '') {
                            this.next('input[type=hidden]').value = '';
                        } else {
                            if (this.setInputValue) this.setInputValue(false);
                        }
                    }).observe('click', function (e) { e.stop(); }).writeAttribute('readonly', null);
                    var rp = this.options.relativepositioning;
                    var link = new Element('a', { href: '#', className: 'pickerLink' }).insert(new Element('div', { className: 'pickerImage' }));
                    if (!disabled) {
                        link.observe('click', function (e) {
                            var el = e.element();
                            var container = el.up('.datepickerInputContainer');
                            var input = container.down('input');
                            var datepicker = datepickers.get(input.id);
                            if (this.options.closeOthers) {
                                $$('input.datepicker').each(function (input) {
                                    var datepickerClassInstance = datepickers.get(input.id);
                                    datepickerClassInstance.hide();
                                });
                            }
                            if (!rp) {
                                //datepicker.div.setStyle({ position: 'relative' });
                                //datepicker.div.clonePosition(input, { setWidth: false, setHeight: false, offsetTop: 22 });
                                //datepicker.div.setStyle({ position: 'absolute' });
                                //alert(div.cumulativeOffset());
                                var co = input.cumulativeOffset();
                                div.setStyle({ position: 'absolute', top: co.top + input.getHeight() + 'px', left: co.left + 'px' });
                            }
                            input.fire('datepicker:beforeshow');
                            datepicker.show();
                            e.stop();
                        } .bind(this));
                    } else {
                        link.addClassName('pickerLinkDisabled');
                    }

                    input.insert({ after: link });
                } else {
                    useToday = true;
                }
                if (useToday) {
                    var date = new Date();
                    this.month = date.getMonth();
                    this.year = date.getFullYear();
                    this.day = date.getDate();
                    this.hour = date.getHours();
                    this.min = date.getMinutes();
                    this.date = date;
                    var mod = this.min % 15;
                    if (mod != 0) {
                        if (mod >= 7) this.date.add(15 - mod, 'minute');
                        if (mod < 7) this.date.add(-mod, 'minute');
                        this.hour = this.date.getHours();
                        this.min = this.date.getMinutes();
                    }
                }
                var div = this.render.bind(this)();
                this.div = div;
                div.observe('click', function (e) {
                    var el = e.element();
                    if (el.match('div.datepicker-changemonth-previous') || el.match('div.datepicker-changemonth-next')) {
                        var newDate = null;
                        var currDate = (new Date(this.year, this.month, 1));
                        if (el.match('div.datepicker-changemonth-previous')) {
                            newDate = currDate.add(-1, 'month');
                        }
                        else {
                            newDate = currDate.add(1, 'month');
                        }
                        if ((this.options.MinDate && newDate > this.options.MinDate) || !this.options.MinDate) {
                            if ((this.options.MaxDate && newDate < this.options.MaxDate) || !this.options.MaxDate) {

                                this.year = newDate.getFullYear();
                                this.month = newDate.getMonth();
                                this.date = new Date(this.year, this.month, this.day, this.hour, this.min);
                                this.setInputValue(true);
                                this.redrawBody.bind(this)(el.up().next());
                                el.fire('datepicker:monthChanged');
                            }
                        }
                    }
                    if (el.match('div.datepicker-button-cancel')) {
                        if (this.originalValue) this.input.value = this.originalValue;
                        this.hide();
                        var el = e.element();
                        el.fire('datepicker:closeClick');
                    }
                    if (el.match('div.datepicker-header-monthyear') || el.match('div.datepicker-header-label') || el.match('div.datepicker-changemonth-down')) {
                        if (this.monthyearpicker == undefined) {
                            this.pickerMonth = this.month;
                            this.pickerYear = this.year;

                            var monthyearpicker = this.getDiv('datepicker-monthyearpicker');
                            this.monthyearpicker = monthyearpicker;
                            var months = this.getDiv('datepicker-monthyearpicker-month');
                            this.pickerMonthDiv = years;
                            this.monthNames.each(function (mn, i) {
                                var m = this.getDiv('datepicker-monthyearpicker-item').update(mn.substring(0, 3)).addClassName('month');
                                if (i == this.pickerMonth)
                                    m.addClassName('selected');
                                m.writeAttribute("index", i);
                                months.insert(m);
                            } .bind(this));
                            monthyearpicker.insert(months);
                            var years = this.getDiv('datepicker-monthyearpicker-year')
                            this.pickerYearDiv = years;
                            this.pickerYearStart = this.pickerYear - 4;
                            this.pickerYearEnd = this.pickerYear + 5;
                            this.redrawMonthYearPickerYears.bind(this)(years);
                            monthyearpicker.insert(years);
                            var footer = this.getDiv('datepicker-monthyearpicker-footer');
                            footer.insert(new Element('button').update('OK').observe('click', function (e) {
                                this.monthyearpicker.hide();
                                var newDate = new Date(this.year, this.month, this.day, this.hour, this.min);
                                if ((this.options.MinDate && newDate > this.options.MinDate) || !this.options.MinDate) {
                                    if ((this.options.MaxDate && newDate < this.options.MaxDate) || !this.options.MaxDate) {
                                        this.year = this.pickerYear;
                                        this.month = this.pickerMonth;
                                        this.date = new Date(this.year, this.month, this.day, this.hour, this.min);
                                        this.setInputValue(true);
                                        var body = this.div.down().next(1);
                                        this.redrawBody.bind(this)(body);
                                    }
                                }
                                e.stop();
                                e.element().fire('datepicker:monthYearPickerHidden');
                            } .bind(this)));
                            footer.insert(new Element('button').update('Cancel').observe('click', function (e) {
                                this.monthyearpicker.hide();
                                e.stop();
                                e.element().fire('datepicker:monthYearPickerHidden');
                            } .bind(this)));
                            monthyearpicker.insert(footer);
                            monthyearpicker.observe('mousemove', function (e) {
                                var el = e.element();
                                el.up(1).select('div.highlight').invoke('removeClassName', 'highlight');
                                if (el.match('div.datepicker-monthyearpicker-item')) {
                                    el.addClassName('highlight');
                                }
                            })
                            .observe('click', function (e) {
                                var el = e.element();
                                if (el.match('div.datepicker-monthyearpicker-item.month')) {
                                    el.up(1).select('div.datepicker-monthyearpicker-item.month.selected').invoke('removeClassName', 'selected');
                                    el.addClassName('selected');
                                    this.pickerMonth = el.getAttribute('index');
                                    el.fire('datepicker:monthYearPickerMonthClick');

                                }
                                if (el.match('div.datepicker-monthyearpicker-item.year')) {
                                    el.up(1).select('div.datepicker-monthyearpicker-item.year.selected').invoke('removeClassName', 'selected');
                                    el.addClassName('selected');
                                    this.pickerYear = el.innerHTML;
                                    el.fire('datepicker:monthYearPickerYearClick');
                                }
                                if (el.match('div.datepicker-monthyearpicker-previousyear')) {
                                    this.pickerYearStart -= 5;
                                    this.pickerYearEnd -= 5;
                                    this.redrawMonthYearPickerYears.bind(this)(this.pickerYearDiv);
                                    //el.fire('datepicker:monthYearPickerPreviousYearClick');
                                }
                                if (el.match('div.datepicker-monthyearpicker-nextyear')) {
                                    this.pickerYearStart += 5;
                                    this.pickerYearEnd += 5;
                                    this.redrawMonthYearPickerYears.bind(this)(this.pickerYearDiv);
                                    //el.fire('datepicker:monthYearPickerNextYearClick');
                                }

                            } .bind(this));
                            this.div.insert({ top: monthyearpicker });
                        } else {
                            this.pickerYear = this.year;
                            this.pickerMonth = this.month;
                            this.pickerYearEnd = this.year + 5;
                            this.pickerYearStart = this.year - 4;
                            this.monthyearpicker.select('div.datepicker-monthyearpicker-item.selected').invoke('removeClassName', 'selected');
                            this.monthyearpicker.select('div.datepicker-monthyearpicker-item.selected').invoke('removeClassName', 'selected');
                            this.monthyearpicker.select('div.datepicker-monthyearpicker-item.month')[this.month].addClassName('selected');
                            this.monthyearpicker.select('div.datepicker-monthyearpicker-item.year')[(this.pickerYearEnd - this.year + 1)].addClassName('selected');
                            this.monthyearpicker.show();
                        }
                        el.fire('datepicker:monthYearPickerShown');
                    }
                    if (el.match('div.datepicker-body td:not(.unselectable)')) {
                        if (!this.options.readonly) {
                            el.up(1).select('td.selected').invoke('removeClassName', 'selected');
                            el.addClassName('selected');
                            this.day = el.value;
                            this.date = new Date(this.year, this.month, this.day, this.hour, this.min);
                            this.setInputValue(true);
                            el.fire('datepicker:dayClick');
                            if (this.options.closeOnChoice) {
                                this.hide();
                                this.input.fire('datepicker:hidden');
                            }
                        }
                    }
                    e.stop();
                } .bind(this));
                if (!this.options.nohover)
                    div.observe('mousemove', function (e) {
                        var el = e.element();
                        if (el.tagName == 'TD' && !el.hasClassName('unselectable')) {
                            if (!el.hasClassName('highlight')) {
                                this.select('td.highlight').invoke('removeClassName', 'highlight');
                                el.addClassName('highlight');
                            }
                        } else {
                            this.select('td.highlight').invoke('removeClassName', 'highlight');
                        }
                    });
                if (this.options.closeOnDocumentClick)
                    document.observe('click', function (e) {
                        //document clicked, close all datepickers
                        $$('input.datepicker').each(function (dpi) {
                            var dp = datepickers.get(dpi.id);
                            if (dp) {
                                if (dp.div.visible()) {
                                    dp.hide();
                                    dp.input.fire('datepicker:hidden');
                                }
                            }
                        });

                    });
                div.hide();
                if (this.options.relativepositioning) {
                    this.input.up('div').insert(div);
                } else {
                    $$('body').first().insert(div);
                }
                if (this.options.autoshow) {
                    if (!this.options.relativepositioning) this.div.clonePosition(this.input, { setWidth: false, setHeight: false, offsetTop: 22 });
                    this.show.bind(this)();
                }
            },
            redrawMonthYearPickerYears: function (div) {
                div.update();
                div.insert(this.getDiv('datepicker-monthyearpicker-previousyear'));
                div.insert(this.getDiv('datepicker-monthyearpicker-nextyear'));
                var yearNames = $R(this.pickerYearStart, this.pickerYearEnd);
                yearNames.each(function (yn, i) {
                    var y = this.getDiv('datepicker-monthyearpicker-item').update(yn).addClassName('year');
                    if (yn == this.pickerYear)
                        y.addClassName('selected');
                    div.insert(y);
                } .bind(this));
            },
            redrawBody: function (body, selectDay) {
                if (!selectDay) selectDay = body.select('td.selected').pluck('innerHTML');
                body.update();
                body.insert(this.renderDayGrid.bind(this)(selectDay));
                body.previous().down('div.datepicker-header-label').update(this.getMonthYearLabel());
                body.fire('datepicker:gridRedraw');
            },
            getMonthYearLabel: function () {
                return this.monthNames[this.month] + ' ' + this.year;
            },
            render: function () {
                var selectDay = this.day;
                var d = this.getDiv('datepicker')
                    .insert(this.getDiv('datepicker-header')
                        .insert(this.getDiv('datepicker-changemonth-previous'))
                        .insert(
                          this
                            .getDiv('datepicker-header-monthyear')
                                .insert(this.getDiv('datepicker-header-label').update(this.getMonthYearLabel()))
                                .insert(this.getDiv('datepicker-changemonth-down'))
                         )
                         .insert(this.getDiv('datepicker-changemonth-next'))
                         .insert(this.showTime ? '' : this.getDiv('datepicker-button-cancel'))
                         )
                    .insert(
                        this.getDiv('datepicker-body')
                            .insert(this.renderDayGrid.bind(this)(selectDay))
                        )
                    .insert(this.getDiv('datepicker-footer')
                        .insert(this.renderButton.bind(this)('Today')
                            .observe('click', this.jumpToToday.bindAsEventListener(this))
                        ).insert(this.renderButton.bind(this)('OK')
                            .observe('click', this.close.bindAsEventListener(this))
                        )
                    );
                if (this.options.addclass) {
                    var addclass = this.options.addclass;
                    var cn = this.input.className.split(' ');
                    cn.each(function (c) {
                        d.addClassName(c + addclass);
                    });
                    d.addClassName(this.options.addclass);
                }
                if (this.showTime) {
                    d.addClassName('datetimepicker');
                    var timepicker = new Element('div', { className: 'timepicker' });
                    var header = new Element('div', { className: 'timepicker-header' });
                    header.insert(this.getDiv('datepicker-button-cancel'));
                    timepicker.insert(header);
                    var subheader = new Element('div', { className: 'timepicker-subheader' }).insert(new Element('div', { className: 'label' }).update('A.M')).insert(new Element('div', { className: 'label' }).update('P.M'));
                    timepicker.insert(subheader);
                    var options = this.getTimepickerDiv();
                    timepicker.insert(options);
                    d.insert(timepicker);

                    var scrollbarWidth = this.getScrollbarWidth();
                    var timepickerWidth = timepicker.getWidth();
                    $A([header, subheader, timepicker, options]).invoke('setStyle', { width: (78 - scrollbarWidth) + 'px' });

                }
                this.div = d;
                return d;
            },
            getScrollbarWidth: function () {
                if (window.GlobalScrollbarWidth) return window.GlobalScrollbarWidth;
                var textarea = new Element('textarea');
                var words = ['this', 'is', 'some', 'text', 'cats', 'are', 'cool', 'but', 'random', 'text', 'better', 'than', 'them'];
                var phrase = ''; (255).times(function (n) { phrase += '' + words[Math.round(parseFloat(Math.random() * (words.length - 1)))] + ' '; });
                $$('body').first().insert(textarea);
                textarea.update(phrase);
                textarea.setStyle({ overflow: 'hidden' });
                var sbw = textarea.offsetWidth;
                textarea.setStyle({ overflow: '' });
                sbw -= textarea.offsetWidth;
                textarea.remove();
                window.GlobalScrollbarWidth = sbw;
                return sbw;
            },
            close: function (e) {
                this.hide();
                var el = e.element();
                el.fire('datepicker:closeClick');
                e.stop();
            },
            jumpToToday: function (e) {
                var el = e.element();
                el.fire('datepicker:todayClick');
                var newDate = new Date();
                if ((this.options.MinDate && newDate > this.options.MinDate) || !this.options.MinDate) {
                    if ((this.options.MaxDate && newDate < this.options.MaxDate) || !this.options.MaxDate) {
                        this.year = newDate.getFullYear();
                        this.month = newDate.getMonth();
                        this.date = newDate;
                        this.redrawBody.bind(this)(el.up(1).previous(), newDate.getDate());
                        var mins = newDate.getMinutes();
                        var mod = mins % 15;
                        if (mod != 0) {
                            if (mod >= 7) this.date.add(15 - mod, 'minute');
                            if (mod < 7) this.date.add(-mod, 'minute');
                        }
                        this.min = this.date.getMinutes();
                        this.hour = this.date.getHours();
                        this.setInputValue(true);
                        var hour = this.hour;
                        var min = this.min;
                        this.div.select('div.timepickeroptions li.selected').invoke('removeClassName', 'selected');
                        if (!this.options.readonly) var selectedTime = this.div.select('div.timepickeroptions li div.label').find(function (t) { return t.innerHTML == (hour.toPaddedString(2) + ':' + min.toPaddedString(2)); });
                        if (selectedTime) {
                            selectedTime = selectedTime.up();
                            var ul = selectedTime.up('ul');
                            var allLiInColumn = ul.select('li');
                            var offset = (allLiInColumn.indexOf(selectedTime)) * selectedTime.getHeight();
                            var options = selectedTime.up('div.timepickeroptions');
                            options.scrollTop = offset;
                            if (selectedTime) selectedTime.addClassName('selected');
                        }
                    }
                }
                e.stop();
            },
            renderButton: function (text) {
                var b = this.getDiv('datepicker-button')
                    .insert(this.getDiv('datepicker-button-left'))
                    .insert(new Element('button', { id: 'datepicker-button-' + text }).update(text))
                    .insert(this.getDiv('datepicker-button-right'));
                return b;
            },
            renderDayGrid: function (selectDay) {
                var startOfMonth = new Date(this.year, this.month, 1);
                var date = startOfMonth.add(-startOfMonth.getDay() - 1, 'days');
                var tb = new Element('tbody');
                var t = new Element('table').insert(tb);
                var tr = new Element('tr');
                this.dayNames.each(function (d) {
                    tr.insert(new Element('td').update(d).addClassName('unselectable'));
                });
                ;
                for (var i = 0; i < 49; i++) {
                    if (i % 7 == 0) {
                        tb.insert(tr);
                        tr = new Element('tr');
                    }
                    date.add(1, 'days');
                    var td = new Element('td').update(date.getDate());
                    if (this.options.MaxDate) {
                        if (date > this.options.MaxDate) {
                            td.addClassName('unselectable');
                        }
                    }
                    if (this.options.MinDate) {
                        //alert('date: ' + date + ', MinDate: ' + this.options.MinDate + ' date < MinDate: ' + (date < this.options.MinDate));
                        if (date < this.options.MinDate) {
                            td.addClassName('unselectable');
                        }
                    }
                    td.value = date.getDate();
                    if (date.getMonth() != this.month)
                        td.addClassName('unselectable');
                    else {
                        if (!this.options.nodefault && selectDay && date.getDate() == selectDay && !td.hasClassName('unselectable')) td.addClassName('selected');
                        if (this.selectedDays) {
                            var select = $A(this.selectedDays).map(function (d) { return '!' + new String(d) + '!'; }).include('!' + new String(date) + '!');
                            if (select) td.addClassName('selected');
                        }
                    }

                    tr.insert(td);
                }
                return t;
            },
            getDiv: function (clsName) {
                return (new Element('div', { className: clsName }));
            },
            setSelected: function (inputValue) {
                if (inputValue != undefined)
                    this.input.value = inputValue;
                if (this.input.value != '') {
                    var inputValue = this.input.value;
                    var date = $D(inputValue);
                    if (this.options.dateFormat == '%d/%m/%Y') {
                        date = inputValue.dateFromDMY();
                    }
                    if (!(date instanceof Date)) { alert('Cannot parse the date value (' + inputValue + ') in ' + (this.input.id ? this.input.id : this.input.name)); return; }
                    this.date = date;
                    //round the mins
                    var mins = this.date.getMinutes();
                    var mod = mins % 15;
                    if (mod != 0) {
                        if (mod >= 7) this.date.add(15 - mod, 'minute');
                        if (mod < 7) this.date.add(-mod, 'minute');
                    }
                    this.min = this.date.getMinutes();
                    this.hour = this.date.getHours();
                    this.month = date.getMonth();
                    this.year = date.getFullYear();
                    this.day = date.getDate();
                    this.date = new Date(this.year, this.month, this.day, this.hour, this.min, 0);
                    this.setInputValue(false);
                }
                //make selected day
                this.redrawBody($$('div.datepicker-body').first(), (this.day + ''))
                //make selected day
                var dayTds = $$('div.datepicker-body').first().select('td').invoke('removeClassName', 'selected');
                var day = this.day;
                var selectedDay = dayTds.find(function (td) { return (td.innerHTML.stripTags().strip() == ('' + day)); });
                if (selectedDay && !selectedDay.hasClassName('unselectable')) selectedDay.addClassName('selected');

                if (this.showTime) {
                    var timeLis = $$('div.timepickeroptions').first().select('li').invoke('removeClassName', 'selected');
                    var hour = this.hour;
                    var min = this.min;
                    var selectedTime = timeLis.find(function (li) { return (!li.hasClassName('unselectable') & li.innerHTML.stripTags() == (hour.toPaddedString(2) + ':' + min.toPaddedString(2))); });
                    if (selectedTime) {
                        selectedTime.addClassName('selected');
                        var ul = selectedTime.up('ul');
                        var allLiInColumn = ul.select('li');
                        var offset = (allLiInColumn.indexOf(selectedTime)) * selectedTime.getHeight();
                        var options = selectedTime.up('div.timepickeroptions');
                        options.scrollTop = offset;
                    }
                }
            },
            setSelectedTimes: function () {
                var selectedTimes = this.selectedTimes;
                if (selectedTimes) {
                    var timeLis = $$('div.timepickeroptions').first().select('li').invoke('removeClassName', 'selected');
                    var selectedTime = timeLis.findAll(function (li) { return (!li.hasClassName('unselectable') & selectedTimes.include(li.innerHTML.stripTags().replace(':', ''))); }).invoke('addClassName', 'selected');
                }
            },
            show: function () {
                $$('input.datepicker').each(function (dpi) {
                    if (datepickers[dpi.id])
                        if (datepickers[dpi.id].div.visible()) {
                            datepickers[dpi.id].hide();
                            datepickers[dpi.id].input.fire('datepicker:hidden');
                        }
                });
                if (this.input.value != '') {
                    var inputValue = this.input.value;
                    var date = $D(inputValue);
                    if (this.options.dateFormat == '%d/%m/%Y') {
                        date = inputValue.dateFromDMY();
                    }
                    if (!(date instanceof Date)) { alert('Cannot parse the date value in ' + (this.input.id ? this.input.id : this.input.name)); return; }
                    this.date = date;
                    //round the mins
                    var mins = this.date.getMinutes();
                    var mod = mins % 15;
                    if (mod != 0) {
                        if (mod >= 7) this.date.add(15 - mod, 'minute');
                        if (mod < 7) this.date.add(-mod, 'minute');
                    }
                    this.min = this.date.getMinutes();
                    this.hour = this.date.getHours();
                    this.month = date.getMonth();
                    this.year = date.getFullYear();
                    this.day = date.getDate();
                    this.date = new Date(this.year, this.month, this.day, this.hour, this.min, 0);
                    this.setInputValue(false);
                }
                this.div.show();
                this.input.fire('datepicker:shown');
                if (this.showTime) {
                    var selectedTime = this.div.down('div.timepickeroptions li.selected');
                    if (selectedTime) {
                        var ul = selectedTime.up('ul');
                        var allLiInColumn = ul.select('li');
                        var offset = (allLiInColumn.indexOf(selectedTime)) * selectedTime.getHeight();
                        var options = selectedTime.up('div.timepickeroptions');
                        options.scrollTop = offset;
                    }
                }
                if (this.input) {
                    this.originalValue = this.input.value;
                }
            },
            hide: function () {
                this.input.fire('datepicker:hidden');
                this.div.hide();
            },
            getTimepickerDiv: function () {
                if (this.timepickerDiv) return this.timepickerDiv;
                div = new Element('div', { className: 'timepickeroptions' });
                if (!this.options.nohover)
                    div.observe('mouseover', function (e) {
                        var obj = Event.element(e);
                        var li = null;
                        if (obj.tagName == 'LI' || obj.hasClassName('label'))
                            if (obj.tagName == 'LI')
                                li = obj;
                            else
                                li = obj.up('li');
                        if (li)
                            li.addClassName('highlight');
                    })
                    .observe('mouseout', function (e) {
                        this.select('li.highlight').invoke('removeClassName', 'highlight');
                    });
                var d = [{ hmin: 0, hmax: 12, label: 'a.m', subtract: 0 }, { hmin: 12, hmax: 24, label: 'p.m', subtract: 12}];
                for (var s = 0; s < 2; s++) {
                    var o = d[s];
                    var col = new Element('div', { className: 'col' });
                    var ul = new Element('ul');
                    var selectedLi = null;
                    for (var h = o.hmin; h < o.hmax; h++) {
                        for (var m = 0; m < 60; m += this.options.timepickerinterval) {
                            var li = new Element('li')
                                .addClassName((m % 30 == 0) ? 'alt' : '')
                                .addClassName((m == 0) ? 'hour' : '')
                                .writeAttribute('twentyfourhour', h.toPaddedString(2) + ':' + m.toPaddedString(2))
                                .insert(new Element('div', { className: 'label' })
                                    .update((h - o.subtract).toPaddedString(2) + ':' + m.toPaddedString(2)));
                            if (!this.options.readonly)
                                li.observe('click', function (e) {
                                    var el = e.element();
                                    if (el.tagName != 'LI') el = el.up('li');
                                    if (!el) return;
                                    var div = el.up('div.timepickeroptions');
                                    var time = el.readAttribute('twentyfourhour').split(":").invoke('parseInt');
                                    this.hour = time[0];
                                    this.min = time[1];
                                    this.date = new Date(this.year, this.month, this.day, this.hour, this.min);
                                    this.setInputValue(false);
                                    div.select('li.selected').invoke('removeClassName', 'selected');
                                    el.addClassName('selected');
                                    Event.stop(e);
                                } .bindAsEventListener(this));
                            if (!this.options.nodefault)
                                if (h == this.hour && m == this.min) selectedLi = li.addClassName('selected');
                            ul.insert(li);
                        }
                    }
                    col.insert(ul);
                    div.insert(col);
                }
                this.timepickerDiv = div;
                return div;
            }
        });
        Event.observe(document, 'sniper:initcontrols', function(e) {
            $$('input.datepicker').each(function(dateInput) {
                if (!dateInput.hasClassName('noautoinit'))
                    new SniperSystemsDatePicker(dateInput);
            });
        });

        Event.observe(document, 'sniper:destroycontrols', function(e) {
            if (datepickers) {
                datepickers.each(destroy);
            }
        });

        function destroy(pair) {
            var input = $(pair.key);
            var dpo = pair.value;
            dpo.dispose();
        }

        Event.observe(window, 'load', function(e) {
            document.fire("sniper:initcontrols");
        });
        var datepickers = $H({});