﻿(function ($) {

    function MXCalendar(/*el,*/ options) {

        //this.el = $(el);

        this.r = {
            today: 'Сегодня',
            clear: 'Очистить',
            mmmm: ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
            mmm: ['Янв','Фев','Мар','Апр','Май','Июн','Июл','Авг','Сен','Окт','Ноя','Дек'],
            dddd: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'],
            ddd: ['вск','пнд','втр','срд','чтв','птн','сбт'],
            dd: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'],
            fmt: 'dmy',
            sep: '.',
            firstDay: 1
        };

    	this.init(options);
    }

    MXCalendar.prototype = {

        init: function(options) {
			_created = false;
            /* animation timeout */
            _t = 200;
            /* state : 0 - days, 1 - months, 2 - years */
            _s = 0;
            /* day/month/year div names */
            _views = ['#mx-cal-days', '#mx-cal-months', '#mx-cal-years'];
            /* current year / month */
            _y = _m = _y0 = _m0 = _d0 = 0;
            /* active input and his wrapper */
            _i = _w = null;
			/* value on enter + last changed value */
			_v = _vc = null;
        },
		
		_onMouseDownEx: function(e) {
			if (_i == null) return;
			var $t = $(e.target);
			var id = 'mx-cal-popup';
			if ($t[0].id != id && $t.parents('#' + id).length == 0 && $t.parents('#' + _w[0].id).length == 0)
				this.hidePopup();
		},
		
		createCalendar: function() {
			if (_created) return;
			_created = true;

            var me = this;
            
            var html =
                '<div id="mx-cal-popup"><div id="mx-cal-top"><div id="mx-cal-prev"></div><div id="mx-cal-next"></div><div id="mx-cal-title"></div></div><div id="mx-cal-body"><div id="mx-cal-years"><table>';

            for (var i = 0; i < 12; i++) {
                if (i % 4 == 0) html += '<tr>';
                html += '<td id="mx-cal-y-' + i + '"' + (i == 0 || i == 11 ? ' class="mx-cal-inactive"' : '') + '></td>';
                if (i % 4 == 3) html += '</tr>';
            }
            html += '</table></div><div id="mx-cal-months"><table>';
            for (i = 0; i < 12; i++) {
                if (i % 4 == 0) html += '<tr>';
                html += '<td id="mx-cal-m-' + i + '">' + this.r.mmm[i] + '</td>';
                if (i % 4 == 3) html += '</tr>';
            }
            html += '</table></div><div id="mx-cal-days"><table><thead><tr>';
            for (j = 0; j < 7; j++) {
				var jj = j + this.r.firstDay;
				if (jj > 6) jj -= 7;
                html += '<td name="m-' + j + '">' + this.r.dd[jj] + '</td>';
            }
            html += '</tr></thead>';
            for (i = 0; i < 42; i++) {
                if (i % 7 == 0) html += '<tr>';
                html += '<td id="mx-cal-d-' + i + '"' + ((i + this.r.firstDay + 6) % 7 >= 5 ? ' class="mx-cal-weekend"' : '') + '>' + i + '</td>';
                if (i % 7 == 6) html += '</tr>';
            }

            html += '</table></div></div><div id="mx-cal-footer"><div id="mx-cal-today">' + this.r.today + '</div><div id="mx-cal-clear">' + this.r.clear + '</div></div>';
            
            $('#mx-popup-wrap').append(html);

            $('#mx-cal-title').click(function() { me.onClickTitle(); });
            $('#mx-cal-prev').click(function() { me.onClickPrev(); });
            $('#mx-cal-next').click(function() { me.onClickNext(); });
            $('#mx-cal-years td').click(function() { me.onClickYear(this); });
            $('#mx-cal-months td').click(function() { me.onClickMonth(this); });
            $('#mx-cal-days td').click(function() { me.onClickDay(this); });
            $('#mx-cal-today').click(function() {
                var d = new Date();
                me.setValue(me._format(d.getFullYear(), d.getMonth(), d.getDate()));
            });
            $('#mx-cal-clear').click(function() {
                me.setValue('');
            });
			
			//$(document).mousedown(function(e){ me._onMouseDownEx(e) });
            //$(document).bind('click', function(e){ alert('x'); me._onMouseDownEx(e); });
		},
        
    	_getDaysInMonth: function(y, m) {
	    	return 32 - new Date(y, m, 32).getDate();
	    },

    	_getFirstDayOfMonth: function(y, m) {
	    	return new Date(y, m, 1).getDay();
	    },
	    
	    _parse: function(s) {
            var y = -1, m = -1, d = -1, da, p = [], v = '', fmt = this.r.fmt;
            try {
                /* split */
                for (var i = 0; i < s.length; i++) {
                    var c = s.charAt(i);
                    if ("1234567890".indexOf(c) < 0) {
                        if (v != '') p.push(v);
                        v = '';
                    } else {
                        v += c;
                    }
                }
                if (v != '') p.push(v);
                
                if (p.length == 1) {
                    var ss = p[0];
                    if (ss.length == 7 || ss.length == 5 || ss.length == 3)
                        ss = '0' + ss;
                        
                    if (ss.length == 8) {
                        if (fmt[0] == 'y')
                            p = [ss.substring(0, 4), ss.substring(4, 6), ss.substring(6)];
                        else if (fmt[1] == 'y')
                            p = [ss.substring(0, 2), ss.substring(2, 6), ss.substring(6)];
                        else
                            p = [ss.substring(0, 2), ss.substring(2, 4), ss.substring(4)];
                    } else if (ss.length == 6) {
                        p = [ss.substring(0, 2), ss.substring(2, 4), ss.substring(4)];
                    } else if (ss.length == 4) {
                        p = [ss.substring(0, 2), ss.substring(2)];
                    }
                }

                for (var i = 0, j = 0; i < fmt.length; i++) {
                    var c = fmt.charAt(i);
                    if (c == 'y' && p.length > 2)
                        y = parseInt(p[j++], 10);
                    else if (c == 'm')
                        m = parseInt(p[j++], 10) - 1;
                    else if (c == 'd')
                        d = parseInt(p[j++], 10);
                }
                
                if (y < 0)
                    y = (new Date()).getFullYear();
                else {
                    if (y < 50) y += 2000;
                    if (y < 100) y += 1900;
                }

                if (m < 0 || m > 11 || isNaN(m) || d < 0 || d > 31 || isNaN(d))
                    da = new Date();
                else
                    da = new Date(y, m, d);
            } catch (err) {
                da = new Date();
            }
			_y0 = _y = da.getFullYear(); 
            _m0 = _m = da.getMonth();
            _d0 = da.getDate();
	    },
	    
	    _formatN: function(n, l) {
	        n = n.toString();
	        while (n.length < l) n = '0' + n;
	        return n;
	    },
	    
	    _format: function(y, m, d) {
	        var s = '', x = '';
            for (var i = 0; i < this.r.fmt.length; i++)
            {
                var p = '';
                var c = this.r.fmt.charAt(i);
                if (c == 'y')
                    p = this._formatN(y, 4);
                else if (c == 'm')
                    p = this._formatN(m + 1, 2);
                else if (c == 'd')
                    p = this._formatN(d, 2);
                if (p != '') {
                    s += x + p;
                    x = this.r.sep;
                } else {
                    s += c;
                    x = '';
                }
            }
	        return s;
	    },

        updateTitle: function() {
            $('#mx-cal-title').text(
                (_s == 0 ? this.r.mmmm[_m] + ', ' + _y :
                (_s == 1 ? _y : _y + ' - ' + (_y + 10))));
        },
        
        updateYears: function() {
            for (var i = 0; i < 12; i++) {
                var c = $('#mx-cal-y-' + i);
                c.text(_y + i - 1);
                if (_y + i - 1 == _y0 && i > 0 && i < 11)
                    c.addClass('mx-cal-current');
                else
                    c.removeClass('mx-cal-current');
            }
        },
        
        updateMonths: function() {
            for (var i = 0; i < 12; i++) {
                var c = $('#mx-cal-m-' + i);
                if (i == _m0 && _y == _y0)
                    c.addClass('mx-cal-current');
                else
                    c.removeClass('mx-cal-current');
            }
        },
        
        updateDates: function() {
            var d1 = this._getFirstDayOfMonth(_y, _m) - this.r.firstDay;
			if (d1 < 0) d1 += 7;
            var m0 = this._getDaysInMonth(_y, _m);
            var m1 = this._getDaysInMonth(_y, _m);
            for (var i = 0; i < 42; i ++)
            {
                var c = $('#mx-cal-d-' + i);
                var d = i - d1 + 1;
                if (d < 1 || d > m1) {
                    c.text(d < 1 ? d + m0 : d - m1);
                    c.addClass('mx-cal-inactive');
                } else {
                    c.text(d);
                    c.removeClass('mx-cal-inactive');
                }
                if (d == _d0 && _m == _m0 && _y == _y0)
                    c.addClass('mx-cal-current');
                else
                    c.removeClass('mx-cal-current');
            }
        },

        scrollV: function(pm) {
            var h = $('#mx-cal-days').height();
            for (var i = 0; i < 3; i++) {
                $(_views[i]).animate({ top: pm + '=' + h }, _t);
            }
        },

        scrollH: function(pm) {
            var v = $(_views[_s]);
            var w = v.width();
            v.animate({ left: pm + '=' + w }, _t / 2, function() {
                v.css('left', pm == '+' ? -w : w);
                v.animate({ left: '0px' }, _t / 2);
            });
        },

        onClickTitle: function () {
            if (_s < 2) {
                this.scrollV('+');
                _s++;
                if (_s == 2) {
                    _y = _y - _y % 10;
                    this.updateYears();
                } else if (_s == 1)
                    this.updateMonths();
                this.updateTitle();
            }
        },

        onClickPrev: function () {
            this.scrollH('-');
            if (_s == 0) {
                _m--; if (_m < 0) { _m = 11; _y--; }
                this.updateDates();
            } else if (_s == 1) {
                _y--;
                this.updateMonths();
            } else if (_s == 2) {
                _y -= 10;
                this.updateYears();
            }
            this.updateTitle();
        },
        
        onClickNext: function () {
            this.scrollH('+');
            if (_s == 0) {
                _m++; if (_m > 11) { _m = 0; _y++; }
                this.updateDates();
            } else if (_s == 1) {
                _y++;
                this.updateMonths();
            } else if (_s == 2) {
                _y += 10;
                this.updateYears();
            }
            this.updateTitle();
        },
 
        onClickYear: function (el) {
            if (_s == 2) {
                this.scrollV('-');
                _s--;
                _y = _y - 1 + parseInt(el.id.substring(9)); // 'mx-cal-y-'.length
                this.updateMonths();
                this.updateTitle();
            }
        },

        onClickMonth: function (el) {
            if (_s == 1) {
                this.scrollV('-');
                _s--;
                _m = parseInt(el.id.substring(9)); // 'mx-cal-m-'.length
                this.updateDates();
                this.updateTitle();
            }
        },

        onClickDay: function (el) {
            if (_s == 0) {
                if (el.id.length > 0) {
                    var d = parseInt(el.id.substring(9)), // 'mx-cal-d-'.length
                    d1 = this._getFirstDayOfMonth(_y, _m) - 1,
                    m0 = this._getDaysInMonth(_y, _m),
                    m1 = this._getDaysInMonth(_y, _m),
                    m = _m,
                    y = _y;
                    d = d - d1 + 1;
                    if (d < 1) {
                        d = d + m0;
                        m--; if (m < 0) { m = 11; y--; }
                    } else if (d > m1) {
                        d = d - m1;
                        m++; if (m > 11) { m = 0; y++; }
                    }
    
                    this.setValue(this._format(y, m, d));
                }
            }
        },
        
        _onKeyDown: function(w, i, e) {
			var me = this;
			if (e.keyCode == 9) {
				if (_i != null) {
					i.val(me._format(_y0, _m0, _d0));
					me.hidePopup();
				} else {
					var v = i.val();
					if (v != _v && v != '') { // trim?
						me._parse(v);
						i.val(me._format(_y0, _m0, _d0));
					}
				}
			} else if (e.keyCode == 13) {
				if (_i == null) 
					me.showPopup(w, i);
				else {
					i.val(this._format(_y0, _m0, _d0));
					me.hidePopup();
				}
			} else if (e.keyCode == 27) {
				if (_i != null) {
					me.hidePopup();
				}
			} else if (e.keyCode == 40) { // DOWN
				if (_i == null)
					me.showPopup(w, i);
			} else {
				setTimeout(function() {
					var v = i.val();
					if (v != _vc) {
						me._parse(v);
						if (_i == null) { 
							me.showPopup(w, i);
						} else {
							me.updateTitle();
							me.updateYears();
							me.updateMonths();
							me.updateDates();
						}
					}
				}, 0);
			}
        },
        
        _onKeyPress: function(w, i, e) {
			//alert('onKeyPress');
        },
        
        _onKeyUp: function(w, i, e) {
			//alert('onKeyUp');
        },
		
        _onFocus: function(w, i, e) {
			if (_i == null)
				_v = _vc = i.val();
		},

        getValue: function() {
            return _i.val();
        },
        
        setValue: function(s) {
            _i.val(s);
			_v = _vc = s;
            this.hidePopup();
        },

        showPopup: function(w, i) {
			this.createCalendar();
			
            var c = $('#mx-cal-popup');
            if (_i != null && _i.get(0) == i.get(0)) {
			    _i = _w = null;
                c.hide();
                i.focus();
            } else {
			    _i = i;
				_w = w;
                i.focus();
                var p = w.position();
                c.css('left', p.left);
                c.css('top', p.top + w.height() + 3);

                var h = '138px';
                $('#mx-cal-days').css('top', '0px');
                $('#mx-cal-months').css('top', '-138px');
                $('#mx-cal-years').css('top', '-276px');

                _s = 0;
                this._parse(this.getValue());
                this.updateTitle();
                this.updateDates();
                
                c.show();
            }
        },
        
        hidePopup: function() {
			if (_i != null) {
				_i.focus();
				_vc = _i.val();
			}
            _i = _w = null;
            $('#mx-cal-popup').hide();
        },
		
		attach: function(w) {

			var me = this, b, i;
			b = w.find('.mx-edt-btn'); // w.children('.mx-edt-btn');
			i = w.find(':input'); // w.children('*').children(':input');

			b.click(function(e){
				e.preventDefault();
				if (me._i == null)
					me._v = me._vc = i.val();
				me.showPopup(w, i);
			});

			i.keydown(function(e) {
				me._onKeyDown(w, i, e);
			}).keypress(function(e) {
				me._onKeyPress(w, i, e);
			}).keyup(function(e) {
				me._onKeyUp(w, i, e);
			}).focus(function(e) {
				me._onFocus(w, i, e);
			});
		}
    };

$.mxCalendar = new MXCalendar(); // singleton instance

$.fn.mxDate = function(options) {
	$.mxCalendar.attach(this);
};

} (jQuery));


