import './datePicker.scss'
const ELEM = '.cd-date-picker';
const THIS = 'current-date';
const DISABLED = 'cd-disable';
const TIPS_OUT = '开始日期超出了结束日期<br>建议重新选择';
const LIMIT_YEAR = [100, 200000];
const ELEM_STATIC = 'cd-date-picker-static';
const ELEM_LIST = 'cd-date-picker-list';
const ELEM_SELECTED = 'cd-date-picker-selected';
const ELEM_HINT = 'cd-date-picker-hint';
const ELEM_PREV = 'cd-date-picker-day-prev';
const ELEM_NEXT = 'cd-date-picker-day-next';
const ELEM_FOOTER = 'cd-date-picker-footer';
const ELEM_CONFIRM = '.cd-date-picker-btns-confirm';
const ELEM_TIME_BTN = '.cd-date-picker-btns-time';
const RANGE_START_DADE = 'start-date';
const RANGE_END_DADE = 'end-date';
const ELEM_DATE_VALUE = 'J-date-value';
const DatePicker = (($) => {
    
    const DefaultConfig = {
        
        elem: "",
        
        bindElem: "",
        
        positionElem: "",
        
        type: 'date',
        
        range: false,
        
        format: 'yyyy-MM-dd',
        value: null,
        
        min: '1900-1-1',
        
        max: '2099-12-31',
        
        limitRange: 1000000,
        
        trigger: 'focus',
        
        show: false,
        
        showBottom: true,
        
        btns: ['clear', 'now', 'confirm'],
        lang: 'cn',
        
        position: null,
        
        calendar: false,
        
        mark: {},
        zIndex: null,
        
        isBackfill: true,
        
        done: null,
        
        change: null,
        
        confirm: null,
        
        isClosePicker: true
    };
    class DatePicker {
        constructor(config) {
            this.index = 5;
            this.limitRangeLegal = true;
            this.listYM = [];
            this._config = this._getConfig(config);
            this.init();
        }
        
        isLeapYear(year) {
            return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
        }
        
        lang() {
            let options = this._config,
                text = {
                    cn: {
                        weeks: ['日', '一', '二', '三', '四', '五', '六'],
                        time: ['时', '分', '秒'],
                        timeTips: '选择时间',
                        startTime: '开始时间',
                        endTime: '结束时间',
                        dateTips: '返回日期',
                        month: ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二'],
                        tools: {
                            confirm: '确定',
                            clear: '清空',
                            now: '现在'
                        }
                    },
                    en: {
                        weeks: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
                        time: ['Hours', 'Minutes', 'Seconds'],
                        timeTips: 'Select Time',
                        startTime: 'Start Time',
                        endTime: 'End Time',
                        dateTips: 'Select Date',
                        month: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
                        tools: {
                            confirm: 'Confirm',
                            clear: 'Clear',
                            now: 'Now'
                        }
                    }
                };
            return text[options.lang] || text['cn'];
        }
        
        init() {
            let self = this;
            let options = this._config,
                dateType = 'yyyy|y|MM|M|dd|d|HH|H|mm|m|ss|s',
                isStatic = options.position === 'static',
                format = {
                    year: 'yyyy',
                    month: 'yyyy-MM',
                    date: 'yyyy-MM-dd',
                    time: 'HH:mm:ss',
                    datetime: 'yyyy-MM-dd HH:mm:ss'
                };
            options.elem = $(options.elem);
            options.positionElem = $(options.positionElem || options.eventElem || options.elem);
            options.eventElem = $(options.eventElem);
            if (!options.elem[0]) return;
            
            if (options.range === true) options.range = '-';
            
            if (options.format === format.date) {
                options.format = format[options.type];
            }
            
            this.format = options.format.match(new RegExp(dateType + '|.', 'g')) || [];
            
            this.EXP_IF = '';
            this.EXP_SPLIT = '';
            $.each(this.format, (i, item) => {
                let EXP = '';
                if (new RegExp(dateType).test(item)) {
                    EXP = '\\d{' + function () {
                        if (new RegExp(dateType).test(self.format[i === 0 ? i + 1 : i - 1] || '')) {
                            if (/^yyyy|y$/.test(item)) return 4;
                            return item.length;
                        }
                        if (/^yyyy$/.test(item)) return '1,4';
                        if (/^y$/.test(item)) return '1,308';
                        return '1,2';
                    }() + '}'
                } else {
                    EXP = '\\' + item
                }
                this.EXP_IF = this.EXP_IF + EXP;
                this.EXP_SPLIT = this.EXP_SPLIT + '(' + EXP + ')';
            });
            this.EXP_IF = new RegExp('^' + (
                options.range ? this.EXP_IF + '\\s\\' + options.range + '\\s' + this.EXP_IF : this.EXP_IF
            ) + '$');
            this.EXP_SPLIT = new RegExp('^' + this.EXP_SPLIT + '$', '');
            
            if (!this.isInput(options.elem[0])) {
                if (options.trigger === 'focus') {
                    options.trigger = 'click';
                }
            }
            if (!options.elem.attr('date-key')) {
                options.elem.attr('date-key', ++this.index);
                options.eventElem.attr('date-key', ++this.index);
            }
            
            let defaultMark = {};
            if (options.calendar && options.lang === 'cn') {
                defaultMark = {
                    '0-1-1': '元旦',
                    '0-2-14': '情人',
                    '0-3-8': '妇女',
                    '0-3-12': '植树',
                    '0-4-1': '愚人',
                    '0-5-1': '劳动',
                    '0-5-4': '青年',
                    '0-6-1': '儿童',
                    '0-9-10': '教师',
                    '0-9-18': '国耻',
                    '0-10-1': '国庆',
                    '0-12-25': '圣诞'
                }
            }
            options.mark = $.extend({}, defaultMark, options.mark);
            
            this.setLimitDate();
            this.elemID = 'cd-date-picker' + options.elem.attr('date-key');
            if (options.show || isStatic) {
                this.render();
            }
            if (!isStatic) {
                this.events();
            }
            
            if (options.value) {
                if (options.value.constructor === Date) {
                    this.setValue(this.parse(0, this.systemDate(options.value)));
                } else {
                    this.setValue(options.value);
                }
            }
            this.addModalClose();
        }
        
        addModalClose() {
            $(window).on('winModalClose', () => {
                this.close();
            })
        }
        
        setLimitDate() {
            let options = this._config;
            $.each(['min', 'max'], (i, item) => {
                let ymd = [],
                    hms = [];
                if (typeof options[item] === 'number') { 
                    let day = options[item];
                    let time = new Date().getTime();
                    let STAMP = 86400000;
                    let thisDate = new Date(
                        day ? (
                            day < STAMP ? time + day * STAMP : day 
                        ) : time
                    );
                    ymd = [thisDate.getFullYear(), thisDate.getMonth() + 1, thisDate.getDate()];
                    day < STAMP || (hms = [thisDate.getHours(), thisDate.getMinutes(), thisDate.getSeconds()]);
                } else {
                    ymd = (options[item].match(/\d+-\d+-\d+/) || [''])[0].split('-');
                    hms = (options[item].match(/\d+:\d+:\d+/) || [''])[0].split(':');
                }
                options[item] = {
                    year: ymd[0] | 0 || new Date().getFullYear(),
                    month: ymd[1] ? (ymd[1] | 0) - 1 : new Date().getMonth(),
                    date: ymd[2] | 0 || new Date().getDate(),
                    hours: hms[0] | 0,
                    minutes: hms[1] | 0,
                    seconds: hms[2] | 0
                };
            });
        }
        
        setDateTimeOrStartTime() {
            let options = this._config;
            if (options.range) {
                this.startTimeInterim = $.extend({}, this.startTime);
                this.endTimeInterim = $.extend({}, this.endTime);
            } else {
                options.dateTimeInterim = $.extend({}, options.dateTime);
            }
        }
        
        resetDateTimeOrStartTime() {
            let options = this._config;
            if (options.range) {
                this.startTime = $.extend({}, this.startTimeInterim);
                this.endTime = $.extend({}, this.endTimeInterim);
            } else {
                options.dateTime = $.extend({}, options.dateTimeInterim);
            }
            this.autoSetDate();
            this.setBtnStatus();
        }
        
        render() {
            let options = this._config;
            let isStatic = options.position === 'static';
            this.elem = $('<div>', {
                id: this.elemID,
                'class': [
                    'cd-date-picker',
                    options.range ? ' cd-date-picker-range' : '',
                    isStatic ? (' ' + ELEM_STATIC) : ''
                ].join('')
            });
            this.elemMain = [];
            this.elemHeader = [];
            this.elemCont = [];
            this.table = [];
            if (options.zIndex) {
                this.elem.css({
                    zIndex: options.zIndex
                })
            }
            $.each(new Array(2), (i) => {
                if (!options.range && i > 0) {
                    return true;
                }
                this.elemMain[i] = $('<div>', {
                    'class': 'cd-date-picker-main cd-date-picker-main-list-' + i,
                    mainIndex: i
                });
                this.elemMain[i].append(this.renderHeaderInput());
                this.elemMain[i].append(this.renderHeader());
                this.elemMain[i].append(this.renderContent());
            });
            $.each(this.elemMain, (i, main) => {
                this.elem.append(main);
            });
            this.remove(this.thisElemDate);
            this.footer = this.renderFooter();
            this.checkDate().calendar(); 
            if (isStatic) {
                options.elem.append(this.elem);
            } else {
                $(document.body).append(this.elem);
                this.position();
            }
            this.changeEvent(); 
            this.thisElemDate = this.elemID;
            this.autoSetDate();
            if (options.ready instanceof Function) {
                options.ready($.extend({}, options.dateTime, {
                    month: options.dateTime.month + 1
                }));
            }
        }
        
        renderHeaderInput() {
            let options = this._config;
            let divInputArea = $('<div>', {
                class: 'cd-date-picker-input'
            });
            let dateInput = $('<input>', {
                class: 'cd-date-picker-ym-input',
                readonly: "readonly"
            });
            let timeInput = $('<input>', {
                class: 'cd-date-picker-time-input',
                readonly: "readonly",
                'btn-type': 'datetime'
            });
            divInputArea.append(dateInput);
            if(options.type == 'datetime'){
                divInputArea.append(timeInput);
            }
            return divInputArea;
        }
        
        renderHeader() {
            let divHeader = $('<div>', {
                'class': 'cd-date-picker-header'
            });
            let headerChild = [
                function () { 
                    let elem = $('<i>', {
                        'class': 'cd-component cd-component-icon_picker_prev_y_arrow cd-date-picker-prev-y'
                    });
                    return elem;
                }(),
                function () { 
                    let elem = $('<i>', {
                        'class': 'cd-component cd-component-icon_picker_prev_m_arrow cd-date-picker-prev-m'
                    });
                    return elem;
                }(),
                function () { 
                    let elem = $('<div>', {
                        'class': 'cd-date-picker-set-ym'
                    });
                    let spanY = $('<span>');
                    let spanM = $('<span>');
                    elem.append(spanY);
                    elem.append(spanM);
                    return elem;
                }(),
                function () { 
                    let elem = $('<i>', {
                        'class': 'cd-component cd-component-icon_picker_next_m_arrow cd-date-picker-next-m'
                    });
                    return elem;
                }(),
                function () { 
                    let elem = $('<i>', {
                        'class': 'cd-component cd-component-icon_picker_next_y_arrow cd-date-picker-next-y'
                    });
                    return elem;
                }()
            ];
            $.each(headerChild, function (i, item) {
                divHeader.append(item);
            });
            this.elemHeader.push(headerChild);
            return divHeader;
        }
        
        renderContent() {
            let lang = this.lang();
            let divContent = $('<div>', {
                'class': 'cd-date-picker-content'
            });
            let table = $('<table>');
            let thead = $('<thead>');
            let theadTr = $('<tr>');
            thead.append(theadTr);
            $.each(new Array(6), function (i) { 
                let tr = table[0].insertRow(0);
                $.each(new Array(7), function (j) {
                    
                    if (i === 0) {
                        let th = $('<th>');
                        th.html(lang.weeks[j]);
                        theadTr.append(th);
                    }
                    
                    tr.insertCell(j);
                });
            });
            thead.insertBefore(table.children(0)); 
            divContent.append(table);
            this.elemCont.push(divContent);
            this.table.push(table);
            return divContent;
        }
        
        renderFooter() {
            let options = this._config;
            let isStatic = options.position === 'static';
            let lang = this.lang();
            let divFooter = $('<div>', {
                'class': ELEM_FOOTER
            });
            $(divFooter).html(function () {
                let html = [];
                let btns = [];
                if (options.type === 'datetime') {
                }
                $.each(options.btns, function (i, item) {
                    let title = lang.tools[item] || 'btn';
                    if (options.range && item === 'now') {
                        return;
                    }
                    if (isStatic && item === 'clear') {
                        title = options.lang === 'cn' ? '清除' : 'Reset';
                    }
                    btns.push('<span btn-type="' + item + '" class="cd-date-picker-btns-' + item + ' cd-middle-black-border-btn">' + title + '</span>');
                });
                html.push('<div class="cd-date-picker-footer-btns">' + btns.join('') + '</div>');
                return html.join('');
            }());
            options.showBottom && this.elem.append(divFooter);
            return divFooter;
        }
        
        remove(prev) {
            let elem = $('#' + (prev || this.elemID));
            if (!elem.hasClass(ELEM_STATIC)) {
                this.checkDate(function () {
                    elem.remove();
                }, true);
            }
            return this;
        }
        
        position() {
            let options = this._config;
            let elem = options.positionElem[0] || this.bindElem || options.elem[0];
            let rect = elem.getBoundingClientRect();
            let elemWidth = this.elem.outerWidth();
            let elemHeight = this.elem.outerHeight(); 
            let scrollArea = function (type) {
                type = type ? 'scrollLeft' : 'scrollTop';
                return document.body[type] | document.documentElement[type];
            };
            let winArea = function (type) {
                return type ? $(window).width() : $(window).height();
            };
            let docArea = function (type) {
                return type ? $(document).width() : $(document).height();
            };
            let margin = 5;
            let left = rect.left;
            let top = rect.bottom;
            
            if (left + elemWidth + margin > winArea('width')) {
                left = winArea('width') - elemWidth - margin + $(document).scrollLeft();
            }
            
            if (top + elemHeight + margin > docArea()) {
                
                top = rect.top > elemHeight ? rect.top - elemHeight : docArea() - elemHeight;
                top = top - margin * 2;
            }
            if (options.position) {
                this.elem.css({
                    position: options.position
                });
            }
            this.elem.css({
                left: left + (options.position === 'fixed' ? 0 : scrollArea(1)),
                top: top + (options.position === 'fixed' ? 0 : scrollArea())
            });
        }
        
        hint(content) {
            let div = $('<div>', {
                'class': ELEM_HINT
            });
            div.html(content || '');
            $(this.elem).find('.' + ELEM_HINT).remove();
            this.elem.append(div);
            clearTimeout(this.hinTimer);
            this.hinTimer = setTimeout(() => {
                $(this.elem).find('.' + ELEM_HINT).remove();
            }, 3000);
        }
        
        getAsYM(Y, M, type) {
            type ? M-- : M++;
            if (M < 0) {
                M = 11;
                Y--;
            }
            if (M > 11) {
                M = 0;
                Y++;
            }
            return [Y, M];
        }
        
        systemDate(newDate) {
            let nowDate = newDate || new Date();
            return {
                year: nowDate.getFullYear(),
                month: nowDate.getMonth(),
                date: nowDate.getDate(),
                hours: newDate ? newDate.getHours() : 0,
                minutes: newDate ? newDate.getMinutes() : 0,
                seconds: newDate ? newDate.getSeconds() : 0
            }
        }
        
        systemDateEnd() {
            let nowDate = new Date();
            return {
                year: nowDate.getFullYear(),
                month: nowDate.getMonth(),
                date: nowDate.getDate(),
                hours: 23,
                minutes: 59,
                seconds: 59
            }
        }
        
        checkDate(fn, isClear) {
            let self = this;
            let options = this._config;
            let dateTime = options.dateTime = options.dateTime || this.systemDate();
            let thisMaxDate;
            let error;
            let elem = this.bindElem || options.elem[0];
            let value = options.value || this.systemDate(); 
            if (this.isInput(elem)) {
                if (isClear) {
                    value = elem.value;
                } else {
                    if (options.range) {
                        value = elem.value ? elem.value : this.systemDateToString(this.systemDate()) + " - " + this.systemDateToString(this.systemDateEnd());
                    } else {
                        value = elem.value ? elem.value : this.systemDateToString(this.systemDate());
                    }
                }
            } else {
                if (options.position === 'static') {
                    value = "";
                } else {
                    if (this.EXP_IF.test(elem.innerHTML)) {
                        value = elem.innerHTML;
                    }
                }
            }
            let checkValid = (dateTime) => {
                if (dateTime.year > LIMIT_YEAR[1]) {
                    dateTime.year = LIMIT_YEAR[1];
                    error = true;
                }
                if (dateTime.month > 11) {
                    dateTime.month = 11;
                    error = true;
                }
                if (dateTime.hours > 23) {
                    dateTime.hours = 0;
                    error = true;
                }
                if (dateTime.minutes > 59) {
                    dateTime.minutes = 0;
                    dateTime.hours++;
                    error = true;
                }
                if (dateTime.seconds > 59) {
                    dateTime.seconds = 0;
                    dateTime.minutes++;
                    error = true;
                }
                thisMaxDate = this.getEndDate(dateTime.month + 1, dateTime.year);
                if (dateTime.date > thisMaxDate) {
                    dateTime.date = thisMaxDate;
                    error = true;
                }
            };
            let initDate = (dateTime, value, index) => {
                let startEnd = ['startTime', 'endTime'];
                value = (value.match(this.EXP_SPLIT) || []).slice(1);
                index = index || 0;
                if (options.range) {
                    this[startEnd[index]] = this[startEnd[index]] || {};
                }
                $.each(this.format, function (i, item) {
                    let thisv = parseFloat(value[i]);
                    if (value[i].length < item.length) {
                        error = true;
                    }
                    if (/yyyy|y/.test(item)) { 
                        if (thisv < LIMIT_YEAR[0]) {
                            thisv = LIMIT_YEAR[0], error = true;
                        } 
                        dateTime.year = thisv;
                    } else if (/MM|M/.test(item)) { 
                        if (thisv < 1) {
                            thisv = 1;
                            error = true;
                        }
                        dateTime.month = thisv - 1;
                    } else if (/dd|d/.test(item)) { 
                        if (thisv < 1) {
                            thisv = 1;
                            error = true;
                        }
                        dateTime.date = thisv;
                    } else if (/HH|H/.test(item)) { 
                        if (thisv < 1) {
                            thisv = 0;
                            error = true;
                        }
                        dateTime.hours = thisv;
                        options.range && (self[startEnd[index]].hours = thisv);
                    } else if (/mm|m/.test(item)) { 
                        if (thisv < 1) {
                            thisv = 0;
                            error = true;
                        }
                        dateTime.minutes = thisv;
                        options.range && (self[startEnd[index]].minutes = thisv);
                    } else if (/ss|s/.test(item)) { 
                        if (thisv < 1) {
                            thisv = 0;
                            error = true;
                        }
                        dateTime.seconds = thisv;
                        options.range && (self[startEnd[index]].seconds = thisv);
                    }
                });
                checkValid(dateTime)
            };
            if (fn === 'limit') {
                checkValid(dateTime);
                return this;
            }
            value = value || options.value;
            if (typeof value === 'string') {
                value = value.replace(/\s+/g, ' ').replace(/^\s|\s$/g, '');
            }
            if (this.startState && !this.endState) {
                delete this.startState;
                this.endState = true;
            }
            if (typeof value === 'string' && value) {
                if (this.EXP_IF.test(value)) { 
                    if (options.range) {
                        value = value.split(' ' + options.range + ' ');
                        this.startDate = this.startDate || this.systemDate();
                        this.endDate = this.endDate || this.systemDate();
                        options.dateTime = $.extend({}, this.startDate);
                        $.each([this.startDate, this.endDate], function (i, item) {
                            initDate(item, value[i], i);
                        });
                    } else {
                        initDate(dateTime, value)
                    }
                } else {
                    this.hint('日期格式不合法<br>必须遵循下述格式：<br>' + (
                        options.range ? (options.format + ' ' + options.range + ' ' + options.format) : options.format
                    ) + '<br>已为你重置');
                    error = true;
                }
            } else if (value && value.constructor === Date) { 
                options.dateTime = this.systemDate(value);
            } else {
                options.dateTime = this.systemDate();
                delete this.startState;
                delete this.endState;
                delete this.startDate;
                delete this.endDate;
                delete this.startTime;
                delete this.endTime;
            }
            checkValid(dateTime);
            this.setDateTimeOrStartTime();
            if (error && value) {
                this.setValue(
                    options.range ? (self.endDate ? self.parse() : '') : self.parse()
                );
            }
            fn && fn();
            return this;
        }
        
        mark(td, YMD) {
            let mark;
            let options = this._config;
            $.each(options.mark, function (key, title) {
                var keys = key.split('-');
                if ((keys[0] == YMD[0] || keys[0] == 0) 
                    && (keys[1] == YMD[1] || keys[1] == 0) 
                    && keys[2] == YMD[2]) { 
                    mark = title || YMD[2];
                }
            });
            mark && td.html('<span class="cd-date-picker-day-mark">' + mark + '</span>');
            return this;
        }
        
        limit(elem, date, index, time) {
            let options = this._config;
            let timestrap = {};
            let dateTime = options[index > 41 ? 'endDate' : 'dateTime'];
            if (options.range) {
                
                if (index == 1 && this.endDate) {
                    dateTime = this.endDate;
                } else if (index == 0 && this.startDate) {
                    dateTime = this.startDate;
                }
            }
            let isOut;
            let thisDateTime = $.extend({}, dateTime, date || {});
            $.each({
                    now: thisDateTime,
                    min: options.min,
                    max: options.max
                },
                (key, item) => {
                    timestrap[key] = this.newDate($.extend({
                            year: item.year,
                            month: item.month,
                            date: item.date
                        },
                        function () {
                            let hms = {};
                            $.each(time, function (i, keys) {
                                hms[keys] = item[keys];
                            });
                            return hms;
                        }())
                    ).getTime();  
                }
            );
            isOut = timestrap.now < timestrap.min || timestrap.now > timestrap.max;
            elem && elem[isOut ? 'addClass' : 'removeClass'](DISABLED);
            return isOut;
        }
        
        calendar(value) {
            let options = this._config;
            let dateTime = value || options.dateTime;
            let thisDate = new Date(), startWeek, prevMaxDate, thisMaxDate;
            let lang = this.lang();
            let isAlone = options.type !== 'date' && options.type !== 'datetime';
            let index = value ? 1 : 0;
            let tds = $(this.table[index]).find('td');
            let elemYM = $(this.elemHeader[index][2]).find('span');
            if (dateTime.year < LIMIT_YEAR[0]) {
                dateTime.year = LIMIT_YEAR[0];
                this.hint('最低只能支持到公元' + LIMIT_YEAR[0] + '年');
            }
            if (dateTime.year > LIMIT_YEAR[1]) {
                dateTime.year = LIMIT_YEAR[1];
                this.hint('最高只能支持到公元' + LIMIT_YEAR[1] + '年');
            }
            if (!this.firstDate) {
                this.firstDate = $.extend({}, dateTime);
            }
            thisDate.setFullYear(dateTime.year, dateTime.month, 1);
            startWeek = thisDate.getDay();
            prevMaxDate = this.getEndDate(dateTime.month || 12, dateTime.year); 
            thisMaxDate = this.getEndDate(dateTime.month + 1, dateTime.year); 
            $.each(tds, (index, item) => {
                let YMD = [dateTime.year, dateTime.month];
                let st = 0;
                item = $(item);
                item.removeAttr('class');
                if (index < startWeek) {
                    st = prevMaxDate - startWeek + index;
                    item.addClass('cd-date-picker-day-prev');
                    YMD = this.getAsYM(dateTime.year, dateTime.month, 'sub');
                } else if (index >= startWeek && index < thisMaxDate + startWeek) {
                    st = index - startWeek;
                    
                    if (!options.range) {
                        st + 1 === dateTime.date && item.addClass(THIS);
                    }
                } else {
                    st = index - thisMaxDate - startWeek;
                    item.addClass('cd-date-picker-day-next');
                    YMD = this.getAsYM(dateTime.year, dateTime.month);
                }
                YMD[1]++;
                YMD[2] = st + 1;
                let itemSub = $('<div></div>').html('<span>' + YMD[2] + '</span>');
                
                item.attr(ELEM_DATE_VALUE, YMD.join('-')).html(itemSub);
                
                if (YMD[1] == this.systemDate().month + 1 && YMD[2] == this.systemDate().date) {
                    item.addClass('cd-date-picker-tody');
                }
                this.mark(item, YMD).limit(item, {
                    year: YMD[0],
                    month: YMD[1] - 1,
                    date: YMD[2]
                }, index);
            });
            $(elemYM[0]).attr('date-ym', dateTime.year + '-' + (dateTime.month + 1));
            $(elemYM[1]).attr('date-ym', dateTime.year + '-' + (dateTime.month + 1));
            if (options.lang === 'cn') {
                $(elemYM[0]).attr('date-type', 'year').html(dateTime.year + '年');
                $(elemYM[1]).attr('date-type', 'month').html((dateTime.month + 1) + '月');
            } else {
                $(elemYM[0]).attr('ldate-type', 'month').html(lang.month[dateTime.month]);
                $(elemYM[1]).attr('date-type', 'year').html(dateTime.year);
            }
            if (isAlone) {
                if (options.range) {
                    value ? this.endDate = (this.endDate || {
                        year: dateTime.year + (options.type === 'year' ? 1 : 0),
                        month: dateTime.month + (options.type === 'month' ? 0 : -1)
                    }) : (this.startDate = this.startDate || {
                        year: dateTime.year,
                        month: dateTime.month
                    });
                    if (value) {
                        this.listYM = [
                            [this.startDate.year, this.startDate.month + 1],
                            [this.endDate.year, this.endDate.month + 1]
                        ];
                        this.list(options.type, 0).list(options.type, 1);
                        if (options.type === 'time') {
                            this.setBtnStatus('时间', $.extend({}, this.systemDate(), this.startTime), $.extend({}, this.systemDate(), this.endTime));
                        } else {
                            this.setBtnStatus(true);
                        }
                    }
                }
                if (!options.range) {
                    this.listYM = [[dateTime.year, dateTime.month + 1]];
                    this.list(options.type, 0);
                }
            }
            if (options.range && !value) {
                let EYM = this.getAsYM(dateTime.year, dateTime.month);
                this.calendar($.extend({}, dateTime, {
                    year: EYM[0],
                    month: EYM[1]
                }));
            }
            if (!options.range) this.limit($(this.footer).find(ELEM_CONFIRM), null, 0, ['hours', 'minutes', 'seconds']);
            if (options.range && value && !isAlone) {
                this.stampRange();
            }
            return this;
        }
        
        list(type, index, mainIndex) {
            let self = this;
            let options = this._config;
            let dateTime = options.dateTime;
            let lang = this.lang();
            let isAlone = options.range && options.type !== 'date' && options.type !== 'datetime'; 
            let ul = $('<ul>', {
                'class': ELEM_LIST + ' ' + ({
                    year: 'cd-date-picker-year-list',
                    month: 'cd-date-picker-month-list',
                    time: 'cd-date-picker-time-list'
                })[type]
            });
            let elemHeader = this.elemHeader[index];
            let elemYM = $(elemHeader[2]).find('span');
            let elemCont = this.elemCont[index || 0];
            let haveList = $(elemCont).find('.' + ELEM_LIST)[0];
            let isCN = options.lang === 'cn';
            let text = isCN ? '年' : '';
            let listYM = this.listYM[index] || {};
            let hms = ['hours', 'minutes', 'seconds'];
            let startEnd = ['startTime', 'endTime'][index];
            if (listYM[0] < 1) listYM[0] = 1;
            if (type === 'year') { 
                let yearNum;
                let startY = yearNum = listYM[0] - 7;
                if (startY < 1) {
                    startY = yearNum = 1;
                }
                $.each(new Array(15), () => {
                    let li = $('<li>', {
                        'date-ym': yearNum
                    });
                    let ymd = {year: yearNum};
                    yearNum == listYM[0] && $(li).addClass(THIS);
                    li.html(yearNum + text);
                    ul.append(li);
                    if (yearNum < this.firstDate.year) {
                        ymd.month = options.min.month;
                        ymd.date = options.min.date;
                    } else if (yearNum >= this.firstDate.year) {
                        ymd.month = options.max.month;
                        ymd.date = options.max.date;
                    }
                    this.limit($(li), ymd, index);
                    yearNum++;
                });
                $(elemYM[isCN ? 0 : 1]).attr('date-ym', (yearNum - 8) + '-' + listYM[1]).html((startY + text) + ' - ' + (yearNum - 1 + text));
            } else if (type === 'month') { 
                $.each(new Array(12), function (i) {
                    let li = $('<li>', {
                        'date-ym': i
                    });
                    let ymd = {
                        year: listYM[0],
                        month: i
                    };
                    i + 1 == listYM[1] && $(li).addClass(THIS);
                    li.html(lang.month[i] + (isCN ? '月' : ''));
                    ul.append(li);
                    if (listYM[0] < self.firstDate.year) {
                        ymd.date = options.min.date;
                    } else if (listYM[0] >= self.firstDate.year) {
                        ymd.date = options.max.date;
                    }
                    self.limit($(li), ymd, index);
                });
                $(elemYM[isCN ? 0 : 1]).attr('date-ym', listYM[0] + '-' + listYM[1]).html(listYM[0] + text);
            } else if (type === 'time') { 
                var setTimeStatus = () => {
                    $(ul).find('ol').each((i, ol) => {
                        $(ol).find('li').each((ii, li) => {
                            let minutes = $(li).html();
                            self.limit($(li), [
                                    {
                                        hours: ii
                                    },
                                    {
                                        hours: this[startEnd].hours,
                                        minutes: minutes
                                    },
                                    {
                                        hours: this[startEnd].hours,
                                        minutes: this[startEnd].minutes,
                                        seconds: ii
                                    }
                                ][i],
                                index,
                                [['hours'], ['hours', 'minutes'], ['hours', 'minutes', 'seconds']][i]
                            );
                        });
                    });
                    if (!options.range) {
                        self.limit($(self.footer).find(ELEM_CONFIRM), self[startEnd], 0, ['hours', 'minutes', 'seconds']);
                    }
                    self.autoSetDate();
                };
                if (options.range) {
                    if (!self[startEnd]) {
                        self[startEnd] = {
                            hours: 0,
                            minutes: 0,
                            seconds: 0
                        };
                    }
                } else {
                    self[startEnd] = dateTime;
                }
                let timeHeader = $('<div class="cd-date-picker-time-title"></div>');
                $.each(lang.time, (i) => {
                    timeHeader.append('<p class="cd-item-title">' + lang.time[i] + '</p>');
                });
                ul.append(timeHeader);
                $.each([24], (i, item) => {
                    let li = $('<li>');
                    let childUL = ['<ol>'];
                    $.each(new Array(item), (ii) => {
                        childUL.push('<li' + (self[startEnd][hms[0]] === ii ? ' class="' + THIS + '"' : '') + '>' + self.digit(ii, 2) + '</li>');
                    });
                    li.html(childUL.join('') + '</ol>');
                    ul.append(li);
                });
                $.each([60], (i, item) => {
                    let li = $('<li>');
                    let childUL = ['<ol>'];
                    $.each(new Array(item), (ii) => {
                        childUL.push('<li' + (self[startEnd][hms[1]] === ii ? ' class="' + THIS + '"' : '') + '>' + self.digit(ii, 2) + '</li>');
                    });
                    li.html(childUL.join('') + '</ol>');
                    ul.append(li);
                });
                $.each([60], (i, item) => {
                    let li = $('<li>');
                    let childUL = ['<ol>'];
                    $.each(new Array(item), (ii) => {
                        childUL.push('<li' + (self[startEnd][hms[2]] === ii ? ' class="' + THIS + '"' : '') + '>' + self.digit(ii, 2) + '</li>');
                    });
                    li.html(childUL.join('') + '</ol>');
                    ul.append(li);
                });
                ul.append(self.listFooterForTime());
                setTimeStatus();
            }
            if (haveList) {
                elemCont[0].removeChild(haveList);
            }
            if (type == 'time') {
                if (index == mainIndex) {
                    ul.removeClass('hide');
                } else {
                    ul.addClass('hide');
                }
            }
            elemCont.append(ul);
            if (type === 'year' || type === 'month') {
                $(this.elemMain[index]).addClass('cd-date-picker-ym-show');
                $(ul).find('li').on('click', (evt) => {
                    let ym = $(evt.target).attr('date-ym') | 0;
                    if ($(evt.target).hasClass(DISABLED)) return;
                    if (index === 0) {
                        dateTime[type] = ym;
                        if (isAlone) this.startDate[type] = ym;
                        self.limit($(this.footer).find(ELEM_CONFIRM), null, 0);
                    } else { 
                        if (isAlone) { 
                            self.endDate[type] = ym;
                        } else { 
                            let YM = type === 'year' ? this.getAsYM(ym, listYM[1] - 1, 'sub') : this.getAsYM(listYM[0], ym, 'sub');
                            $.extend(dateTime, {
                                year: YM[0],
                                month: YM[1]
                            });
                        }
                    }
                    if (options.type === 'year' || options.type === 'month') {
                        $(ul).find('.' + THIS).removeClass(THIS);
                        $(evt.target).addClass(THIS);
                        if (options.type === 'month' && type === 'year') {
                            self.listYM[index][0] = ym;
                            isAlone && (self[['startDate', 'endDate'][index]].year = ym);
                            self.list('month', index);
                        }
                    } else {
                        self.checkDate('limit').calendar();
                        self.closeList();
                    }
                    self.setBtnStatus(); 
                    options.range || self.done(null, 'change');
                    $(self.footer).find(ELEM_TIME_BTN).removeClass(DISABLED);
                });
            } else {
                let scroll = function () { 
                    $(ul).find('ol').each(function (i, el) {
                        let ol = $(el);
                        let time = self[startEnd][hms[i]];
                        
                        if (i == 1) {
                            time = Math.abs(time);
                        }
                        ol.scrollTop(30 * time);
                    });
                };
                scroll();
                $(this.elemMain[index]).addClass('cd-date-picker-time-show');
                $(ul).find('ol').each(function (i) {
                    let ol = this;
                    $(ol).find('li').on('click', (evt) => {
                        let $Node = $(evt.target);
                        let value = $Node.html() | 0;
                        if ($Node.hasClass(DISABLED)) {
                            return;
                        }
                        if (options.range) {
                            self[startEnd][hms[i]] = value;
                        } else {
                            dateTime[hms[i]] = value;
                        }
                        $(ol).find('.' + THIS).removeClass(THIS);
                        $(evt.target).addClass(THIS);
                        setTimeStatus();
                        scroll();
                        (self.endDate || options.type === 'time') && self.done(null, 'change');
                        self.setBtnStatus();
                        self.autoSetDate();
                    });
                });
            }
            return self;
        }
        
        listFooterForTime() {
            return '<div class="cd-date-picker-list-footer">' +
                '<a href="javascript:;" class="cd-date-picker-btns-clear J_clear">取消</a>' +
                '<a href="javascript:;" class="cd-date-picker-btns-confirm J_confirm">确定</a>' +
                '</div>';
        }
        
        closeList() {
            $.each(this.elemCont, (index, item) => {
                $(item).find('.' + ELEM_LIST).remove();
                $(this.elemMain[index]).removeClass('cd-date-picker-ym-show cd-date-picker-time-show');
            });
        }
        
        setBtnStatus(tips, start, end) {
            let options = this._config;
            let isOut, elemBtn = $(this.footer).find(ELEM_CONFIRM);
            let isAlone = options.range && options.type !== 'date' && options.type !== 'time';
            if (isAlone) {
                start = start || this.startDate;
                end = end || this.endDate;
                isOut = this.newDate(start).getTime() > this.newDate(end).getTime();
                (this.limit(null, start) || this.limit(null, end)) ? elemBtn.addClass(DISABLED) : elemBtn[isOut ? 'addClass' : 'removeClass'](DISABLED);
                if (tips && isOut) {
                    this.hint(
                        typeof tips === 'string' ? TIPS_OUT.replace(/日期/g, tips) : TIPS_OUT
                    );
                }
            }
        }
        
        systemDateToString(date) {
            let dateTime = date || this.systemDate();
            let format = this.format.concat();
            $.each(format, (i, item) => {
                if (/yyyy|y/.test(item)) { 
                    format[i] = this.digit(dateTime.year, item.length);
                } else if (/MM|M/.test(item)) { 
                    format[i] = this.digit(dateTime.month + 1, item.length);
                } else if (/dd|d/.test(item)) { 
                    format[i] = this.digit(dateTime.date, item.length);
                } else if (/HH|H/.test(item)) { 
                    format[i] = this.digit(dateTime.hours, item.length);
                } else if (/mm|m/.test(item)) { 
                    format[i] = this.digit(dateTime.minutes, item.length);
                } else if (/ss|s/.test(item)) { 
                    format[i] = this.digit(dateTime.seconds, item.length);
                }
            });
            return format.join('');
        }
        
        parse(state, date) {
            let options = this._config;
            let dateTime = "";
            if (date) {
                dateTime = date;
            } else {
                if (state) {
                    if (this.endDate) {
                        dateTime = $.extend({}, this.endDate, this.endTime);
                    } else {
                        dateTime = options.dateTime;
                    }
                } else {
                    if (options.range) {
                        if (this.startDate) {
                            dateTime = $.extend({}, this.startDate, this.startTime);
                        }
                    } else {
                        dateTime = options.dateTime;
                    }
                }
            }
            let format = this.format.concat();
            $.each(format, (i, item) => {
                if (/yyyy|y/.test(item)) { 
                    format[i] = this.digit(dateTime.year, item.length);
                } else if (/MM|M/.test(item)) { 
                    format[i] = this.digit(dateTime.month + 1, item.length);
                } else if (/dd|d/.test(item)) { 
                    format[i] = this.digit(dateTime.date, item.length);
                } else if (/HH|H/.test(item)) { 
                    format[i] = this.digit(dateTime.hours, item.length);
                } else if (/mm|m/.test(item)) { 
                    format[i] = this.digit(dateTime.minutes, item.length);
                } else if (/ss|s/.test(item)) { 
                    format[i] = this.digit(dateTime.seconds, item.length);
                }
            });
            if (options.range && !state) {
                return format.join('') + ' ' + options.range + ' ' + this.parse(1);
            }
            return format.join('');
        }
        
        newDate(dateTime) {
            dateTime = dateTime || {};
            return new Date(
                dateTime.year || 1,
                dateTime.month || 0,
                dateTime.date || 1,
                dateTime.hours || 0,
                dateTime.minutes || 0,
                dateTime.seconds || 0
            );
        }
        
        setValue(value) {
            let options = this._config;
            let elem = this.bindElem || options.elem[0];
            let valType = this.isInput(elem) ? 'val' : 'html';
            
            if (!options.isBackfill || options.position === 'static') {
            } else {
                $(elem)[valType](value || '');
            }
            return this;
        }
        
        stampRange() {
            let options = this._config;
            let startTime, endTime;
            let tds = $(this.elem).find('td');
            if (options.range && !this.endDate) {
                $(this.footer).find(ELEM_CONFIRM).addClass(DISABLED);
            }
            if (!this.endDate) {
                return;
            }
            startTime = this.newDate({
                year: this.startDate.year,
                month: this.startDate.month,
                date: this.startDate.date
            }).getTime();
            endTime = this.newDate({
                year: this.endDate.year,
                month: this.endDate.month,
                date: this.endDate.date
            }).getTime();
            if (startTime > endTime) {
                return this.hint(TIPS_OUT);
            }
            $.each(tds, (i, item) => {
                let ymd = $(item).attr(ELEM_DATE_VALUE).split('-');
                let thisTime = this.newDate({
                    year: ymd[0],
                    month: ymd[1] - 1,
                    date: ymd[2]
                }).getTime();
                $(item).removeClass(ELEM_SELECTED);
                $(item).removeClass(THIS);
                $(item).removeClass(RANGE_START_DADE);
                $(item).removeClass(RANGE_END_DADE);
                if (thisTime === startTime || thisTime === endTime) {
                    let clsName = $(item).hasClass(ELEM_PREV) || $(item).hasClass(ELEM_NEXT) ? ELEM_SELECTED : THIS;
                    $(item).addClass(clsName);
                }
                
                if (thisTime == startTime) {
                    $(item).addClass(RANGE_START_DADE);
                }
                
                if (thisTime == endTime) {
                    $(item).addClass(RANGE_END_DADE);
                } else {
                    $(item).removeClass(RANGE_END_DADE);
                }
                if (thisTime > startTime && thisTime < endTime) {
                    $(item).addClass(ELEM_SELECTED);
                } else {
                    $(item).removeClass(ELEM_SELECTED);
                }
            });
        }
        
        done(param, type) {
            let options = this._config;
            let start = $.extend({}, this.startDate ? $.extend(this.startDate, this.startTime) : options.dateTime);
            let end = $.extend({}, $.extend(this.endDate, this.endTime));
            $.each([start, end], (i, item) => {
                if (!('month' in item)) {
                    return;
                }
                $.extend(item, {
                    month: item.month + 1
                });
            });
            param = param || [this.parse(), start, end];
            
            if (options.range) {
                let startString = this.newDate({...start}).getTime();
                let endString = this.newDate({...end}).getTime();
                let STAMP = 86400000;
                if (startString >= endString - STAMP * options.limitRange) {
                    this.limitRangeLegal = true;
                } else {
                    this.limitRangeLegal = false;
                }
            }
            if (this.limitRangeLegal) {
                typeof options[type || 'done'] === 'function' && options[type || 'done'].apply(options, param);
            }
            return this;
        }
        
        confirm(param) {
            let options = this._config;
            let start = $.extend({}, this.startDate ? $.extend(this.startDate, this.startTime) : options.dateTime);
            let end = $.extend({}, $.extend(this.endDate, this.endTime));
            $.each([start, end], (i, item) => {
                if (!('month' in item)) {
                    return;
                }
                $.extend(item, {
                    month: item.month + 1
                });
            });
            param = param || [this.parse(), start, end];
            
            if (options.range) {
                let startString = this.newDate({...start}).getTime();
                let endString = this.newDate({...end}).getTime();
                let STAMP = 86400000;
                if (startString >= endString - STAMP * options.limitRange) {
                    this.limitRangeLegal = true;
                } else {
                    this.limitRangeLegal = false;
                    this.hint('开始时间与结束时间不能超过' + options.limitRange + '天');
                }
            }
            if (this.limitRangeLegal) {
                typeof options['confirm'] === 'function' && options['confirm'].apply(options, param);
            }
            return this;
        }
        
        choose(td) {
            let self = this;
            let options = this._config;
            let dateTime = options.dateTime;
            let tds = $(self.elem).find('td');
            let YMD = td.attr(ELEM_DATE_VALUE).split('-');
            let setDateTime = function (one, isSetStartDate) {
                one && $.extend(dateTime, YMD);
                if (options.range) {
                    if (!isSetStartDate) {
                        return;
                    }
                    if (self.startDate) {
                        $.extend(self.startDate, YMD);
                    } else {
                        self.startDate = $.extend({}, YMD, self.startTime)
                    }
                    self.startYMD = YMD;
                }
            };
            YMD = {
                year: YMD[0] | 0,
                month: (YMD[1] | 0) - 1,
                date: YMD[2] | 0
            };
            if (td.hasClass(DISABLED)) return;
            
            if (options.range) {
                $.each(['startTime', 'endTime'], (i, item) => {
                    let deftTime = {
                        hours: 0,
                        minutes: 0,
                        seconds: 0
                    };
                    if (item == 'endTime') {
                        deftTime.hours = 23;
                        deftTime.minutes = 59;
                        deftTime.seconds = 59;
                    } else {
                        deftTime.hours = 0;
                        deftTime.minutes = 0;
                        deftTime.seconds = 0;
                    }
                    self[item] = self[item] || deftTime;
                });
                if (self.endState) {
                    
                    setDateTime(true, true);
                    delete self.endState;
                    delete self.endDate;
                    self.startState = true;
                    tds.removeClass(THIS);
                    tds.removeClass(ELEM_SELECTED);
                    tds.removeClass(RANGE_END_DADE);
                    tds.removeClass(RANGE_START_DADE);
                    td.addClass(THIS);
                } else if (self.startState) {
                    
                    td.addClass(THIS);
                    if (self.endDate) {
                        $.extend(self.endDate, YMD);
                    } else {
                        self.endDate = $.extend({}, YMD, self.endTime)
                    }
                    if (self.newDate(YMD).getTime() < self.newDate(self.startYMD).getTime()) {
                        let startDate = $.extend({}, self.endDate, {
                            hours: self.startDate.hours,
                            minutes: self.startDate.minutes,
                            seconds: self.startDate.seconds
                        });
                        $.extend(self.endDate, self.startDate, {
                            hours: self.endDate.hours,
                            minutes: self.endDate.minutes,
                            seconds: self.endDate.seconds
                        });
                        self.startDate = startDate;
                    }
                    options.showBottom || self.done();
                    self.stampRange(); 
                    self.endState = true;
                    setDateTime(true, false);
                    self.done(null, 'change');
                } else {
                    
                    setDateTime(true, true);
                    delete self.endState;
                    delete self.endDate;
                    tds.removeClass(THIS);
                    tds.removeClass(ELEM_SELECTED);
                    tds.removeClass(RANGE_END_DADE);
                    tds.removeClass(RANGE_START_DADE);
                    td.addClass(THIS);
                    self.startState = true;
                }
                $(self.footer).find(ELEM_CONFIRM)[self.endDate ? 'removeClass' : 'addClass'](DISABLED);
            } else if (options.position === 'static') { 
                setDateTime(true);
                self.calendar().done().done(null, 'change');
            } else if (options.type === 'date') {
                setDateTime(true);
                self.setValue(self.parse()).remove().done();
            } else if (options.type === 'datetime') {
                setDateTime(true);
                self.calendar().done(null, 'change');
            }
        }
        
        autoSetDate() {
            let options = this._config;
            let currentDate = this.parse();
            if (currentDate.indexOf('undefined') >= 0) {
                currentDate = this.parse(0, this.systemDate());
            }
            if (currentDate) {
                let dateArray = currentDate.split(" ");
                
                if (options.range) {
                    if (dateArray[3]) {
                        $("#" + this.elemID).find('.cd-date-picker-ym-input').eq(1).val(dateArray[3]);
                    }
                    if (dateArray[4]) {
                        $("#" + this.elemID).find('.cd-date-picker-time-input').eq(1).val(dateArray[4]);
                    }
                    if(options.type == 'date' && dateArray[2]){
                        $("#" + this.elemID).find('.cd-date-picker-ym-input').eq(1).val(dateArray[2]);
                    }
                }
                
                if (dateArray[0]) {
                    $("#" + this.elemID).find('.cd-date-picker-ym-input').eq(0).val(dateArray[0]);
                }
                
                if (dateArray[1] && options.type == 'datetime') {
                    $("#" + this.elemID).find('.cd-date-picker-time-input').eq(0).val(dateArray[1]);
                }
            }
            this.done();
        }
        
        tool(btn, type, mainIndex) {
            let self = this;
            let options = this._config;
            let dateTime = options.dateTime;
            let isStatic = options.position === 'static';
            let active = {
                datetime() {
                    if ($(btn).hasClass(DISABLED)) return;
                    self.list('time', 0, mainIndex);
                    options.range && self.list('time', 1, mainIndex);
                    $(btn).attr('btn-type', 'date').html(self.lang().dateTips);
                },
                date() {
                    self.closeList();
                    $(btn).attr('btn-type', 'datetime').html(self.lang().timeTips);
                },
                clear() {
                    self.setValue('');
                    if (options.isClosePicker) {
                        self.remove();
                    }
                    if (isStatic) {
                        $.extend(dateTime, self.firstDate);
                        self.calendar();
                    }
                    if (options.range) {
                        delete self.startState;
                        delete self.endState;
                        delete self.endDate;
                        delete self.startTime;
                        delete self.endTime;
                    }
                    self.done(['', {}, {}]);
                },
                now() {
                    let thisDate = new Date();
                    $.extend(dateTime, self.systemDate(), {
                        hours: thisDate.getHours(),
                        minutes: thisDate.getMinutes(),
                        seconds: thisDate.getSeconds()
                    });
                    self.setValue(self.parse());
                    if (options.isClosePicker) {
                        self.remove();
                    }
                    isStatic && self.calendar();
                    self.done();
                },
                confirm() {
                    if (options.range) {
                        if (!self.endDate) {
                            return self.hint('请先选择日期范围');
                        }
                        if ($(btn).hasClass(DISABLED)) {
                            return self.hint(
                                options.type === 'time' ? TIPS_OUT.replace(/日期/g, '时间') : TIPS_OUT
                            );
                        }
                    } else {
                        if ($(btn).hasClass(DISABLED)) {
                            return self.hint('不在有效日期或时间范围内');
                        }
                    }
                    self.done();
                    self.confirm();
                    if (self.limitRangeLegal) {
                        self.setValue(self.parse());
                    }
                    if (options.isClosePicker && self.limitRangeLegal) {
                        self.remove();
                    }
                }
            };
            active[type] && active[type]();
        }
        
        change(index) {
            let self = this;
            let options = this._config;
            let dateTime = options.dateTime;
            let isAlone = options.range && (options.type === 'year' || options.type === 'month');
            let elemCont = this.elemCont[index || 0];
            let listYM = this.listYM[index];
            let addSubYeay = (type) => {
                let startEnd = ['startDate', 'endDate'][index];
                let isYear = $(elemCont).find('.cd-date-picker-year-list')[0];
                let isMonth = $(elemCont).find('.cd-date-picker-month-list')[0];
                if (isYear) {
                    listYM[0] = type ? listYM[0] - 15 : listYM[0] + 15;
                    this.list('year', index);
                }
                if (isMonth) { 
                    type ? listYM[0]-- : listYM[0]++;
                    this.list('month', index);
                }
                if (isYear || isMonth) {
                    $.extend(dateTime, {
                        year: listYM[0]
                    });
                    if (isAlone) {
                        this[startEnd].year = listYM[0];
                    }
                    options.range || this.done(null, 'change');
                    this.setBtnStatus();
                    options.range || this.limit($(this.footer).find(ELEM_CONFIRM), {
                        year: listYM[0]
                    });
                }
                return isYear || isMonth;
            };
            return {
                prevYear() {
                    if (addSubYeay('sub')) {
                        return;
                    }
                    dateTime.year--;
                    self.checkDate('limit').calendar();
                    options.range || self.done(null, 'change');
                },
                prevMonth() {
                    let YM = self.getAsYM(dateTime.year, dateTime.month, 'sub');
                    $.extend(dateTime, {
                        year: YM[0],
                        month: YM[1]
                    });
                    self.checkDate('limit').calendar();
                    options.range || self.done(null, 'change');
                },
                nextMonth() {
                    let YM = self.getAsYM(dateTime.year, dateTime.month);
                    $.extend(dateTime, {
                        year: YM[0],
                        month: YM[1]
                    });
                    self.checkDate('limit').calendar();
                    options.range || self.done(null, 'change');
                },
                nextYear: function () {
                    if (addSubYeay()) {
                        return;
                    }
                    dateTime.year++;
                    self.checkDate('limit').calendar();
                    options.range || self.done(null, 'change');
                }
            };
        }
        
        changeEvent() {
            $(this.elem).on('click', (evt) => {
                let $Node = $(evt.target).closest("#" + this.elemID);
                if ($(evt.target).hasClass('J_confirm') || $(evt.target).hasClass('J_clear')) {
                    if ($(evt.target).hasClass('J_clear')) {
                        this.resetDateTimeOrStartTime();
                    } else {
                        this.setDateTimeOrStartTime();
                    }
                    let type = $(evt.target).attr('date-type') || 'date';
                    this.tool($Node.find('.cd-date-picker-time-input'), type);
                    return false;
                }
                if ($(evt.target).hasClass('.cd-date-picker-time-input') || $(evt.target).parents('.cd-date-picker-time-list').length) {
                } else {
                    let type = $(evt.target).attr('date-type') || 'date';
                    if ($Node.length == 0) {
                        $Node = this.elem;
                    }
                    this.tool($Node.find('.cd-date-picker-time-input'), type);
                }
                return false;
            });
            $.each(this.elemHeader, (i, header) => {
                
                $(header[0]).on('click', () => {
                    this.change(i).prevYear();
                });
                
                $(header[1]).on('click', () => {
                    this.change(i).prevMonth();
                });
                
                $(header[2]).find('span').on('click', (evt) => {
                    let $Node = $(evt.target);
                    let DatePickerYM = $Node.attr('date-ym');
                    let DatePickerType = $Node.attr('date-type');
                    if (!DatePickerYM) {
                        return;
                    }
                    DatePickerYM = DatePickerYM.split('-');
                    this.listYM[i] = [DatePickerYM[0] | 0, DatePickerYM[1] | 0];
                    this.list(DatePickerType, i);
                    $(this.footer).find(ELEM_TIME_BTN).addClass(DISABLED);
                });
                
                $(header[3]).on('click', () => {
                    this.change(i).nextMonth();
                });
                
                $(header[4]).on('click', () => {
                    this.change(i).nextYear();
                });
            });
            
            $.each(this.table, (i, table) => {
                let tds = $(table).find('td');
                tds.on('click', (evt) => {
                    this.choose($(evt.target).closest('td'));
                    this.autoSetDate();
                });
            });
            $(this.footer).find('span').on('click', (evt) => {
                let $Node = $(evt.target);
                let type = $Node.attr('btn-type');
                this.tool(evt.target, type);
            });
            $('.cd-date-picker-time-input').on('click', (evt) => {
                let $Node = $(evt.target);
                let type = $Node.attr('btn-type');
                this.tool(evt.target, type, parseInt($Node.parents(".cd-date-picker-main").attr("mainIndex"), 10));
                return false;
            });
        }
        isInput(elem) {
            return /input|textarea/.test(elem.tagName.toLocaleLowerCase());
        }
        
        close() {
            if (this.elem) {
                this.elem.remove();
            }
        }
        
        events() {
            let self = this;
            let options = self._config;
            let showEvent = function (elem, bind) {
                elem.on(options.trigger, function () {
                    bind && (self.bindElem = this);
                    self.render();
                });
            };
            if (!options.elem[0] || options.elem[0].eventHandler) return;
            showEvent(options.elem, 'bind');
            showEvent(options.eventElem);
            $('body').on('click', (evt) => {
                if (evt.target === options.elem[0]
                    || evt.target === options.eventElem[0]
                    || evt.target === $(options.closeStop)[0]) {
                    return;
                }
                if (this.elem) {
                    this.elem.remove();
                }
            }).on('keydown', (evt) => {
                if (evt.keyCode === 13) {
                    if ($('#' + self.elemID)[0] && self.elemID === self.thisElem) {
                        evt.preventDefault();
                        $(self.footer).find(ELEM_CONFIRM)[0].click();
                    }
                }
            });
            
            $(window).on('resize', function () {
                if (!self.elem || !$(ELEM)[0]) {
                    return false;
                }
                self.position();
            });
            
            $(window).on('scroll', function () {
                if (!self.elem || !$(ELEM)[0]) {
                    return false;
                }
                self.close();
            });
            
            $('.J_content-middle').on('scroll', () => {
                if (!self.elem || !$(ELEM)[0]) {
                    return false;
                }
                self.position();
            });
            options.elem[0].eventHandler = true;
        }
        
        digit(num, length, end) {
            let str = '';
            num = String(num);
            length = length || 2;
            for (let i = num.length; i < length; i++) {
                str += '0';
            }
            return num < Math.pow(10, length) ? str + (num | 0) : num;
        }
        
        getEndDate(month, year) {
            let nowDate = new Date();
            nowDate.setFullYear(year || nowDate.getFullYear(), month || (nowDate.getMonth() + 1), 1);
            return new Date(nowDate.getTime() - 1000 * 60 * 60 * 24).getDate();
        }
        
        _getConfig(config) {
            return $.extend({}, DefaultConfig, config);
        }
        
        dispose() {
        }
    }
    return DatePicker;
})($);
export default DatePicker;