app/assets/javascripts/bootstrap/bootstrap-datepicker.js in bootstrap-rails-engine-1.3.0.3 vs app/assets/javascripts/bootstrap/bootstrap-datepicker.js in bootstrap-rails-engine-1.3.2.0

- old
+ new

@@ -31,125 +31,52 @@ // Picker object var Datepicker = function(element, options) { var that = this; + this._process_options(options); + 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.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.format = DPGlobal.parseFormat(this.o.format); this.isInline = false; this.isInput = this.element.is('input'); 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; + this.picker = $(DPGlobal.template); + this._buildEvents(); this._attachEvents(); - 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'); - } - - - this.picker = $(DPGlobal.template) - .appendTo(this.isInline ? this.element : 'body') - .on({ - click: $.proxy(this.click, this), - mousedown: $.proxy(this.mousedown, this) - }); - if(this.isInline) { - this.picker.addClass('datepicker-inline'); + this.picker.addClass('datepicker-inline').appendTo(this.element); } else { this.picker.addClass('datepicker-dropdown dropdown-menu'); } - if (this.isRTL){ + + if (this.o.rtl){ this.picker.addClass('datepicker-rtl'); this.picker.find('.prev i, .next i') .toggleClass('icon-arrow-left icon-arrow-right'); } - $(document).on('mousedown', function (e) { - // Clicked outside the datepicker, hide it - if ($(e.target).closest('.datepicker.datepicker-inline, .datepicker.datepicker-dropdown').length === 0) { - that.hide(); - } - }); - this.autoclose = false; - if ('autoclose' in options) { - this.autoclose = options.autoclose; - } else if ('dateAutoclose' in this.element.data()) { - this.autoclose = this.element.data('date-autoclose'); - } - this.keyboardNavigation = true; - if ('keyboardNavigation' in options) { - this.keyboardNavigation = options.keyboardNavigation; - } else if ('dateKeyboardNavigation' in this.element.data()) { - this.keyboardNavigation = this.element.data('date-keyboard-navigation'); - } + this.viewMode = this.o.startView; - this.viewMode = this.startViewMode = 0; - switch(options.startView || this.element.data('date-start-view')){ - case 2: - case 'decade': - this.viewMode = this.startViewMode = 2; - break; - case 1: - case 'year': - this.viewMode = this.startViewMode = 1; - 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) + if (this.o.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.setStartDate(this.o.startDate); + this.setEndDate(this.o.endDate); + this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled); + this.fillDow(); this.fillMonths(); this._allow_update = true; @@ -162,13 +89,89 @@ }; Datepicker.prototype = { constructor: Datepicker, + _process_options: function(opts){ + // Store raw options for reference + this._o = $.extend({}, this._o, opts); + // Processed options + var o = this.o = $.extend({}, this._o); + + // Check if "de-DE" style date is available, if not language should + // fallback to 2 letter code eg "de" + var lang = o.language; + if (!dates[lang]) { + lang = lang.split('-')[0]; + if (!dates[lang]) + lang = $.fn.datepicker.defaults.language; + } + o.language = lang; + + switch(o.startView){ + case 2: + case 'decade': + o.startView = 2; + break; + case 1: + case 'year': + o.startView = 1; + break; + default: + o.startView = 0; + } + + switch (o.minViewMode) { + case 1: + case 'months': + o.minViewMode = 1; + break; + case 2: + case 'years': + o.minViewMode = 2; + break; + default: + o.minViewMode = 0; + } + + o.startView = Math.max(o.startView, o.minViewMode); + + o.weekStart %= 7; + o.weekEnd = ((o.weekStart + 6) % 7); + + var format = DPGlobal.parseFormat(o.format) + if (o.startDate !== -Infinity) { + o.startDate = DPGlobal.parseDate(o.startDate, format, o.language); + } + if (o.endDate !== Infinity) { + o.endDate = DPGlobal.parseDate(o.endDate, format, o.language); + } + + o.daysOfWeekDisabled = o.daysOfWeekDisabled||[]; + if (!$.isArray(o.daysOfWeekDisabled)) + o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/); + o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function (d) { + return parseInt(d, 10); + }); + }, _events: [], - _attachEvents: function(){ - this._detachEvents(); + _secondaryEvents: [], + _applyEvents: function(evs){ + for (var i=0, el, ev; i<evs.length; i++){ + el = evs[i][0]; + ev = evs[i][1]; + el.on(ev); + } + }, + _unapplyEvents: function(evs){ + for (var i=0, el, ev; i<evs.length; i++){ + el = evs[i][0]; + ev = evs[i][1]; + el.off(ev); + } + }, + _buildEvents: function(){ if (this.isInput) { // single input this._events = [ [this.element, { focus: $.proxy(this.show, this), keyup: $.proxy(this.update, this), @@ -187,76 +190,109 @@ [this.component, { click: $.proxy(this.show, this) }] ]; } - else if (this.element.is('div')) { // inline datepicker - this.isInline = true; - } + else if (this.element.is('div')) { // inline datepicker + this.isInline = true; + } else { this._events = [ [this.element, { click: $.proxy(this.show, this) }] ]; } - for (var i=0, el, ev; i<this._events.length; i++){ - el = this._events[i][0]; - ev = this._events[i][1]; - el.on(ev); - } + + this._secondaryEvents = [ + [this.picker, { + click: $.proxy(this.click, this) + }], + [$(window), { + resize: $.proxy(this.place, this) + }], + [$(document), { + mousedown: $.proxy(function (e) { + // Clicked outside the datepicker, hide it + if (!( + this.element.is(e.target) || + this.element.find(e.target).size() || + this.picker.is(e.target) || + this.picker.find(e.target).size() + )) { + this.hide(); + } + }, this) + }] + ]; }, + _attachEvents: function(){ + this._detachEvents(); + this._applyEvents(this._events); + }, _detachEvents: function(){ - for (var i=0, el, ev; i<this._events.length; i++){ - el = this._events[i][0]; - ev = this._events[i][1]; - el.off(ev); - } - this._events = []; + this._unapplyEvents(this._events); }, + _attachSecondaryEvents: function(){ + this._detachSecondaryEvents(); + this._applyEvents(this._secondaryEvents); + }, + _detachSecondaryEvents: function(){ + this._unapplyEvents(this._secondaryEvents); + }, + _trigger: function(event, altdate){ + var date = altdate || this.date, + local_date = new Date(date.getTime() + (date.getTimezoneOffset()*60000)); + this.element.trigger({ + type: event, + date: local_date, + format: $.proxy(function(altformat){ + var format = this.format; + if (altformat) + format = DPGlobal.parseFormat(altformat); + return DPGlobal.formatDate(date, format, this.language); + }, this) + }); + }, + show: function(e) { + if (!this.isInline) + this.picker.appendTo('body'); this.picker.show(); this.height = this.component ? this.component.outerHeight() : this.element.outerHeight(); this.place(); - $(window).on('resize', $.proxy(this.place, this)); + this._attachSecondaryEvents(); if (e) { e.preventDefault(); } - this.element.trigger({ - type: 'show', - date: this.date - }); + this._trigger('show'); }, hide: function(e){ if(this.isInline) return; if (!this.picker.is(':visible')) return; - this.picker.hide(); - $(window).off('resize', this.place); - this.viewMode = this.startViewMode; + this.picker.hide().detach(); + this._detachSecondaryEvents(); + this.viewMode = this.o.startView; this.showMode(); - if (!this.isInput) { - $(document).off('mousedown', this.hide); - } if ( - this.forceParse && + this.o.forceParse && ( this.isInput && this.element.val() || this.hasInput && this.element.find('input').val() ) ) this.setValue(); - this.element.trigger({ - type: 'hide', - date: this.date - }); + this._trigger('hide'); }, remove: function() { + this.hide(); this._detachEvents(); + this._detachSecondaryEvents(); this.picker.remove(); delete this.element.data().datepicker; if (!this.isInput) { delete this.element.data().date; } @@ -284,48 +320,35 @@ var formatted = this.getFormattedDate(); if (!this.isInput) { if (this.component){ this.element.find('input').val(formatted); } - this.element.data('date', formatted); } else { this.element.val(formatted); } }, getFormattedDate: function(format) { if (format === undefined) format = this.format; - return DPGlobal.formatDate(this.date, format, this.language); + return DPGlobal.formatDate(this.date, format, this.o.language); }, setStartDate: function(startDate){ - this.startDate = startDate||-Infinity; - if (this.startDate !== -Infinity) { - this.startDate = DPGlobal.parseDate(this.startDate, this.format, this.language); - } + this._process_options({startDate: startDate}); this.update(); this.updateNavArrows(); }, setEndDate: function(endDate){ - this.endDate = endDate||Infinity; - if (this.endDate !== Infinity) { - this.endDate = DPGlobal.parseDate(this.endDate, this.format, this.language); - } + this._process_options({endDate: endDate}); this.update(); this.updateNavArrows(); }, setDaysOfWeekDisabled: function(daysOfWeekDisabled){ - this.daysOfWeekDisabled = daysOfWeekDisabled||[]; - if (!$.isArray(this.daysOfWeekDisabled)) { - this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/); - } - this.daysOfWeekDisabled = $.map(this.daysOfWeekDisabled, function (d) { - return parseInt(d, 10); - }); + this._process_options({daysOfWeekDisabled: daysOfWeekDisabled}); this.update(); this.updateNavArrows(); }, place: function(){ @@ -350,117 +373,163 @@ if(arguments && arguments.length && (typeof arguments[0] === 'string' || arguments[0] instanceof Date)) { date = arguments[0]; fromArgs = true; } else { date = this.isInput ? this.element.val() : this.element.data('date') || this.element.find('input').val(); + delete this.element.data().date; } - this.date = DPGlobal.parseDate(date, this.format, this.language); + this.date = DPGlobal.parseDate(date, this.format, this.o.language); if(fromArgs) this.setValue(); - if (this.date < this.startDate) { - this.viewDate = new Date(this.startDate); - } else if (this.date > this.endDate) { - this.viewDate = new Date(this.endDate); + if (this.date < this.o.startDate) { + this.viewDate = new Date(this.o.startDate); + } else if (this.date > this.o.endDate) { + this.viewDate = new Date(this.o.endDate); } else { this.viewDate = new Date(this.date); } this.fill(); }, fillDow: function(){ - var dowCnt = this.weekStart, + var dowCnt = this.o.weekStart, html = '<tr>'; - if(this.calendarWeeks){ + if(this.o.calendarWeeks){ var cell = '<th class="cw">&nbsp;</th>'; html += cell; this.picker.find('.datepicker-days thead tr:first-child').prepend(cell); } - while (dowCnt < this.weekStart + 7) { - html += '<th class="dow">'+dates[this.language].daysMin[(dowCnt++)%7]+'</th>'; + while (dowCnt < this.o.weekStart + 7) { + html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>'; } html += '</tr>'; this.picker.find('.datepicker-days thead').append(html); }, fillMonths: function(){ var html = '', i = 0; while (i < 12) { - html += '<span class="month">'+dates[this.language].monthsShort[i++]+'</span>'; + html += '<span class="month">'+dates[this.o.language].monthsShort[i++]+'</span>'; } this.picker.find('.datepicker-months td').html(html); }, + setRange: function(range){ + if (!range || !range.length) + delete this.range; + else + this.range = $.map(range, function(d){ return d.valueOf(); }); + this.fill(); + }, + + getClassNames: function(date){ + var cls = [], + year = this.viewDate.getUTCFullYear(), + month = this.viewDate.getUTCMonth(), + currentDate = this.date.valueOf(), + today = new Date(); + if (date.getUTCFullYear() < year || (date.getUTCFullYear() == year && date.getUTCMonth() < month)) { + cls.push('old'); + } else if (date.getUTCFullYear() > year || (date.getUTCFullYear() == year && date.getUTCMonth() > month)) { + cls.push('new'); + } + // Compare internal UTC date with local today, not UTC today + if (this.o.todayHighlight && + date.getUTCFullYear() == today.getFullYear() && + date.getUTCMonth() == today.getMonth() && + date.getUTCDate() == today.getDate()) { + cls.push('today'); + } + if (currentDate && date.valueOf() == currentDate) { + cls.push('active'); + } + if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate || + $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1) { + cls.push('disabled'); + } + if (this.range){ + if (date > this.range[0] && date < this.range[this.range.length-1]){ + cls.push('range'); + } + if ($.inArray(date.valueOf(), this.range) != -1){ + cls.push('selected'); + } + } + return cls; + }, + fill: function() { var d = new Date(this.viewDate), year = d.getUTCFullYear(), month = d.getUTCMonth(), - startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity, - startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity, - endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity, - endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity, + startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity, + startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity, + endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity, + endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity, currentDate = this.date && this.date.valueOf(), - today = new Date(); - this.picker.find('.datepicker-days thead th.switch') - .text(dates[this.language].months[month]+' '+year); + tooltip; + this.picker.find('.datepicker-days thead th.datepicker-switch') + .text(dates[this.o.language].months[month]+' '+year); this.picker.find('tfoot th.today') - .text(dates[this.language].today) - .toggle(this.todayBtn !== false); + .text(dates[this.o.language].today) + .toggle(this.o.todayBtn !== false); + this.picker.find('tfoot th.clear') + .text(dates[this.o.language].clear) + .toggle(this.o.clearBtn !== false); this.updateNavArrows(); this.fillMonths(); var prevMonth = UTCDate(year, month-1, 28,0,0,0,0), day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth()); prevMonth.setUTCDate(day); - prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7); + prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7); var nextMonth = new Date(prevMonth); nextMonth.setUTCDate(nextMonth.getUTCDate() + 42); nextMonth = nextMonth.valueOf(); var html = []; var clsName; while(prevMonth.valueOf() < nextMonth) { - if (prevMonth.getUTCDay() == this.weekStart) { + if (prevMonth.getUTCDay() == this.o.weekStart) { html.push('<tr>'); - if(this.calendarWeeks){ + if(this.o.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), + ws = new Date(+prevMonth + (this.o.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('<td class="cw">'+ calWeek +'</td>'); } } - clsName = ''; - if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) { - clsName += ' old'; - } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) { - clsName += ' new'; - } - // Compare internal UTC date with local today, not UTC today - if (this.todayHighlight && - prevMonth.getUTCFullYear() == today.getFullYear() && - prevMonth.getUTCMonth() == today.getMonth() && - prevMonth.getUTCDate() == today.getDate()) { - clsName += ' today'; - } - if (currentDate && prevMonth.valueOf() == currentDate) { - clsName += ' active'; - } - if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate || - $.inArray(prevMonth.getUTCDay(), this.daysOfWeekDisabled) !== -1) { - clsName += ' disabled'; - } - html.push('<td class="day'+clsName+'">'+prevMonth.getUTCDate() + '</td>'); - if (prevMonth.getUTCDay() == this.weekEnd) { + clsName = this.getClassNames(prevMonth); + clsName.push('day'); + + var before = this.o.beforeShowDay(prevMonth); + if (before === undefined) + before = {}; + else if (typeof(before) === 'boolean') + before = {enabled: before}; + else if (typeof(before) === 'string') + before = {classes: before}; + if (before.enabled === false) + clsName.push('disabled'); + if (before.classes) + clsName = clsName.concat(before.classes.split(/\s+/)); + if (before.tooltip) + tooltip = before.tooltip; + + clsName = $.unique(clsName); + html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + '>'+prevMonth.getUTCDate() + '</td>'); + if (prevMonth.getUTCDay() == this.o.weekEnd) { html.push('</tr>'); } prevMonth.setUTCDate(prevMonth.getUTCDate()+1); } this.picker.find('.datepicker-days tbody').empty().append(html.join('')); @@ -491,11 +560,11 @@ .text(year + '-' + (year + 9)) .end() .find('td'); year -= 1; for (var i = -1; i < 11; i++) { - html += '<span class="year'+(i == -1 || i == 10 ? ' old' : '')+(currentYear == year ? ' active' : '')+(year < startYear || year > endYear ? ' disabled' : '')+'">'+year+'</span>'; + html += '<span class="year'+(i == -1 ? ' old' : i == 10 ? ' new' : '')+(currentYear == year ? ' active' : '')+(year < startYear || year > endYear ? ' disabled' : '')+'">'+year+'</span>'; year += 1; } yearCont.html(html); }, @@ -505,29 +574,29 @@ var d = new Date(this.viewDate), year = d.getUTCFullYear(), month = d.getUTCMonth(); switch (this.viewMode) { case 0: - if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) { + if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()) { this.picker.find('.prev').css({visibility: 'hidden'}); } else { this.picker.find('.prev').css({visibility: 'visible'}); } - if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) { + if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()) { this.picker.find('.next').css({visibility: 'hidden'}); } else { this.picker.find('.next').css({visibility: 'visible'}); } break; case 1: case 2: - if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) { + if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()) { this.picker.find('.prev').css({visibility: 'hidden'}); } else { this.picker.find('.prev').css({visibility: 'visible'}); } - if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) { + if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()) { this.picker.find('.next').css({visibility: 'hidden'}); } else { this.picker.find('.next').css({visibility: 'visible'}); } break; @@ -539,11 +608,11 @@ var target = $(e.target).closest('span, td, th'); if (target.length == 1) { switch(target[0].nodeName.toLowerCase()) { case 'th': switch(target[0].className) { - case 'switch': + case 'datepicker-switch': this.showMode(1); break; case 'prev': case 'next': var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1); @@ -561,40 +630,43 @@ case 'today': var date = new Date(); date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); this.showMode(-2); - var which = this.todayBtn == 'linked' ? null : 'view'; + var which = this.o.todayBtn == 'linked' ? null : 'view'; this._setDate(date, which); break; + case 'clear': + if (this.isInput) + this.element.val(""); + else + this.element.find('input').val(""); + this.update(); + if (this.o.autoclose) + this.hide(); + break; } break; case 'span': 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._trigger('changeMonth', this.viewDate); + if (this.o.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._trigger('changeYear', this.viewDate); + if (this.o.minViewMode === 2) { this._setDate(UTCDate(year, month, day,0,0,0,0)); } } this.showMode(-1); this.fill(); @@ -627,28 +699,25 @@ } }, _setDate: function(date, which){ if (!which || which == 'date') - this.date = date; + this.date = new Date(date); if (!which || which == 'view') - this.viewDate = date; + this.viewDate = new Date(date); this.fill(); this.setValue(); - this.element.trigger({ - type: 'changeDate', - date: this.date - }); + this._trigger('changeDate'); var element; if (this.isInput) { element = this.element; } else if (this.component){ element = this.element.find('input'); } if (element) { element.change(); - if (this.autoclose && (!which || which == 'date')) { + if (this.o.autoclose && (!which || which == 'date')) { this.hide(); } } }, @@ -695,11 +764,11 @@ moveYear: function(date, dir){ return this.moveMonth(date, dir*12); }, dateWithinRange: function(date){ - return date >= this.startDate && date <= this.endDate; + return date >= this.o.startDate && date <= this.o.endDate; }, keydown: function(e){ if (this.picker.is(':not(:visible)')){ if (e.keyCode == 27) // allow escape to hide and re-show picker @@ -714,11 +783,11 @@ this.hide(); e.preventDefault(); break; case 37: // left case 39: // right - if (!this.keyboardNavigation) break; + if (!this.o.keyboardNavigation) break; dir = e.keyCode == 37 ? -1 : 1; if (e.ctrlKey){ newDate = this.moveYear(this.date, dir); newViewDate = this.moveYear(this.viewDate, dir); } else if (e.shiftKey){ @@ -739,11 +808,11 @@ dateChanged = true; } break; case 38: // up case 40: // down - if (!this.keyboardNavigation) break; + if (!this.o.keyboardNavigation) break; dir = e.keyCode == 38 ? -1 : 1; if (e.ctrlKey){ newDate = this.moveYear(this.date, dir); newViewDate = this.moveYear(this.viewDate, dir); } else if (e.shiftKey){ @@ -771,14 +840,11 @@ case 9: // tab this.hide(); break; } if (dateChanged){ - this.element.trigger({ - type: 'changeDate', - date: this.date - }); + this._trigger('changeDate'); var element; if (this.isInput) { element = this.element; } else if (this.component){ element = this.element.find('input'); @@ -789,11 +855,11 @@ } }, showMode: function(dir) { if (dir) { - this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir)); + this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir)); } /* 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. @@ -806,37 +872,165 @@ this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block'); this.updateNavArrows(); } }; + var DateRangePicker = function(element, options){ + this.element = $(element); + this.inputs = $.map(options.inputs, function(i){ return i.jquery ? i[0] : i; }); + delete options.inputs; + + $(this.inputs) + .datepicker(options) + .bind('changeDate', $.proxy(this.dateUpdated, this)); + + this.pickers = $.map(this.inputs, function(i){ return $(i).data('datepicker'); }); + this.updateDates(); + }; + DateRangePicker.prototype = { + updateDates: function(){ + this.dates = $.map(this.pickers, function(i){ return i.date; }); + this.updateRanges(); + }, + updateRanges: function(){ + var range = $.map(this.dates, function(d){ return d.valueOf(); }); + $.each(this.pickers, function(i, p){ + p.setRange(range); + }); + }, + dateUpdated: function(e){ + var dp = $(e.target).data('datepicker'), + new_date = dp.getUTCDate(), + i = $.inArray(e.target, this.inputs), + l = this.inputs.length; + if (i == -1) return; + + if (new_date < this.dates[i]){ + // Date being moved earlier/left + while (i>=0 && new_date < this.dates[i]){ + this.pickers[i--].setUTCDate(new_date); + } + } + else if (new_date > this.dates[i]){ + // Date being moved later/right + while (i<l && new_date > this.dates[i]){ + this.pickers[i++].setUTCDate(new_date); + } + } + this.updateDates(); + }, + remove: function(){ + $.map(this.pickers, function(p){ p.remove(); }); + delete this.element.data().datepicker; + } + }; + + function opts_from_el(el, prefix){ + // Derive options from element data-attrs + var data = $(el).data(), + out = {}, inkey, + replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'), + prefix = new RegExp('^' + prefix.toLowerCase()); + for (var key in data) + if (prefix.test(key)){ + inkey = key.replace(replace, function(_,a){ return a.toLowerCase(); }); + out[inkey] = data[key]; + } + return out; + } + + function opts_from_locale(lang){ + // Derive options from locale plugins + var out = {}; + // Check if "de-DE" style date is available, if not language should + // fallback to 2 letter code eg "de" + if (!dates[lang]) { + lang = lang.split('-')[0] + if (!dates[lang]) + return; + } + var d = dates[lang]; + $.each($.fn.datepicker.locale_opts, function(i,k){ + if (k in d) + out[k] = d[k]; + }); + return out; + } + + var old = $.fn.datepicker; $.fn.datepicker = function ( option ) { var args = Array.apply(null, arguments); args.shift(); - return this.each(function () { + var internal_return, + this_return; + this.each(function () { var $this = $(this), data = $this.data('datepicker'), options = typeof option == 'object' && option; if (!data) { - $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options)))); + var elopts = opts_from_el(this, 'date'), + // Preliminary otions + xopts = $.extend({}, $.fn.datepicker.defaults, elopts, options), + locopts = opts_from_locale(xopts.language), + // Options priority: js args, data-attrs, locales, defaults + opts = $.extend({}, $.fn.datepicker.defaults, locopts, elopts, options); + if ($this.is('.input-daterange') || opts.inputs){ + var ropts = { + inputs: opts.inputs || $this.find('input').toArray() + }; + $this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts)))); + } + else{ + $this.data('datepicker', (data = new Datepicker(this, opts))); + } } if (typeof option == 'string' && typeof data[option] == 'function') { - data[option].apply(data, args); + internal_return = data[option].apply(data, args); + if (internal_return !== undefined) + return false; } }); + if (internal_return !== undefined) + return internal_return; + else + return this; }; $.fn.datepicker.defaults = { + autoclose: false, + beforeShowDay: $.noop, + calendarWeeks: false, + clearBtn: false, + daysOfWeekDisabled: [], + endDate: Infinity, + forceParse: true, + format: 'mm/dd/yyyy', + keyboardNavigation: true, + language: 'en', + minViewMode: 0, + rtl: false, + startDate: -Infinity, + startView: 0, + todayBtn: false, + todayHighlight: false, + weekStart: 0 }; + $.fn.datepicker.locale_opts = [ + 'format', + 'rtl', + 'weekStart' + ]; $.fn.datepicker.Constructor = Datepicker; var dates = $.fn.datepicker.dates = { en: { days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], - today: "Today" + today: "Today", + clear: "Clear" } }; var DPGlobal = { modes: [ @@ -977,26 +1171,26 @@ }; val.dd = (val.d < 10 ? '0' : '') + val.d; val.mm = (val.m < 10 ? '0' : '') + val.m; var date = [], seps = $.extend([], format.separators); - for (var i=0, cnt = format.parts.length; i < cnt; i++) { + for (var i=0, cnt = format.parts.length; i <= cnt; i++) { if (seps.length) date.push(seps.shift()); date.push(val[format.parts[i]]); } return date.join(''); }, headTemplate: '<thead>'+ '<tr>'+ '<th class="prev"><i class="icon-arrow-left"/></th>'+ - '<th colspan="5" class="switch"></th>'+ + '<th colspan="5" class="datepicker-switch"></th>'+ '<th class="next"><i class="icon-arrow-right"/></th>'+ '</tr>'+ '</thead>', contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>', - footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr></tfoot>' + footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr><tr><th colspan="7" class="clear"></th></tr></tfoot>' }; DPGlobal.template = '<div class="datepicker">'+ '<div class="datepicker-days">'+ '<table class=" table-condensed">'+ DPGlobal.headTemplate+ @@ -1019,7 +1213,35 @@ '</table>'+ '</div>'+ '</div>'; $.fn.datepicker.DPGlobal = DPGlobal; + + + /* DATEPICKER NO CONFLICT + * =================== */ + + $.fn.datepicker.noConflict = function(){ + $.fn.datepicker = old; + return this; + }; + + + /* DATEPICKER DATA-API + * ================== */ + + $(document).on( + 'focus.datepicker.data-api click.datepicker.data-api', + '[data-provide="datepicker"]', + function(e){ + var $this = $(this); + if ($this.data('datepicker')) return; + e.preventDefault(); + // component click requires us to explicitly show it + $this.datepicker('show'); + } + ); + $(function(){ + $('[data-provide="datepicker-inline"]').datepicker(); + }); }( window.jQuery );