var dateInputBinding = new Shiny.InputBinding(); $.extend(dateInputBinding, { _newDate: function(date) { if (date instanceof Date) return date; var d = Date.parse(date); if (isNaN(d)) return null; return new Date(d); }, _setMin: function(el, date) { if (date == null) { $(el).bsDatepicker('setStartDate', null); return; } var parsedDate = this._newDate(date); if (parsedDate === null) return; var curValue = $(el).bsDatepicker('getUTCDate'); $(el).bsDatepicker('setStartDate', this._utcDateAsLocal(parsedDate)); if (parsedDate && curValue && parsedDate.getTime() > curValue.getTime()) { $(el).bsDatepicker('clearDates'); } else { $(el).bsDatepicker('setUTCDate', curValue); } }, _setMax: function(el, date) { if (date == null) { $(el).bsDatepicker('setEndDate', null); return; } var parsedDate = this._newDate(date); if (parsedDate === null) return; var curValue = $(el).bsDatepicker('getUTCDate'); $(el).bsDatepicker('setEndDate', this._utcDateAsLocal(parsedDate)); if (parsedDate && curValue && parsedDate.getTime() < curValue.getTime()) { $(el).bsDatepicker('clearDates'); } else { $(el).bsDatepicker('setUTCDate', curValue); } }, _dateAsUTC: function(date) { return new Date(date.getTime() - date.getTimezoneOffset() * 6e4); }, _utcDateAsLocal: function(date) { return new Date(date.getTime() + date.getTimezoneOffset() * 6e4); }, _padZeros: function(n, digits) { var str = n.toString(); while (str.length < digits) { str = "0" + str; } return str; }, _formatDateUTC: function(date) { if (date instanceof Date) { return date.getUTCFullYear() + "-" + this._padZeros(date.getUTCMonth() + 1, 2) + "-" + this._padZeros(date.getUTCDate(), 2); } else { return null; } }, find: function(scope) { return $(scope).find('.shiny-betterDate-input'); }, getId: function(el) { return $(el).attr('id'); }, getValue: function(el) { var $el = $(el); var $input = $el.find('input').eq(0); var value = null; if ($input.data('date-multidate') == true) { value = $input.bsDatepicker('getUTCDates'); if (value.length > 0) { value = value.map(this._formatDateUTC.bind(this)); } } else { value = $input.bsDatepicker('getUTCDate'); value = this._formatDateUTC(value); } return value; }, setValue: function(el, value) { var $el = $(el); var $input = $el.find('input').eq(0); if (value !== void 0) { if (value === null || (Array.isArray(value) && value.length == 0)) { $input.val('').bsDatepicker('update'); } else { if ($input.data('date-multidate') == true && Array.isArray(value) && value.length > 1) { var newValues = value.map(this._newDate.bind(this)); $input.bsDatepicker('setUTCDates', newValues); } else { var newValue = this._newDate(value); $input.bsDatepicker('setUTCDate', newValue); } } } }, setView: function(el, view) { var $el = $(el); var $input = $el.find('input').eq(0); if (view !== void 0) { if (view === null || (Array.isArray(view) && view.length == 0)) { $input.data().datepicker.o.defaultViewDate = new Date(); } else { $input.data().datepicker.o.defaultViewDate = new Date(view); } $input.bsDatepicker('update'); } }, subscribe: function(el, callback) { $(el).on('change', function(event) { callback(); }); }, unsubscribe: function(el) { $(el).off('change'); }, getState: function(el) { return { value: this.getValue(el) }; }, receiveMessage: function(el, data) { var $el = $(el); var $input = $el.find('input').eq(0); if (data.hasOwnProperty('label')) { $(el).find('label[for="' + el.id + 'Text"]').text(data.label); } if (data.hasOwnProperty('min')) { this._setMin($input[0], data.min); } if (data.hasOwnProperty('max')) { this._setMax($input[0], data.max); } if (data.hasOwnProperty('value')) { this.setValue(el, data.value); } if (data.hasOwnProperty('view')) { this.setView(el, data.view); } // if (data.hasOwnProperty('disabled')) { // } $el.trigger('change'); }, initialize: function initialize(el) { var $el = $(el); var $mainInput = $el.find('.input-betterDate').eq(0); var $input = $el.find('input').eq(0) $input.bsDatepicker(); var value = $mainInput.data('initial-date'); this.setValue(el, value); this._setMin($input[0], $mainInput.data('min-date')); this._setMax($input[0], $mainInput.data('max-date')); $input.bsDatepicker().on('changeDate', function(event) { $el.trigger('change'); }) } }); Shiny.inputBindings.register(dateInputBinding, 'shinyExtra.dateInputBinding');