UNPKG

samagra-x-enketo-core

Version:

Extensible Enketo form engine

183 lines (159 loc) 6.65 kB
import 'openrosa-xpath-evaluator/src/date-extensions'; import Widget from '../../js/widget'; import support from '../../js/support'; import { os, browser } from '../../js/sniffer'; import $ from 'jquery'; import { time as timeFormat } from '../../js/format'; import types from '../../js/types'; import events from '../../js/event'; import '../../js/extend'; import 'bootstrap-datepicker'; import '../time/timepicker'; import '../../js/dropdown.jquery'; /** * @augments Widget */ class DatetimepickerExtended extends Widget { /** * @type {string} */ static get selector() { return '.question input[type="datetime-local"]:not([readonly])'; } /** * @return {boolean} to instantiate or not to instantiate, that is the question */ static condition() { return !support.touch || !support.inputTypes[ 'datetime-local' ]; } _init() { this.$fakeDateI = this._createFakeDateInput(); this.$fakeTimeI = this._createFakeTimeInput(); this.element.classList.add( 'hide' ); this.element.before( document.createRange().createContextualFragment( '<div class="datetimepicker widget" />' ) ); const widget = this.question.querySelector( '.widget' ); widget.append( this.$fakeDateI[ 0 ].closest( '.date' ) ); widget.append( this.$fakeTimeI[ 0 ].closest( '.timepicker' ) ); this.$fakeDateI .datepicker( { format: 'yyyy-mm-dd', autoclose: true, todayHighlight: true, forceParse: false } ); this.$fakeTimeI .timepicker( { showMeridian: timeFormat.hour12, meridianNotation: { am: timeFormat.amNotation, pm: timeFormat.pmNotation } } ); this.value = this.originalInputValue; this._setFocusHandler( this.$fakeDateI.add( this.$fakeTimeI ) ); this.$fakeDateI .on( 'change changeDate', () => { if ( !types.date.validate( this.$fakeDateI[ 0 ].value ) ) { this.$fakeDateI.val( '' ).datepicker( 'update' ); } this.originalInputValue = this.value; return false; } ); this.$fakeTimeI .on( 'change', () => { this.originalInputValue = this.value; return false; } ); //reset button this.question.querySelector( '.btn-reset' ).addEventListener( 'click', () => { const event = this.originalInputValue ? 'change' : ''; if ( event || this.$fakeDateI.val() || this.$fakeTimeI.val() ) { this.$fakeDateI.val( '' ).trigger( event ).datepicker( 'update' ); this.$fakeTimeI.val( '' ).trigger( event ); } } ); } /** * @return {Element} fake date input */ _createFakeDateInput() { const $fakeDate = $( '<div class="date">' + '<input class="ignore" type="text" placeholder="yyyy-mm-dd"/>' + '</div>' ); return $fakeDate.find( 'input' ); } /** * @return {Element} fake time input */ _createFakeTimeInput() { const $fakeTime = $( `<div class="timepicker"> <input class="ignore timepicker-default" type="text" placeholder="hh:mm"/> </div>` ) .append( this.resetButtonHtml ); return $fakeTime.find( 'input' ); } /** * @param {jQuery} $els - a set of elements wrapped in jQuery */ _setFocusHandler( $els ) { // Handle focus on original input (goTo functionality) this.element.addEventListener( events.ApplyFocus().type, () => { $els.eq( 0 ).focus(); } ); } update() { const $dateTimeI = $( this.element ); let val = ( $dateTimeI.val().length > 0 ) ? new Date( $dateTimeI.val() ).toISOLocalString() : ''; /** * fix a bug which is only on safari (#745) * If the local timezone is +08:00, for a date value of new Date('2020-10-10T13:10:10') will be: * *** in chrome, the value is: Sat Oct 10 2020 13:10:10 GMT+0800 (China Standard Time) * *** but in safari, the value is: Sat Oct 10 2020 21:10:10 GMT+0800 (CST) * so we have to append the timezone here */ if ( os.macos && browser.safari ) { val = ( $dateTimeI.val().length > 0 ) ? ( new Date( $dateTimeI.val() + new Date().timezoneOffsetAsTime() ) ).toISOLocalString() : ''; } if ( val !== this.value ) { const vals = val.split( 'T' ); const dateVal = vals[ 0 ]; const timeVal = ( vals[ 1 ] && vals[ 1 ].length > 4 ) ? vals[ 1 ].substring( 0, 5 ) : ''; this.$fakeDateI.datepicker( 'setDate', dateVal ); this.$fakeTimeI.timepicker( 'setTime', timeVal ); } } /** * @type {string} */ get value() { if ( this.$fakeDateI.val().length > 0 && this.$fakeTimeI.val().length > 3 ) { const d = this.$fakeDateI.val().split( '-' ); const timeModified = timeFormat.hour12 ? types.time.convertMeridian( this.$fakeTimeI.val() ) : this.$fakeTimeI.val(); const t = timeModified.split( ':' ); return new Date( d[ 0 ], d[ 1 ] - 1, d[ 2 ], t[ 0 ], t[ 1 ] ).toISOLocalString(); } else { return ''; } } set value( value ) { /* Loaded or default datetime values remain untouched until they are edited. This is done to preserve the timezone information (especially for instances-to-edit) if the values are not edited (the original entry may have been done in a different time zone than the edit). However, values shown in the widget should reflect the local time representation of that value. */ const val = value ? new Date( value ).toISOLocalString() : ''; const vals = val.split( 'T' ); const dateVal = vals[ 0 ]; /** * seems the source of issue #649 is in the toISOLocalString function * refer: https://github.com/enketo/enketo-xpathjs/blob/master/src/date-extensions.js#L16 */ const timeVal = ( vals[ 1 ] && vals[ 1 ].length > 4 ) ? vals[ 1 ].substring( 0, 5 ) : ( dateVal && !vals[ 1 ] ) ? '00:00' : ''; this.$fakeDateI.datepicker( 'setDate', dateVal ); this.$fakeTimeI.timepicker( 'setTime', timeVal ); } } export default DatetimepickerExtended;