diff --git a/CHANGELOG.txt b/CHANGELOG.txt index a0894b5..ba17ca4 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -3,6 +3,7 @@ X-editable changelog Version 1.4.5 wip ---------------------------- +[enh] update bootstrap-datepicker to 1.1.2 (vitalets) [enh] allow follow links in disabled state (vitalets) [enh] update combodate to 1.0.4, fix #222 (vitalets) diff --git a/src/inputs/date/bootstrap-datepicker/css/datepicker.css b/src/inputs/date/bootstrap-datepicker/css/datepicker.css index 5ecec5d..6b7ff18 100644 --- a/src/inputs/date/bootstrap-datepicker/css/datepicker.css +++ b/src/inputs/date/bootstrap-datepicker/css/datepicker.css @@ -111,7 +111,7 @@ border-color: #fdf59a #fdf59a #fbed50; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); - color: #000 !important; + color: #000; } .datepicker table tr td.today:hover, .datepicker table tr td.today:hover:hover, @@ -145,6 +145,124 @@ .datepicker table tr td.today.disabled:hover.active { background-color: #fbf069 \9; } +.datepicker table tr td.today:hover:hover { + color: #000; +} +.datepicker table tr td.today.active:hover { + color: #fff; +} +.datepicker table tr td.range, +.datepicker table tr td.range:hover, +.datepicker table tr td.range.disabled, +.datepicker table tr td.range.disabled:hover { + background: #eeeeee; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.datepicker table tr td.range.today, +.datepicker table tr td.range.today:hover, +.datepicker table tr td.range.today.disabled, +.datepicker table tr td.range.today.disabled:hover { + background-color: #f3d17a; + background-image: -moz-linear-gradient(top, #f3c17a, #f3e97a); + background-image: -ms-linear-gradient(top, #f3c17a, #f3e97a); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a)); + background-image: -webkit-linear-gradient(top, #f3c17a, #f3e97a); + background-image: -o-linear-gradient(top, #f3c17a, #f3e97a); + background-image: linear-gradient(top, #f3c17a, #f3e97a); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0); + border-color: #f3e97a #f3e97a #edde34; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.datepicker table tr td.range.today:hover, +.datepicker table tr td.range.today:hover:hover, +.datepicker table tr td.range.today.disabled:hover, +.datepicker table tr td.range.today.disabled:hover:hover, +.datepicker table tr td.range.today:active, +.datepicker table tr td.range.today:hover:active, +.datepicker table tr td.range.today.disabled:active, +.datepicker table tr td.range.today.disabled:hover:active, +.datepicker table tr td.range.today.active, +.datepicker table tr td.range.today:hover.active, +.datepicker table tr td.range.today.disabled.active, +.datepicker table tr td.range.today.disabled:hover.active, +.datepicker table tr td.range.today.disabled, +.datepicker table tr td.range.today:hover.disabled, +.datepicker table tr td.range.today.disabled.disabled, +.datepicker table tr td.range.today.disabled:hover.disabled, +.datepicker table tr td.range.today[disabled], +.datepicker table tr td.range.today:hover[disabled], +.datepicker table tr td.range.today.disabled[disabled], +.datepicker table tr td.range.today.disabled:hover[disabled] { + background-color: #f3e97a; +} +.datepicker table tr td.range.today:active, +.datepicker table tr td.range.today:hover:active, +.datepicker table tr td.range.today.disabled:active, +.datepicker table tr td.range.today.disabled:hover:active, +.datepicker table tr td.range.today.active, +.datepicker table tr td.range.today:hover.active, +.datepicker table tr td.range.today.disabled.active, +.datepicker table tr td.range.today.disabled:hover.active { + background-color: #efe24b \9; +} +.datepicker table tr td.selected, +.datepicker table tr td.selected:hover, +.datepicker table tr td.selected.disabled, +.datepicker table tr td.selected.disabled:hover { + background-color: #9e9e9e; + background-image: -moz-linear-gradient(top, #b3b3b3, #808080); + background-image: -ms-linear-gradient(top, #b3b3b3, #808080); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080)); + background-image: -webkit-linear-gradient(top, #b3b3b3, #808080); + background-image: -o-linear-gradient(top, #b3b3b3, #808080); + background-image: linear-gradient(top, #b3b3b3, #808080); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0); + border-color: #808080 #808080 #595959; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.datepicker table tr td.selected:hover, +.datepicker table tr td.selected:hover:hover, +.datepicker table tr td.selected.disabled:hover, +.datepicker table tr td.selected.disabled:hover:hover, +.datepicker table tr td.selected:active, +.datepicker table tr td.selected:hover:active, +.datepicker table tr td.selected.disabled:active, +.datepicker table tr td.selected.disabled:hover:active, +.datepicker table tr td.selected.active, +.datepicker table tr td.selected:hover.active, +.datepicker table tr td.selected.disabled.active, +.datepicker table tr td.selected.disabled:hover.active, +.datepicker table tr td.selected.disabled, +.datepicker table tr td.selected:hover.disabled, +.datepicker table tr td.selected.disabled.disabled, +.datepicker table tr td.selected.disabled:hover.disabled, +.datepicker table tr td.selected[disabled], +.datepicker table tr td.selected:hover[disabled], +.datepicker table tr td.selected.disabled[disabled], +.datepicker table tr td.selected.disabled:hover[disabled] { + background-color: #808080; +} +.datepicker table tr td.selected:active, +.datepicker table tr td.selected:hover:active, +.datepicker table tr td.selected.disabled:active, +.datepicker table tr td.selected.disabled:hover:active, +.datepicker table tr td.selected.active, +.datepicker table tr td.selected:hover.active, +.datepicker table tr td.selected.disabled.active, +.datepicker table tr td.selected.disabled:hover.active { + background-color: #666666 \9; +} .datepicker table tr td.active, .datepicker table tr td.active:hover, .datepicker table tr td.active.disabled, @@ -268,18 +386,19 @@ .datepicker table tr td span.active.disabled:hover.active { background-color: #003399 \9; } -.datepicker table tr td span.old { +.datepicker table tr td span.old, +.datepicker table tr td span.new { color: #999999; } -.datepicker th.switch { +.datepicker th.datepicker-switch { width: 145px; } .datepicker thead tr:first-child th, -.datepicker tfoot tr:first-child th { +.datepicker tfoot tr th { cursor: pointer; } .datepicker thead tr:first-child th:hover, -.datepicker tfoot tr:first-child th:hover { +.datepicker tfoot tr th:hover { background: #eeeeee; } .datepicker .cw { @@ -299,3 +418,32 @@ width: 16px; height: 16px; } +.input-daterange input { + text-align: center; +} +.input-daterange input:first-child { + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} +.input-daterange input:last-child { + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} +.input-daterange .add-on { + display: inline-block; + width: auto; + min-width: 16px; + height: 18px; + padding: 4px 5px; + font-weight: normal; + line-height: 18px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + vertical-align: middle; + background-color: #eeeeee; + border: 1px solid #ccc; + margin-left: -5px; + margin-right: -5px; +} diff --git a/src/inputs/date/bootstrap-datepicker/js/bootstrap-datepicker.js b/src/inputs/date/bootstrap-datepicker/js/bootstrap-datepicker.js index 8991aa8..be641bf 100644 --- a/src/inputs/date/bootstrap-datepicker/js/bootstrap-datepicker.js +++ b/src/inputs/date/bootstrap-datepicker/js/bootstrap-datepicker.js @@ -33,12 +33,10 @@ 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; @@ -46,13 +44,6 @@ if(this.component && this.component.length === 0) this.component = false; - 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); this._buildEvents(); this._attachEvents(); @@ -62,65 +53,17 @@ } 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'); } - 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; @@ -128,14 +71,10 @@ 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(); @@ -152,6 +91,68 @@ 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: [], _secondaryEvents: [], _applyEvents: function(evs){ @@ -212,7 +213,12 @@ [$(document), { mousedown: $.proxy(function (e) { // Clicked outside the datepicker, hide it - if ($(e.target).closest('.datepicker.datepicker-inline, .datepicker.datepicker-dropdown').length === 0) { + 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) @@ -233,6 +239,21 @@ _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) @@ -244,10 +265,7 @@ if (e) { e.preventDefault(); } - this.element.trigger({ - type: 'show', - date: this.date - }); + this._trigger('show'); }, hide: function(e){ @@ -255,21 +273,18 @@ if (!this.picker.is(':visible')) return; this.picker.hide().detach(); this._detachSecondaryEvents(); - this.viewMode = this.startViewMode; + this.viewMode = this.o.startView; this.showMode(); 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() { @@ -307,7 +322,6 @@ if (this.component){ this.element.find('input').val(formatted); } - this.element.data('date', formatted); } else { this.element.val(formatted); } @@ -316,35 +330,23 @@ 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(); }, @@ -373,16 +375,17 @@ 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); } @@ -390,15 +393,15 @@ }, 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"> </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); @@ -408,46 +411,93 @@ 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 @@ -458,28 +508,26 @@ } } - 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); @@ -514,7 +562,7 @@ .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); @@ -528,12 +576,12 @@ 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'}); @@ -541,12 +589,12 @@ 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'}); @@ -562,7 +610,7 @@ switch(target[0].nodeName.toLowerCase()) { case 'th': switch(target[0].className) { - case 'switch': + case 'datepicker-switch': this.showMode(1); break; case 'prev': @@ -584,9 +632,18 @@ 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': @@ -597,11 +654,8 @@ 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 { @@ -609,11 +663,8 @@ 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)); } } @@ -650,15 +701,12 @@ _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; @@ -667,7 +715,7 @@ } if (element) { element.change(); - if (this.autoclose && (!which || which == 'date')) { + if (this.o.autoclose && (!which || which == 'date')) { this.hide(); } } @@ -718,7 +766,7 @@ }, dateWithinRange: function(date){ - return date >= this.startDate && date <= this.endDate; + return date >= this.o.startDate && date <= this.o.endDate; }, keydown: function(e){ @@ -737,7 +785,7 @@ 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); @@ -762,7 +810,7 @@ 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); @@ -794,10 +842,7 @@ break; } if (dateChanged){ - this.element.trigger({ - type: 'changeDate', - date: this.date - }); + this._trigger('changeDate'); var element; if (this.isInput) { element = this.element; @@ -812,7 +857,7 @@ 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: @@ -829,24 +874,151 @@ } }; + 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: { @@ -855,7 +1027,8 @@ 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" } }; @@ -1000,7 +1173,7 @@ 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]]); @@ -1010,12 +1183,12 @@ 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">'+ @@ -1043,4 +1216,32 @@ $.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 );