/**
 * @license                                     
 * jQuery Tools 1.2.5 Dateinput - <input type="date" /> for humans
 *
 * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
 *
 * http://flowplayer.org/tools/form/dateinput/
 *
 * Since: Mar 2010
 * Date:    Wed Sep 22 06:02:10 2010 +0000
 */
(function($) {

    /* TODO:
         preserve today highlighted
    */
    
    $.tools = $.tools || {version: '1.2.5'};
    
    var instances = [],
         tool,
         
         // h=72, j=74, k=75, l=76, down=40, left=37, up=38, right=39
       //  KEYS = [75, 76, 38, 39, 74, 72, 40, 37, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57],
         KEYS = [75, 76, 38, 39, 74, 72, 40, 37],
         LABELS = {};
    
    tool = $.tools.dateinput = {
        
        conf: {
            format: 'mm/dd/yy',
            selectors: false,
            yearRange: [-5, 5],
            lang: 'en',
            offset: [0, 0],
            speed: 0,
            firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
            min: undefined,
            max: undefined,
            trigger: false,
            
            css: {
                
                prefix: 'cal',
                input: 'date',
                
                // ids
                root: 0,
                head: 0,
                title: 0,
                prev: 0,
                next: 0,
                month: 0,
                year: 0,
                days: 0,
                
                body: 0,
                weeks: 0,
                today: 0,
                current: 0,
                
                // classnames
                week: 0,
                off: 0,
                sunday: 0,
                focus: 0,
                disabled: 0,
                trigger: 0
            }  
        },
        
        localize: function(language, labels) {
            $.each(labels, function(key, val) {
                labels[key] = val.split(",");
            });
            LABELS[language] = labels;
        }
        
    };
    
    tool.localize("en", {
        months:          'January,February,March,April,May,June,July,August,September,October,November,December',
        shortMonths: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec',
        days:          'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday',
        shortDays:      'Sun,Mon,Tue,Wed,Thu,Fri,Sat'
    });


//{{{ private functions


    // @return amount of days in certain month
    function dayAm(year, month) {
        return 32 - new Date(year, month, 32).getDate();
    }
    
    function zeropad(val, len) {
        val = '' + val;
        len = len || 2;
        while (val.length < len) { val = "0" + val; }
        return val;
    }
    
    // thanks: http://stevenlevithan.com/assets/misc/date.format.js
    var Re = /d{1,4}|m{1,4}|yy(?:yy)?|"[^"]*"|'[^']*'/g, tmpTag = $("<a/>");
    
    function format(date, fmt, lang) {
        
        var d = date.getDate(),
            D = date.getDay(),
            m = date.getMonth(),
            y = date.getFullYear(),
            
            flags = {
                d:    d,
                dd:   zeropad(d),
                ddd:  LABELS[lang].shortDays[D],
                dddd: LABELS[lang].days[D],
                m:    m + 1,
                mm:   zeropad(m + 1),
                mmm:  LABELS[lang].shortMonths[m],
                mmmm: LABELS[lang].months[m],
                yy:   String(y).slice(2),
                yyyy: y
            };
        
        var ret = fmt.replace(Re, function ($0) {
            return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
        });
        
        // a small trick to handle special characters
        return tmpTag.html(ret).html();
        
    }
    
    function integer(val) {
        return parseInt(val, 10);
    }
    
    function isSameDay(d1, d2)  {
        return d1.getFullYear() === d2.getFullYear() &&
            d1.getMonth() == d2.getMonth() &&
            d1.getDate() == d2.getDate();
    }
    
    function parseDate(val) {
        
        if (!val) { return; }
        if (val.constructor == Date) { return val; }
        
        if (typeof val == 'string') {
            
            // rfc3339?
            var els = val.split("-");
            if (els.length == 3) {
                return new Date(integer(els[0]), integer(els[1]) -1, integer(els[2]));
            }
            
            // invalid offset
            if (!/^-?\d+$/.test(val)) { return; }
            
            // convert to integer
            val = integer(val);
        }
        
        var date = new Date();
        date.setDate(date.getDate() + val);
        return date;
    }
    
    function parseInput(val) {
        
        if (!val) { return; }
        if (val.constructor == Date) { return val; }
        
        if (typeof val == 'string') {
            val=val.replace(".","/");
            val=val.replace("-","/");
            val=val.replace(" ","/");
            // rfc3339?
            if (/^(0?[1-9]|1[012])[- /.](0?[1-9]|[12][0-9]|3[01])[- /.](19|20)\d\d$/.test(val)){
            	
            	var els = val.split("/");
            
          	  	if (els.length == 3) {
              	  return new Date(integer(els[2]), integer(els[0])-1 , integer(els[1]));
          	 	}
          
            }
            
        }
      
        return false;
    }
//}}}
    
    
    function Dateinput(input, conf)  {
        
        // variables
        var self = this,
             now = new Date(),
             css = conf.css,
             labels = LABELS[conf.lang],
             root = $("#" + css.root),
             title = root.find("#" + css.title),
             trigger,
             pm, nm,
             currYear, currMonth, currDay,
            
             value = input.attr("data-value") || conf.value || input.val(),
             min = input.attr("min") || conf.min,
             max = input.attr("max") || conf.max,
             opened;
       
        // zero min is not undefined
        if (min === 0) { min = "0"; }
        
        // use sane values for value, min & max
        value = parseDate(value) || now;
        min   = parseDate(min || conf.yearRange[0] * 365);
        max   = parseDate(max || conf.yearRange[1] * 365);
        
        
        // check that language exists
        if (!labels) { throw "Dateinput: invalid language: " + conf.lang; }
        
        // Replace built-in date input: NOTE: input.attr("type", "text") throws exception by the browser
        if (input.attr("type") == 'date') {
            var tmp = $("<input/>");
            
            $.each("class,disabled,id,maxlength,name,readonly,required,size,style,tabindex,title,value".split(","), function(i, attr)  {
                tmp.attr(attr, input.attr(attr));
            });
            input.replaceWith(tmp);
            input = tmp;
        }
        input.addClass(css.input);
        
        var fire = input.add(self);
        
        // construct layout
        if (!root.length) {
            
            // root
            root = $('<div><div><a/><div/><a/></div><div><div/><div/></div></div>')
                .hide().css({position: 'absolute'}).attr("id", css.root);
            
            // elements
            root.children()
                .eq(0).attr("id", css.head).end()
                .eq(1).attr("id", css.body).children()
                    .eq(0).attr("id", css.days).end()
                    .eq(1).attr("id", css.weeks).end().end().end()
                .find("a").eq(0).attr("id", css.prev).end().eq(1).attr("id", css.next);
            
            // title
            title = root.find("#" + css.head).find("div").attr("id", css.title);
            
            // year & month selectors
            if (conf.selectors) {
                var monthSelector = $("<select/>").attr("id", css.month),
                     yearSelector = $("<select/>").attr("id", css.year);
                title.html(monthSelector.add(yearSelector));
            }
            
            // day titles
            var days = root.find("#" + css.days);
            
            // days of the week
            for (var d = 0; d < 7; d++) {
                days.append($("<span/>").text(labels.shortDays[(d + conf.firstDay) % 7]));
            }
            
            $("body").append(root);
        }
        
        
        // trigger icon
        if (conf.trigger) {
            trigger = $("<a/>").attr("href", "#").addClass(css.trigger).click(function(e)  {
                self.show();
                return e.preventDefault();
            }).insertAfter(input);
        }
        
        
        // layout elements
        var weeks = root.find("#" + css.weeks);
        yearSelector = root.find("#" + css.year);
        monthSelector = root.find("#" + css.month);
        
        
//{{{ pick
        
        function select(date, conf, e) {
            
            // current value
            value      = date;
            currYear  = date.getFullYear();
            currMonth = date.getMonth();
            currDay     = date.getDate();
            
            
            // change
            e = e || $.Event("api");
            e.type = "change";
            
            fire.trigger(e, [date]);
            if (e.isDefaultPrevented()) { return; }
            
            // formatting
            input.val(format(date, conf.format, conf.lang));
            
            // store value into input
            input.data("date", date);
            
            self.hide(e);
        }
//}}}


//{{{ onShow

        function onShow(ev) {
            
            ev.type = "onShow";
            fire.trigger(ev);
            
            $(document).bind("keydown.d", function(e) {
                    
                if (e.ctrlKey) { return true; }
                var key = e.keyCode;
                
                // backspace clears the value
          /*      if (key == 8) {
                    input.val("");
                    return self.hide(e);
                } */
                
                // esc key
                if (key == 27) { return self.hide(e); }
                
                if ($(KEYS).index(key) >= 0) {
                   
                    if (!opened) {
                        self.show(e);
                        return e.preventDefault();
                    }
                    
                    var days = $("#" + css.weeks + " a"),
                         el = $("." + css.focus),
                         index = days.index(el);
                     
                    el.removeClass(css.focus);
                    
                    if (key == 74 || key == 40) { index += 7; }
                    else if (key == 75 || key == 38) { index -= 7; }
                    else if (key == 76 || key == 39) { index += 1; }
                    else if (key == 72 || key == 37) { index -= 1; }
                    
                    
                    if (index > 41) {
                         self.addMonth();
                         el = $("#" + css.weeks + " a:eq(" + (index-42) + ")");
                    } else if (index < 0) {
                         self.addMonth(-1);
                         el = $("#" + css.weeks + " a:eq(" + (index+42) + ")");
                    } else {
                         el = days.eq(index);
                    }
                    
                    el.addClass(css.focus);
                 //   return e.preventDefault();
                    return true;
                }
                
                // pageUp / pageDown
                if (key == 34) { return self.addMonth(); }
                if (key == 33) { return self.addMonth(-1); }
                
                // home
                if (key == 36) { return self.today(); }
                
                // enter
                if (key == 13) {
                    if (!$(e.target).is("select")) {
                        $("." + css.focus).click();
                    }
                }
                return true;
           //     return $([16, 17, 18, 9]).index(key) >= 0;
            });
            
            
            // click outside dateinput
            $(document).bind("click.d", function(e) {
                var el = e.target;
                
                if (!$(el).parents("#" + css.root).length && el != input[0] && (!trigger || el != trigger[0])) {
                    self.hide(e);
                }
                
            });
        }
//}}}
        
        
        $.extend(self, {

//{{{  show
            
            show: function(e) {
                if(parseInput(input.val())){
                	value = parseInput(input.val());
                }
               
                if (input.attr("readonly") || input.attr("disabled") || opened) { return; }
                
                // onBeforeShow
                e = e || $.Event();
                e.type = "onBeforeShow";
                fire.trigger(e);
                if (e.isDefaultPrevented()) { return; }
                
                $.each(instances, function() {
                    this.hide();
                });
                
                opened = true;
                
                // month selector
                monthSelector.unbind("change").change(function() {
                    self.setValue(yearSelector.val(), $(this).val());
                });
                
                // year selector
                yearSelector.unbind("change").change(function() {
                    self.setValue($(this).val(), monthSelector.val());
                });
                
                // prev / next month
                pm = root.find("#" + css.prev).unbind("click").click(function(e) {
                    if (!pm.hasClass(css.disabled)) {
                        self.addMonth(-1);
                    }
                    return false;
                });
                
                nm = root.find("#" + css.next).unbind("click").click(function(e) {
                    if (!nm.hasClass(css.disabled)) {
                        self.addMonth();
                    }
                    return false;
                });
                
                // set date

                self.setValue(value);
                
                // show calendar
                var pos = input.offset();
                
                // iPad position fix
                if (/iPad/i.test(navigator.userAgent)) {
                    pos.top -= $(window).scrollTop();
                }
                
                root.css({
                    top: pos.top + input.outerHeight({margins: true}) + conf.offset[0],
                    left: pos.left + conf.offset[1]
                });
                
                if (conf.speed) {
                    root.show(conf.speed, function() {
                        onShow(e);
                    });
                } else {
                    root.show();
                    onShow(e);
                }
                
                return self;
            },
//}}}


//{{{  setValue

            setValue: function(year, month, day)  {
                
                var date = integer(month) >= -1 ?
                    new Date(integer(year), integer(month), integer(day || 1)) : year || value
                ;
                
                if (date < min) { date = min; }
                else if (date > max) { date = max; }
                
                year = date.getFullYear();
                month = date.getMonth();
                day = date.getDate();
                
                
                // roll year & month
                if (month == -1) {
                    month = 11;
                    year--;
                } else if (month == 12) {
                    month = 0;
                    year++;
                }
                
                if (!opened) {
                    select(date, conf);
                    return self;
                }
                
                currMonth = month;
                currYear = year;
                
                // variables
                var tmp = new Date(year, month, 1 - conf.firstDay), begin = tmp.getDay(),
                     days = dayAm(year, month),
                     prevDays = dayAm(year, month - 1),
                     week;
                
                // selectors
                if (conf.selectors) {
                    
                    // month selector
                    monthSelector.empty();
                    $.each(labels.months, function(i, m) {
                        if (min < new Date(year, i + 1, -1) && max > new Date(year, i, 0)) {
                            monthSelector.append($("<option/>").html(m).attr("value", i));
                        }
                    });
                    
                    // year selector
                    yearSelector.empty();        
                    var yearNow = now.getFullYear();
                    
                    for (var i = yearNow + conf.yearRange[0];  i < yearNow + conf.yearRange[1]; i++) {
                    /* Had a problem for December. Updated by Yun.
                        if (min <= new Date(i + 1, -1, 1) && max > new Date(i, 0, 0)) {
                    */
                        if (min.getFullYear() <= i && max.getFullYear() >= i) {
                            yearSelector.append($("<option/>").text(i));
                        }
                    }
                    
                    monthSelector.val(month);
                    yearSelector.val(year);
                
                // title
                } else {
                    title.html(labels.months[month] + " " + year);
                }
                
                // populate weeks
                weeks.empty();
                pm.add(nm).removeClass(css.disabled);
                
                // !begin === "sunday"
                for (var j = !begin ? -7 : 0, a, num; j < (!begin ? 35 : 42); j++) {
                    
                    a = $("<a/>");
                    
                    if (j % 7 === 0) {
                        week = $("<div/>").addClass(css.week);
                        weeks.append(week);
                    }
                    
                    if (j < begin)  {
                        a.addClass(css.off);
                        num = prevDays - begin + j + 1;
                        date = new Date(year, month-1, num);
                        
                    } else if (j >= begin + days)  {
                        a.addClass(css.off);
                        num = j - days - begin + 1;
                        date = new Date(year, month+1, num);
                        
                    } else  {
                        num = j - begin + 1;
                        date = new Date(year, month, num);
                        
                        // current date
                        if (isSameDay(value, date)) {
                            a.attr("id", css.current).addClass(css.focus);
                            
                        // today
                        } else if (isSameDay(now, date)) {
                            a.attr("id", css.today);
                        }
                    }
                    
                    // disabled
                    if (min && date < min) {
                        a.add(pm).addClass(css.disabled);
                    }
                    
                    if (max && date > max) {
                        a.add(nm).addClass(css.disabled);
                    }
                    
                    a.attr("href", "#" + num).text(num).data("date", date);
                    
                    week.append(a);
                }
                
                // date picking
                weeks.find("a").click(function(e) {
                    var el = $(this);
                    if (!el.hasClass(css.disabled)) {  
                        $("#" + css.current).removeAttr("id");
                        el.attr("id", css.current);
                        select(el.data("date"), conf, e);
                    }
                    return false;
                });
                
                // sunday
                if (css.sunday) {
                    weeks.find(css.week).each(function() {
                        var beg = conf.firstDay ? 7 - conf.firstDay : 0;
                        $(this).children().slice(beg, beg + 1).addClass(css.sunday);
                    });
                }
                
                return self;
            },
    //}}}
            
            setMin: function(val, fit) {
                min = parseDate(val);
                
                if (fit && value < min) { self.setValue(min); }
                return self;
            },
            
            setMax: function(val, fit) {
                max = parseDate(val);
                if (fit && value > max) { self.setValue(max); }
                return self;
            },
            
            today: function() {
                return self.setValue(now);
            },
            
            addDay: function(amount) {
                return this.setValue(currYear, currMonth, currDay + (amount || 1));
            },
            
            addMonth: function(amount) {
                return this.setValue(currYear, currMonth + (amount || 1), currDay);
            },
            
            addYear: function(amount) {
                return this.setValue(currYear + (amount || 1), currMonth, currDay);
            },
            
            hide: function(e) {
                
                if (opened) {
                    
                    // onHide
                    e = $.Event();
                    e.type = "onHide";
                    fire.trigger(e);
                    
                    $(document).unbind("click.d").unbind("keydown.d");
                    
                    // cancelled ?
                    if (e.isDefaultPrevented()) { return; }
                    
                    // do the hide
                    root.hide();
                    opened = false;
                }
                return self;
            },
            
            getConf: function() {
                return conf;
            },
            
            getInput: function() {
                return input;
            },
            
            getCalendar: function() {
                return root;
            },
            
            getValue: function(dateFormat) {
                return dateFormat ? format(value, dateFormat, conf.lang) : value;
            },
            
            isOpen: function() {
                return opened;
            }
            
        });
        
        // callbacks
        $.each(['onBeforeShow','onShow','change','onHide'], function(i, name) {
            
            // configuration
            if ($.isFunction(conf[name]))  {
                $(self).bind(name, conf[name]);
            }
            
            // API methods
            self[name] = function(fn) {
                if (fn) { $(self).bind(name, fn); }
                return self;
            };
        });
        
        
        // show dateinput & assign keyboard shortcuts
        input.bind("focus click", self.show).keydown(function(e) {
            var key = e.keyCode;
            
            // open dateinput with navigation keyw
           if (!opened &&  $(KEYS).index(key) >= 0) {
                self.show(e);
                return e.preventDefault();
            }
            
            // allow tab
        //    return e.shiftKey || e.ctrlKey || e.altKey || key == 9 ? true : e.preventDefault();
            return true;
        });
        
        input.bind("focus click", self.show).keyup(function(e) {
         
        	var inputVal = input.val();
        	
			if(parseInput(inputVal)){
				value = parseInput(inputVal);
				self.setValue(value);
			}
            
            return true;
        });
        
        // initial value
        if (parseDate(input.val())) {
            select(value, conf);
        }
        
    }
    
    $.expr[':'].date = function(el) {
        var type = el.getAttribute("type");
        return type && type == 'date' || !!$(el).data("dateinput");
    };
    
    
    $.fn.dateinput = function(conf) {
        
        // already instantiated
        if (this.data("dateinput")) { return this; }
        
        // configuration
        conf = $.extend(true, {}, tool.conf, conf);
        
        // CSS prefix
        $.each(conf.css, function(key, val) {
            if (!val && key != 'prefix') {
                conf.css[key] = (conf.css.prefix || '') + (val || key);
            }
        });
        
        var els;
        
        this.each(function() {
            var el = new Dateinput($(this), conf);
            instances.push(el);
            var input = el.getInput().data("dateinput", el);
            els = els ? els.add(input) : input;
        });
    
        return els ? els : this;
    };
    

}) (jQuery);

