From 4774a7a64eb21c1b40252f4aa747023c27721cd7 Mon Sep 17 00:00:00 2001 From: Rashid Khan Date: Tue, 23 Jul 2013 12:44:09 -0700 Subject: [PATCH] updated date picker js --- common/lib/datepicker.js | 398 ++++++++++++++++++++++++++++++--------- 1 file changed, 304 insertions(+), 94 deletions(-) diff --git a/common/lib/datepicker.js b/common/lib/datepicker.js index 6c97ff522c2..41e07ba81a3 100644 --- a/common/lib/datepicker.js +++ b/common/lib/datepicker.js @@ -35,46 +35,38 @@ this.element = $(element); this.language = options.language||this.element.data('date-language')||"en"; + this.language = this.language in dates ? this.language : this.language.split('-')[0]; //Check if "de-DE" style date is available, if not language should fallback to 2 letter code eg "de" this.language = this.language in dates ? this.language : "en"; - this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy'); - this.picker = $(DPGlobal.template) - .appendTo('body') - .on({ - click: $.proxy(this.click, this) - }); + this.isRTL = dates[this.language].rtl||false; + this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||dates[this.language].format||'mm/dd/yyyy'); + this.isInline = false; this.isInput = this.element.is('input'); - this.component = this.element.is('.date') ? this.element.find('.add-on') : false; + this.component = this.element.is('.date') ? this.element.find('.add-on, .btn') : false; this.hasInput = this.component && this.element.find('input').length; if(this.component && this.component.length === 0) this.component = false; - if (this.isInput) { - this.element.on({ - focus: $.proxy(this.show, this), - keyup: $.proxy(this.update, this), - keydown: $.proxy(this.keydown, this) - }); - } else { - if (this.component && this.hasInput){ - // For components that are not readonly, allow keyboard nav - this.element.find('input').on({ - focus: $.proxy(this.show, this), - keyup: $.proxy(this.update, this), - keydown: $.proxy(this.keydown, this) - }); - - this.component.on('click', $.proxy(this.show, this)); - } else { - this.element.on('click', $.proxy(this.show, this)); - } + this.forceParse = true; + if ('forceParse' in options) { + this.forceParse = options.forceParse; + } else if ('dateForceParse' in this.element.data()) { + this.forceParse = this.element.data('date-force-parse'); } - $(document).on('mousedown', function (e) { - // Clicked outside the datepicker, hide it - if ($(e.target).closest('.datepicker').length == 0) { - that.hide(); - } - }); + this.picker = $(DPGlobal.template); + this._buildEvents(); + this._attachEvents(); + + if(this.isInline) { + this.picker.addClass('datepicker-inline').appendTo(this.element); + } else { + this.picker.addClass('datepicker-dropdown dropdown-menu'); + } + if (this.isRTL){ + this.picker.addClass('datepicker-rtl'); + this.picker.find('.prev i, .next i') + .toggleClass('icon-arrow-left icon-arrow-right'); + } this.autoclose = false; if ('autoclose' in options) { @@ -90,6 +82,7 @@ this.keyboardNavigation = this.element.data('date-keyboard-navigation'); } + this.viewMode = this.startViewMode = 0; switch(options.startView || this.element.data('date-start-view')){ case 2: case 'decade': @@ -99,39 +92,156 @@ case 'year': this.viewMode = this.startViewMode = 1; break; - case 0: - case 'month': - default: - this.viewMode = this.startViewMode = 0; - break; } + this.minViewMode = options.minViewMode||this.element.data('date-min-view-mode')||0; + if (typeof this.minViewMode === 'string') { + switch (this.minViewMode) { + case 'months': + this.minViewMode = 1; + break; + case 'years': + this.minViewMode = 2; + break; + default: + this.minViewMode = 0; + break; + } + } + + this.viewMode = this.startViewMode = Math.max(this.startViewMode, this.minViewMode); + this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false); this.todayHighlight = (options.todayHighlight||this.element.data('date-today-highlight')||false); + this.calendarWeeks = false; + if ('calendarWeeks' in options) { + this.calendarWeeks = options.calendarWeeks; + } else if ('dateCalendarWeeks' in this.element.data()) { + this.calendarWeeks = this.element.data('date-calendar-weeks'); + } + if (this.calendarWeeks) + this.picker.find('tfoot th.today') + .attr('colspan', function(i, val){ + return parseInt(val) + 1; + }); + + this._allow_update = false; + this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7); this.weekEnd = ((this.weekStart + 6) % 7); this.startDate = -Infinity; this.endDate = Infinity; + this.daysOfWeekDisabled = []; this.setStartDate(options.startDate||this.element.data('date-startdate')); this.setEndDate(options.endDate||this.element.data('date-enddate')); + this.setDaysOfWeekDisabled(options.daysOfWeekDisabled||this.element.data('date-days-of-week-disabled')); this.fillDow(); this.fillMonths(); + + this._allow_update = true; + this.update(); this.showMode(); + + if(this.isInline) { + this.show(); + } }; Datepicker.prototype = { constructor: Datepicker, + _events: [], + _secondaryEvents: [], + _applyEvents: function(evs){ + for (var i=0, el, ev; i this.endDate) { @@ -232,8 +390,13 @@ }, fillDow: function(){ - var dowCnt = this.weekStart; - var html = ''; + var dowCnt = this.weekStart, + html = ''; + if(this.calendarWeeks){ + var cell = ' '; + html += cell; + this.picker.find('.datepicker-days thead tr:first-child').prepend(cell); + } while (dowCnt < this.weekStart + 7) { html += ''+dates[this.language].daysMin[(dowCnt++)%7]+''; } @@ -242,8 +405,8 @@ }, fillMonths: function(){ - var html = ''; - var i = 0 + var html = '', + i = 0; while (i < 12) { html += ''+dates[this.language].monthsShort[i++]+''; } @@ -258,13 +421,13 @@ startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity, endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity, endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity, - currentDate = this.date.valueOf(), + currentDate = this.date && this.date.valueOf(), today = new Date(); - this.picker.find('.datepicker-days thead th:eq(1)') + this.picker.find('.datepicker-days thead th.switch') .text(dates[this.language].months[month]+' '+year); this.picker.find('tfoot th.today') .text(dates[this.language].today) - .toggle(this.todayBtn); + .toggle(this.todayBtn !== false); this.updateNavArrows(); this.fillMonths(); var prevMonth = UTCDate(year, month-1, 28,0,0,0,0), @@ -279,6 +442,21 @@ while(prevMonth.valueOf() < nextMonth) { if (prevMonth.getUTCDay() == this.weekStart) { html.push(''); + if(this.calendarWeeks){ + // ISO 8601: First week contains first thursday. + // ISO also states week starts on Monday, but we can be more abstract here. + var + // Start of current week: based on weekstart/current date + ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5), + // Thursday of this week + th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5), + // First Thursday of year, year from thursday + yth = new Date(+(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5), + // Calendar week: ms between thursdays, div ms per day, div 7 days + calWeek = (th - yth) / 864e5 / 7 + 1; + html.push(''+ calWeek +''); + + } } clsName = ''; if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) { @@ -293,10 +471,11 @@ prevMonth.getUTCDate() == today.getDate()) { clsName += ' today'; } - if (prevMonth.valueOf() == currentDate) { + if (currentDate && prevMonth.valueOf() == currentDate) { clsName += ' active'; } - if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate) { + if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate || + $.inArray(prevMonth.getUTCDay(), this.daysOfWeekDisabled) !== -1) { clsName += ' disabled'; } html.push(''+prevMonth.getUTCDate() + ''); @@ -306,14 +485,14 @@ prevMonth.setUTCDate(prevMonth.getUTCDate()+1); } this.picker.find('.datepicker-days tbody').empty().append(html.join('')); - var currentYear = this.date.getUTCFullYear(); + var currentYear = this.date && this.date.getUTCFullYear(); var months = this.picker.find('.datepicker-months') .find('th:eq(1)') .text(year) .end() .find('span').removeClass('active'); - if (currentYear == year) { + if (currentYear && currentYear == year) { months.eq(this.date.getUTCMonth()).addClass('active'); } if (year < startYear || year > endYear) { @@ -342,6 +521,8 @@ }, updateNavArrows: function() { + if (!this._allow_update) return; + var d = new Date(this.viewDate), year = d.getUTCFullYear(), month = d.getUTCMonth(); @@ -375,7 +556,6 @@ }, click: function(e) { - e.stopPropagation(); e.preventDefault(); var target = $(e.target).closest('span, td, th'); if (target.length == 1) { @@ -401,10 +581,7 @@ break; case 'today': var date = new Date(); - date.setUTCHours(0); - date.setUTCMinutes(0); - date.setUTCSeconds(0); - date.setUTCMilliseconds(0); + date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); this.showMode(-2); var which = this.todayBtn == 'linked' ? null : 'view'; @@ -416,19 +593,29 @@ if (!target.is('.disabled')) { this.viewDate.setUTCDate(1); if (target.is('.month')) { + var day = 1; var month = target.parent().find('span').index(target); + var year = this.viewDate.getUTCFullYear(); this.viewDate.setUTCMonth(month); this.element.trigger({ type: 'changeMonth', date: this.viewDate }); + if ( this.minViewMode == 1 ) { + this._setDate(UTCDate(year, month, day,0,0,0,0)); + } } else { var year = parseInt(target.text(), 10)||0; + var day = 1; + var month = 0; this.viewDate.setUTCFullYear(year); this.element.trigger({ type: 'changeYear', date: this.viewDate }); + if ( this.minViewMode == 2 ) { + this._setDate(UTCDate(year, month, day,0,0,0,0)); + } } this.showMode(-1); this.fill(); @@ -440,7 +627,7 @@ var year = this.viewDate.getUTCFullYear(), month = this.viewDate.getUTCMonth(); if (target.is('.old')) { - if (month == 0) { + if (month === 0) { month = 11; year -= 1; } else { @@ -480,8 +667,8 @@ } if (element) { element.change(); - if (this.autoclose) { - this.hide(); + if (this.autoclose && (!which || which == 'date')) { + this.hide(); } } }, @@ -625,9 +812,19 @@ showMode: function(dir) { if (dir) { - this.viewMode = Math.max(0, Math.min(2, this.viewMode + dir)); + this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir)); } - this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show(); + /* + vitalets: fixing bug of very special conditions: + jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover. + Method show() does not set display css correctly and datepicker is not shown. + Changed to .css('display', 'block') solve the problem. + See https://github.com/vitalets/x-editable/issues/37 + + In jquery 1.7.2+ everything works fine. + */ + //this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show(); + this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block'); this.updateNavArrows(); } }; @@ -660,7 +857,7 @@ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], today: "Today" } - } + }; var DPGlobal = { modes: [ @@ -680,28 +877,28 @@ navStep: 10 }], isLeapYear: function (year) { - return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)) + return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); }, getDaysInMonth: function (year, month) { - return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month] + return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; }, - validParts: /dd?|mm?|MM?|yy(?:yy)?/g, - nonpunctuation: /[^ -\/:-@\[-`{-~\t\n\r]+/g, + validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g, + nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g, parseFormat: function(format){ // IE treats \0 as a string end in inputs (truncating the value), // so it's a bad format delimiter, anyway var separators = format.replace(this.validParts, '\0').split('\0'), parts = format.match(this.validParts); - if (!separators || !separators.length || !parts || parts.length == 0){ + if (!separators || !separators.length || !parts || parts.length === 0){ throw new Error("Invalid date format."); } return {separators: separators, parts: parts}; }, parseDate: function(date, format, language) { if (date instanceof Date) return date; - if (/^[-+]\d+[dmwy]([\s,]+[-+]\d+[dmwy])*$/.test(date)) { - var part_re = /([-+]\d+)([dmwy])/, - parts = date.match(/([-+]\d+)([dmwy])/g), + if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) { + var part_re = /([\-+]\d+)([dmwy])/, + parts = date.match(/([\-+]\d+)([dmwy])/g), part, dir; date = new Date(); for (var i=0; i', footTemplate: '' }; - DPGlobal.template = '