<commit_before><template>
  <div
    class="vhd__datepicker__wrapper"
    :class="{
      'vhd__datepicker__wrapper--grid': gridStyle,
      'vhd__datepicker__wrapper--booking': bookings.length > 0,
      vhd__datepicker__fullview: alwaysVisible,
    }"
    :ref="`DatePicker-${hash}`"
    v-if="value"
  >
    <div class="vhd__datepicker__close-button vhd__hide-on-desktop" v-if="isOpen" @click="closeMobileDatepicker">
      <i>+</i>
    </div>
    <div class="vhd__datepicker__dummy-wrapper" :class="{ 'vhd__datepicker__dummy-wrapper--is-active': isOpen }">
      <date-input
        :i18n="i18n"
        :input-date="formatDate(checkIn)"
        input-date-type="check-in"
        :is-open="isOpen"
        :toggle-datepicker="toggleDatepicker"
        :single-day-selection="singleDaySelection"
      />

      <date-input
        v-if="!singleDaySelection"
        :i18n="i18n"
        :input-date="formatDate(checkOut)"
        input-date-type="check-out"
        :is-open="isOpen"
        :toggle-datepicker="toggleDatepicker"
        :single-day-selection="singleDaySelection"
      />
    </div>
    <div class="vhd__datepicker__clear-button" tabindex="0" @click="clearSelection" v-show="showClearSelectionButton">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 68" role="img" aria-label="x">
        <title>x</title>
        <path d="M6.5 6.5l55 55m0-55l-55 55" stroke="#000" fill="none" stroke-linecap="square" />
      </svg>
    </div>
    <div
      class="vhd__datepicker"
      :class="{
        'vhd__datepicker--open': isOpen && !alwaysVisible,
        'vhd__datepicker--closed': !isOpen && !alwaysVisible,
        'vhd__datepicker--right': positionRight,
      }"
    >
      <div class="vhd__hide-on-desktop">
        <div
          v-if="isOpen"
          class="vhd__datepicker__dummy-wrapper vhd__datepicker__dummy-wrapper--no-border"
          :class="{ 'vhd__datepicker__dummy-wrapper--is-active': isOpen }"
          @click="toggleDatepicker"
        >
          <div
            class="vhd__datepicker__input"
            tabindex="0"
            :class="{
              'vhd__datepicker__dummy-input--is-active': isOpen && checkIn == null,
            }"
            type="button"
          >
            {{ `${checkIn ? formatDate(checkIn) : i18n['check-in']}` }}
          </div>
          <div
            class="vhd__datepicker__input"
            tabindex="0"
            :class="{
              'vhd__datepicker__dummy-input--is-active': isOpen && checkOut == null && checkIn !== null,
            }"
            type="button"
          >
            {{ `${checkOut ? formatDate(checkOut) : i18n['check-out']}` }}
          </div>
        </div>
      </div>
      <div v-if="isOpen || alwaysVisible" class="vhd__datepicker__inner">
        <div
          :class="{
            vhd__datepicker__header: isDesktop,
            'vhd__datepicker__header-mobile': !isDesktop,
          }"
        >
          <button
            type="button"
            class="vhd__datepicker__month-button vhd__datepicker__month-button--prev"
            @click="renderPreviousMonth"
            @keyup.enter.stop.prevent="renderPreviousMonth"
            :tabindex="isOpen ? 0 : -1"
            :disabled="activeMonthIndex === 0"
          />
          <button
            type="button"
            class="vhd__datepicker__month-button vhd__datepicker__month-button--next"
            @click="renderNextMonth"
            @keyup.enter.stop.prevent="renderNextMonth"
            :disabled="isPreventedMaxMonth"
            :tabindex="isOpen ? 0 : -1"
          />
        </div>
        <div
          v-if="isDesktop || alwaysVisible"
          class="vhd__datepicker__months"
          :class="{ 'vhd__datepicker__months--full': showSingleMonth }"
        >
          <Month
            v-for="(month, monthIndex) in paginateMonths"
            :key="`${datepickerMonthKey}-${monthIndex}-desktop`"
            ref="datepickerMonth"
            :month="month"
            :dayKey="datepickerDayKey"
            :weekKey="datepickerWeekKey"
            :isDesktop="isDesktop"
            :firstDayOfWeek="firstDayOfWeek"
            :showYear="showYear"
            :yearBeforeMonth="yearBeforeMonth"
            :activeMonthIndex="activeMonthIndex"
            :bookings="sortBookings"
            :checkIn="checkIn"
            :checkIncheckOutHalfDay="checkIncheckOutHalfDay"
            :checkInPeriod="checkInPeriod"
            :checkOut="checkOut"
            :disableCheckoutOnCheckin="disableCheckoutOnCheckin"
            :duplicateBookingDates="duplicateBookingDates"
            :hoveringDate="hoveringDate"
            :hoveringPeriod="hoveringPeriod"
            :i18n="i18n"
            :isOpen="isOpen"
            :minNightCount="minNightCount"
            :nextDisabledDate="nextDisabledDate"
            :nextPeriodDisableDates="nextPeriodDisableDates"
            :options="dayOptions"
            :priceSymbol="priceSymbol"
            :screenSize="screenSize"
            :showCustomTooltip="showCustomTooltip"
            :showPrice="showPrice"
            :disabledDates="disabledDates"
            :periodDates="periodDates"
            :sortedDisabledDates="sortedDisabledDates"
            :sortedPeriodDates="sortedPeriodDates"
            :tooltipMessage="customTooltipMessage"
            @clear-selection="clearSelection"
            @booking-clicked="handleBookingClicked"
            @day-clicked="handleDayClick"
            @enter-day="enterDay"
            @enter-month="enterMonth"
          />
        </div>
        <div
          v-if="!isDesktop && isOpen && !alwaysVisible"
          :class="['vhd__datepicker__months-wrapper', { 'vhd__show-tooltip': showCustomTooltip && hoveringTooltip }]"
        >
          <div class="vhd__datepicker__tooltip--mobile" v-if="hoveringTooltip">
            <template v-if="customTooltipMessage">
              {{ cleanString(customTooltipMessage) }}
            </template>
          </div>
          <div class="vhd__datepicker__months" ref="swiperWrapper">
            <Month
              v-for="(month, monthIndex) in paginateMonths"
              :key="`${datepickerMonthKey}-${monthIndex}-desktop`"
              ref="datepickerMonth"
              :month="month"
              :dayKey="datepickerDayKey"
              :weekKey="datepickerWeekKey"
              :isDesktop="isDesktop"
              :firstDayOfWeek="firstDayOfWeek"
              :showYear="showYear"
              :yearBeforeMonth="yearBeforeMonth"
              :activeMonthIndex="activeMonthIndex"
              :bookings="sortBookings"
              :checkIn="checkIn"
              :checkIncheckOutHalfDay="checkIncheckOutHalfDay"
              :checkInPeriod="checkInPeriod"
              :checkOut="checkOut"
              :disableCheckoutOnCheckin="disableCheckoutOnCheckin"
              :duplicateBookingDates="duplicateBookingDates"
              :hoveringDate="hoveringDate"
              :hoveringPeriod="hoveringPeriod"
              :i18n="i18n"
              :isOpen="isOpen"
              :minNightCount="minNightCount"
              :nextDisabledDate="nextDisabledDate"
              :nextPeriodDisableDates="nextPeriodDisableDates"
              :options="dayOptions"
              :priceSymbol="priceSymbol"
              :screenSize="screenSize"
              :showCustomTooltip="false"
              :showPrice="showPrice"
              :sortedDisabledDates="sortedDisabledDates"
              :sortedPeriodDates="sortedPeriodDates"
              :tooltipMessage="customTooltipMessage"
              @clear-selection="clearSelection"
              @booking-clicked="handleBookingClicked"
              @day-clicked="handleDayClick"
              @enter-day="enterDay"
              @enter-month="enterMonth"
            />
          </div>
        </div>
      </div>
      <slot name="content" />
    </div>
  </div>
</template>

<script>
import throttle from 'lodash.throttle'
import fecha from 'fecha'

import Month from './components/Month.vue'
import DateInput from './components/DateInput.vue'
import Helpers from '../helpers'
import i18nDefaults from '../../public/i18n/en'

export default {
  name: 'HotelDatePicker',
  components: {
    Month,
    DateInput,
  },
  props: {
    alwaysVisible: {
      type: Boolean,
      default: false,
    },
    bookings: {
      type: Array,
      default() {
        return []
      },
    },
    closeDatepickerOnClickOutside: {
      type: Boolean,
      default: true,
    },
    disableCheckoutOnCheckin: {
      type: Boolean,
      default: false,
    },
    disabledDates: {
      type: Array,
      default() {
        return []
      },
    },
    disabledDaysOfWeek: {
      type: Array,
      default() {
        return []
      },
    },
    disabledWeekDays: {
      type: Object,
      default() {
        return {}
      },
    },
    displayClearButton: {
      type: Boolean,
      default: true,
    },
    enableCheckout: {
      type: Boolean,
      default: false,
    },
    endDate: {
      type: [Date, String, Number],
      default: Infinity,
    },
    endingDateValue: {
      type: [Date, null],
      default: null,
    },
    firstDayOfWeek: {
      type: Number,
      default: 0,
    },
    format: {
      type: String,
      default: 'YYYY-MM-DD',
    },
    gridStyle: {
      type: Boolean,
      default: true,
    },
    halfDay: {
      type: Boolean,
      default: true,
    },
    hoveringTooltip: {
      default: true,
      type: [Boolean, Function],
    },
    i18n: {
      type: Object,
      default: () => i18nDefaults,
    },
    lastDateAvailable: {
      type: [Number, Date],
      default: Infinity,
    },
    maxNights: {
      type: [Number, null],
      default: null,
    },
    minNights: {
      type: Number,
      default: 1,
    },
    periodDates: {
      type: Array,
      default() {
        return []
      },
    },
    positionRight: {
      type: Boolean,
      default: false,
    },
    priceSymbol: {
      type: String,
      default: '',
    },
    showPrice: {
      type: Boolean,
      default: false,
    },
    showSingleMonth: {
      type: Boolean,
      default: false,
    },
    showYear: {
      type: Boolean,
      default: true,
    },
    singleDaySelection: {
      type: Boolean,
      default: false,
    },
    startDate: {
      type: [Date, String],
      default() {
        return new Date()
      },
    },
    startingDateValue: {
      type: [Date, null],
      default: null,
    },
    tooltipMessage: {
      type: [String, null],
      default: null,
    },
    value: {
      type: Boolean,
      default: true,
    },
    yearBeforeMonth: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      activeMonthIndex: 0,
      checkIn: this.startingDateValue,
      checkIncheckOutHalfDay: {},
      checkInPeriod: {},
      checkOut: this.endingDateValue,
      hoveringPeriod: {},
      customTooltip: '',
      customTooltipHalfday: '',
      datepickerDayKey: 0,
      datepickerMonthKey: 0,
      datepickerWeekKey: 0,
      dynamicNightCounts: null,
      hash: Date.now(),
      hoveringDate: null,
      isTouchMove: false,
      months: [],
      nextDisabledDate: null,
      nextPeriodDisableDates: [],
      open: false,
      screenSize: null,
      showCustomTooltip: false,
      sortedDisabledDates: null,
      xDown: null,
      xUp: null,
      yDown: null,
      yUp: null,
    }
  },
  computed: {
    isOpen: {
      get() {
        return this.open
      },
      set(open) {
        this.open = open

        if (!this.isDesktop && !this.alwaysVisible) {
          const body = document.querySelector('body')

          if (open) {
            body.style.overflow = 'hidden'

            this.$nextTick(() => {
              if (this.$refs) {
                const { swiperWrapper } = this.$refs
                const monthHeihgt = this.$refs.datepickerMonth[0].offsetHeight
                const currentSelectionIndex = this.checkOut ? this.getMonthDiff(new Date(), this.checkOut) : 0

                swiperWrapper.scrollTop = currentSelectionIndex * monthHeihgt
              }
            })
          } else {
            body.style.overflow = ''
          }
        }

        this.$emit('input', this.open)
      },
    },
    sortBookings() {
      if (this.bookings.length > 0) {
        const bookings = [...this.bookings]

        return bookings.sort((a, b) => {
          const aa = a.checkInDate.split('/').reverse().join()
          const bb = b.checkOutDate.split('/').reverse().join()

          // eslint-disable-next-line no-nested-ternary
          return aa < bb ? -1 : aa > bb ? 1 : 0
        })
      }

      return []
    },
    duplicateBookingDates() {
      return this.baseHalfDayDates.filter(
        (
          (newArr) => (date) =>
            newArr.has(date) || !newArr.add(date)
        )(new Set()),
      )
    },
    baseHalfDayDates() {
      if (this.sortBookings.length > 0) {
        const bookings = this.sortBookings.map((x) => [x.checkInDate, x.checkOutDate])

        return bookings.reduce((a, b) => {
          return a.concat(b)
        })
      }

      return this.disabledDates
    },
    paginateMonths() {
      const months = [this.months[this.activeMonthIndex]]

      if (!(this.showSingleMonth || (this.alwaysVisible && !this.isDesktop))) {
        months.push(this.months[this.activeMonthIndex + 1])
      }

      return months
    },
    customTooltipMessage() {
      let tooltip = ''

      if (this.hoveringDate && (this.customTooltip || this.customTooltipHalfday)) {
        if (this.customTooltip && this.customTooltipHalfday) {
          tooltip = `${this.customTooltipHalfday}. <br/> ${this.customTooltip}`
        } else if (this.customTooltipHalfday && !this.customTooltip) {
          tooltip = this.customTooltipHalfday
        } else {
          tooltip = this.customTooltip
        }

        return tooltip
      }

      return this.tooltipMessage
    },
    sortedPeriodDates() {
      let periodDates = []

      if (this.periodDates) {
        const sortFunction = (fecha1, fecha2) => {
          const v1 = fecha1.startAt.split('/').reverse().join() + fecha1.endAt.split('/').reverse().join()
          const v2 = fecha2.startAt.split('/').reverse().join() + fecha2.endAt.split('/').reverse().join()

          // eslint-disable-next-line no-nested-ternary
          return v1 < v2 ? -1 : v1 > v2 ? 1 : 0
        }

        periodDates = [...this.periodDates].sort(sortFunction)
      }

      return periodDates
    },
    sliceMonthMobile() {
      const nbMonthRenderDom = 4

      if (this.activeMonthIndex >= nbMonthRenderDom) {
        return this.months.slice(this.activeMonthIndex - 3, this.activeMonthIndex + 1)
      }

      return this.months.slice(0, nbMonthRenderDom)
    },
    isPreventedMaxMonth() {
      const lastIndexMonthAvailable = this.getMonthDiff(this.startDate, this.lastDateAvailable)

      return this.activeMonthIndex >= lastIndexMonthAvailable - 1
    },
    minNightCount() {
      return this.dynamicNightCounts || this.minNights
    },
    showClearSelectionButton() {
      return Boolean((this.checkIn || this.checkOut) && this.displayClearButton)
    },
    disabledWeekDaysObject() {
      const disabledDays = this.disabledDaysOfWeek.map((d) => d.toLowerCase())
      // const names = this.i18n['day-names']
      const names = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']
      const SUNDAY = names[0]
      const MONDAY = names[1]
      const TUESDAY = names[2]
      const WEDNESDAY = names[3]
      const THURSDAY = names[4]
      const FRIDAY = names[5]
      const SATURDAY = names[6]
      // The order of _default is important!
      const disabledWeekDaysObject = {
        sunday: disabledDays.includes(SUNDAY),
        monday: disabledDays.includes(MONDAY),
        tuesday: disabledDays.includes(TUESDAY),
        wednesday: disabledDays.includes(WEDNESDAY),
        thursday: disabledDays.includes(THURSDAY),
        friday: disabledDays.includes(FRIDAY),
        saturday: disabledDays.includes(SATURDAY),
      }

      return Object.assign(disabledWeekDaysObject, this.disabledWeekDays)
    },
    disabledWeekDaysArray() {
      const days = this.disabledWeekDaysObject
      // const names = this.i18n['day-names']
      const names = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']

      const fn = function fnDisabledWeekDaysArray(day, ix) {
        return day[1] ? names[ix] : false
      }

      return Object.entries(days)
        .map(fn)
        .filter((v) => v)
    },
    dayOptions() {
      return { ...this.$props, disabledWeekDaysObject: this.disabledWeekDaysObject }
    },
    numberOfMonths() {
      return this.showSingleMonth ? 1 : 2
    },
    isDesktop() {
      return this.screenSize === 'desktop'
    },
  },
  watch: {
    bookings() {
      this.createHalfDayDates(this.baseHalfDayDates)
      this.reRender()
    },
    checkIn(newDate) {
      this.$emit('check-in-changed', newDate)
      this.$emit('starting-date-changed', newDate)
      this.reRender()
    },
    checkOut(newDate) {
      this.$emit('ending-date-changed', newDate)

      if (this.checkOut !== null) {
        this.hoveringDate = null
        this.nextDisabledDate = null
        this.createHalfDayDates(this.baseHalfDayDates)
        this.reRender()
        this.showCustomTooltip = false
        this.isOpen = false
      }

      this.$emit('check-out-changed', newDate)
      this.reRender()
    },
    firstDayOfWeek(newDay) {
      this.$emit('first-day-of-week-changed', newDay)
      const startDate = new Date(this.startDate)
      const offset = this.numberOfMonths + this.activeMonthIndex

      this.generateInitialMonths()

      for (let i = this.numberOfMonths; i < offset; i++) {
        this.createMonth(new Date(startDate.getFullYear(), startDate.getMonth() + i, 1))
      }

      this.reRender()
    },
    startingDateValue(date) {
      this.setCheckIn(date)
    },
    endingDateValue(date) {
      this.setCheckOut(date)
    },
    singleDaySelection(single) {
      if (single) {
        this.setCheckOut(this.checkIn)
      } else {
        this.setCheckIn(this.checkIn)
        this.setCheckOut(null)
      }

      this.reRender()
    },
    yearBeforeMonth() {
      this.reRender()
    },
    i18n() {
      this.configureI18n()
    },
    disabledDates() {
      this.nextDisabledDate = null
      this.createHalfDayDates(this.baseHalfDayDates)
      this.reRender()
    },
  },
  created() {
    this.configureI18n()
    this.generateInitialMonths()
  },
  mounted() {
    this.handleWindowResize()

    window.addEventListener('resize', this.handleWindowResize)

    if (!this.isDesktop) {
      document.addEventListener('touchstart', this.handleTouchStart, false)
      document.addEventListener('touchmove', this.handleTouchMove, false)
      document.addEventListener('touchend', this.handleTouchEnd, false)
    } else {
      document.addEventListener('click', this.handleClickOutside, false)
      document.addEventListener('keyup', this.escFunction, false)
    }

    this.onElementHeightChange(document.body, () => {
      this.emitHeighChangeEvent()
    })
    this.createHalfDayDates(this.baseHalfDayDates)
  },
  destroyed() {
    window.removeEventListener('resize', this.handleWindowResize)

    if (!this.isDesktop) {
      document.removeEventListener('touchstart', this.handleTouchStart)
      document.removeEventListener('touchmove', this.handleTouchMove)
      document.removeEventListener('touchend', this.handleTouchEnd)
    } else {
      document.removeEventListener('keyup', this.escFunction, false)
      document.removeEventListener('click', this.handleClickOutside)
    }
  },
  methods: {
    ...Helpers,
    transformDisabledWeekDays() {},
    configureI18n() {
      fecha.setGlobalDateI18n({
        dayNames: this.i18n['day-names'],
        dayNamesShort: this.shortenString(this.i18n['day-names'], 3),
        monthNames: this.i18n['month-names'],
        monthNamesShort: this.shortenString(this.i18n['month-names'], 3),
        amPm: ['am', 'pm'],
        // D is the day of the month, function returns something like...  3rd or 11th
        DoFn(D) {
          return D + ['th', 'st', 'nd', 'rd'][D % 10 > 3 ? 0 : ((D - (D % 10) !== 10) * D) % 10]
        },
      })
    },
    generateInitialMonths() {
      this.months = []

      if (
        this.checkIn &&
        (this.getMonthDiff(this.getNextMonth(new Date(this.startDate)), this.checkIn) > 0 ||
          this.getMonthDiff(this.startDate, this.checkIn) > 0)
      ) {
        this.createMonth(new Date(this.startDate))
        const count = this.getMonthDiff(this.startDate, this.checkIn)
        let nextMonth = new Date(this.startDate)

        for (let i = 0; i <= count; i++) {
          const tempNextMonth = this.getNextMonth(nextMonth)

          this.createMonth(tempNextMonth)
          nextMonth = tempNextMonth<commit_msg>Fix Generate Initial Months in case of Single Month View<commit_after><template>
  <div
    class="vhd__datepicker__wrapper"
    :class="{
      'vhd__datepicker__wrapper--grid': gridStyle,
      'vhd__datepicker__wrapper--booking': bookings.length > 0,
      vhd__datepicker__fullview: alwaysVisible,
    }"
    :ref="`DatePicker-${hash}`"
    v-if="value"
  >
    <div class="vhd__datepicker__close-button vhd__hide-on-desktop" v-if="isOpen" @click="closeMobileDatepicker">
      <i>+</i>
    </div>
    <div class="vhd__datepicker__dummy-wrapper" :class="{ 'vhd__datepicker__dummy-wrapper--is-active': isOpen }">
      <date-input
        :i18n="i18n"
        :input-date="formatDate(checkIn)"
        input-date-type="check-in"
        :is-open="isOpen"
        :toggle-datepicker="toggleDatepicker"
        :single-day-selection="singleDaySelection"
      />

      <date-input
        v-if="!singleDaySelection"
        :i18n="i18n"
        :input-date="formatDate(checkOut)"
        input-date-type="check-out"
        :is-open="isOpen"
        :toggle-datepicker="toggleDatepicker"
        :single-day-selection="singleDaySelection"
      />
    </div>
    <div class="vhd__datepicker__clear-button" tabindex="0" @click="clearSelection" v-show="showClearSelectionButton">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 68" role="img" aria-label="x">
        <title>x</title>
        <path d="M6.5 6.5l55 55m0-55l-55 55" stroke="#000" fill="none" stroke-linecap="square" />
      </svg>
    </div>
    <div
      class="vhd__datepicker"
      :class="{
        'vhd__datepicker--open': isOpen && !alwaysVisible,
        'vhd__datepicker--closed': !isOpen && !alwaysVisible,
        'vhd__datepicker--right': positionRight,
      }"
    >
      <div class="vhd__hide-on-desktop">
        <div
          v-if="isOpen"
          class="vhd__datepicker__dummy-wrapper vhd__datepicker__dummy-wrapper--no-border"
          :class="{ 'vhd__datepicker__dummy-wrapper--is-active': isOpen }"
          @click="toggleDatepicker"
        >
          <div
            class="vhd__datepicker__input"
            tabindex="0"
            :class="{
              'vhd__datepicker__dummy-input--is-active': isOpen && checkIn == null,
            }"
            type="button"
          >
            {{ `${checkIn ? formatDate(checkIn) : i18n['check-in']}` }}
          </div>
          <div
            class="vhd__datepicker__input"
            tabindex="0"
            :class="{
              'vhd__datepicker__dummy-input--is-active': isOpen && checkOut == null && checkIn !== null,
            }"
            type="button"
          >
            {{ `${checkOut ? formatDate(checkOut) : i18n['check-out']}` }}
          </div>
        </div>
      </div>
      <div v-if="isOpen || alwaysVisible" class="vhd__datepicker__inner">
        <div
          :class="{
            vhd__datepicker__header: isDesktop,
            'vhd__datepicker__header-mobile': !isDesktop,
          }"
        >
          <button
            type="button"
            class="vhd__datepicker__month-button vhd__datepicker__month-button--prev"
            @click="renderPreviousMonth"
            @keyup.enter.stop.prevent="renderPreviousMonth"
            :tabindex="isOpen ? 0 : -1"
            :disabled="activeMonthIndex === 0"
          />
          <button
            type="button"
            class="vhd__datepicker__month-button vhd__datepicker__month-button--next"
            @click="renderNextMonth"
            @keyup.enter.stop.prevent="renderNextMonth"
            :disabled="isPreventedMaxMonth"
            :tabindex="isOpen ? 0 : -1"
          />
        </div>
        <div
          v-if="isDesktop || alwaysVisible"
          class="vhd__datepicker__months"
          :class="{ 'vhd__datepicker__months--full': showSingleMonth }"
        >
          <Month
            v-for="(month, monthIndex) in paginateMonths"
            :key="`${datepickerMonthKey}-${monthIndex}-desktop`"
            ref="datepickerMonth"
            :month="month"
            :dayKey="datepickerDayKey"
            :weekKey="datepickerWeekKey"
            :isDesktop="isDesktop"
            :firstDayOfWeek="firstDayOfWeek"
            :showYear="showYear"
            :yearBeforeMonth="yearBeforeMonth"
            :activeMonthIndex="activeMonthIndex"
            :bookings="sortBookings"
            :checkIn="checkIn"
            :checkIncheckOutHalfDay="checkIncheckOutHalfDay"
            :checkInPeriod="checkInPeriod"
            :checkOut="checkOut"
            :disableCheckoutOnCheckin="disableCheckoutOnCheckin"
            :duplicateBookingDates="duplicateBookingDates"
            :hoveringDate="hoveringDate"
            :hoveringPeriod="hoveringPeriod"
            :i18n="i18n"
            :isOpen="isOpen"
            :minNightCount="minNightCount"
            :nextDisabledDate="nextDisabledDate"
            :nextPeriodDisableDates="nextPeriodDisableDates"
            :options="dayOptions"
            :priceSymbol="priceSymbol"
            :screenSize="screenSize"
            :showCustomTooltip="showCustomTooltip"
            :showPrice="showPrice"
            :disabledDates="disabledDates"
            :periodDates="periodDates"
            :sortedDisabledDates="sortedDisabledDates"
            :sortedPeriodDates="sortedPeriodDates"
            :tooltipMessage="customTooltipMessage"
            @clear-selection="clearSelection"
            @booking-clicked="handleBookingClicked"
            @day-clicked="handleDayClick"
            @enter-day="enterDay"
            @enter-month="enterMonth"
          />
        </div>
        <div
          v-if="!isDesktop && isOpen && !alwaysVisible"
          :class="['vhd__datepicker__months-wrapper', { 'vhd__show-tooltip': showCustomTooltip && hoveringTooltip }]"
        >
          <div class="vhd__datepicker__tooltip--mobile" v-if="hoveringTooltip">
            <template v-if="customTooltipMessage">
              {{ cleanString(customTooltipMessage) }}
            </template>
          </div>
          <div class="vhd__datepicker__months" ref="swiperWrapper">
            <Month
              v-for="(month, monthIndex) in paginateMonths"
              :key="`${datepickerMonthKey}-${monthIndex}-desktop`"
              ref="datepickerMonth"
              :month="month"
              :dayKey="datepickerDayKey"
              :weekKey="datepickerWeekKey"
              :isDesktop="isDesktop"
              :firstDayOfWeek="firstDayOfWeek"
              :showYear="showYear"
              :yearBeforeMonth="yearBeforeMonth"
              :activeMonthIndex="activeMonthIndex"
              :bookings="sortBookings"
              :checkIn="checkIn"
              :checkIncheckOutHalfDay="checkIncheckOutHalfDay"
              :checkInPeriod="checkInPeriod"
              :checkOut="checkOut"
              :disableCheckoutOnCheckin="disableCheckoutOnCheckin"
              :duplicateBookingDates="duplicateBookingDates"
              :hoveringDate="hoveringDate"
              :hoveringPeriod="hoveringPeriod"
              :i18n="i18n"
              :isOpen="isOpen"
              :minNightCount="minNightCount"
              :nextDisabledDate="nextDisabledDate"
              :nextPeriodDisableDates="nextPeriodDisableDates"
              :options="dayOptions"
              :priceSymbol="priceSymbol"
              :screenSize="screenSize"
              :showCustomTooltip="false"
              :showPrice="showPrice"
              :sortedDisabledDates="sortedDisabledDates"
              :sortedPeriodDates="sortedPeriodDates"
              :tooltipMessage="customTooltipMessage"
              @clear-selection="clearSelection"
              @booking-clicked="handleBookingClicked"
              @day-clicked="handleDayClick"
              @enter-day="enterDay"
              @enter-month="enterMonth"
            />
          </div>
        </div>
      </div>
      <slot name="content" />
    </div>
  </div>
</template>

<script>
import throttle from 'lodash.throttle'
import fecha from 'fecha'

import Month from './components/Month.vue'
import DateInput from './components/DateInput.vue'
import Helpers from '../helpers'
import i18nDefaults from '../../public/i18n/en'

export default {
  name: 'HotelDatePicker',
  components: {
    Month,
    DateInput,
  },
  props: {
    alwaysVisible: {
      type: Boolean,
      default: false,
    },
    bookings: {
      type: Array,
      default() {
        return []
      },
    },
    closeDatepickerOnClickOutside: {
      type: Boolean,
      default: true,
    },
    disableCheckoutOnCheckin: {
      type: Boolean,
      default: false,
    },
    disabledDates: {
      type: Array,
      default() {
        return []
      },
    },
    disabledDaysOfWeek: {
      type: Array,
      default() {
        return []
      },
    },
    disabledWeekDays: {
      type: Object,
      default() {
        return {}
      },
    },
    displayClearButton: {
      type: Boolean,
      default: true,
    },
    enableCheckout: {
      type: Boolean,
      default: false,
    },
    endDate: {
      type: [Date, String, Number],
      default: Infinity,
    },
    endingDateValue: {
      type: [Date, null],
      default: null,
    },
    firstDayOfWeek: {
      type: Number,
      default: 0,
    },
    format: {
      type: String,
      default: 'YYYY-MM-DD',
    },
    gridStyle: {
      type: Boolean,
      default: true,
    },
    halfDay: {
      type: Boolean,
      default: true,
    },
    hoveringTooltip: {
      default: true,
      type: [Boolean, Function],
    },
    i18n: {
      type: Object,
      default: () => i18nDefaults,
    },
    lastDateAvailable: {
      type: [Number, Date],
      default: Infinity,
    },
    maxNights: {
      type: [Number, null],
      default: null,
    },
    minNights: {
      type: Number,
      default: 1,
    },
    periodDates: {
      type: Array,
      default() {
        return []
      },
    },
    positionRight: {
      type: Boolean,
      default: false,
    },
    priceSymbol: {
      type: String,
      default: '',
    },
    showPrice: {
      type: Boolean,
      default: false,
    },
    showSingleMonth: {
      type: Boolean,
      default: false,
    },
    showYear: {
      type: Boolean,
      default: true,
    },
    singleDaySelection: {
      type: Boolean,
      default: false,
    },
    startDate: {
      type: [Date, String],
      default() {
        return new Date()
      },
    },
    startingDateValue: {
      type: [Date, null],
      default: null,
    },
    tooltipMessage: {
      type: [String, null],
      default: null,
    },
    value: {
      type: Boolean,
      default: true,
    },
    yearBeforeMonth: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      activeMonthIndex: 0,
      checkIn: this.startingDateValue,
      checkIncheckOutHalfDay: {},
      checkInPeriod: {},
      checkOut: this.endingDateValue,
      hoveringPeriod: {},
      customTooltip: '',
      customTooltipHalfday: '',
      datepickerDayKey: 0,
      datepickerMonthKey: 0,
      datepickerWeekKey: 0,
      dynamicNightCounts: null,
      hash: Date.now(),
      hoveringDate: null,
      isTouchMove: false,
      months: [],
      nextDisabledDate: null,
      nextPeriodDisableDates: [],
      open: false,
      screenSize: null,
      showCustomTooltip: false,
      sortedDisabledDates: null,
      xDown: null,
      xUp: null,
      yDown: null,
      yUp: null,
    }
  },
  computed: {
    isOpen: {
      get() {
        return this.open
      },
      set(open) {
        this.open = open

        if (!this.isDesktop && !this.alwaysVisible) {
          const body = document.querySelector('body')

          if (open) {
            body.style.overflow = 'hidden'

            this.$nextTick(() => {
              if (this.$refs) {
                const { swiperWrapper } = this.$refs
                const monthHeihgt = this.$refs.datepickerMonth[0].offsetHeight
                const currentSelectionIndex = this.checkOut ? this.getMonthDiff(new Date(), this.checkOut) : 0

                swiperWrapper.scrollTop = currentSelectionIndex * monthHeihgt
              }
            })
          } else {
            body.style.overflow = ''
          }
        }

        this.$emit('input', this.open)
      },
    },
    sortBookings() {
      if (this.bookings.length > 0) {
        const bookings = [...this.bookings]

        return bookings.sort((a, b) => {
          const aa = a.checkInDate.split('/').reverse().join()
          const bb = b.checkOutDate.split('/').reverse().join()

          // eslint-disable-next-line no-nested-ternary
          return aa < bb ? -1 : aa > bb ? 1 : 0
        })
      }

      return []
    },
    duplicateBookingDates() {
      return this.baseHalfDayDates.filter(
        (
          (newArr) => (date) =>
            newArr.has(date) || !newArr.add(date)
        )(new Set()),
      )
    },
    baseHalfDayDates() {
      if (this.sortBookings.length > 0) {
        const bookings = this.sortBookings.map((x) => [x.checkInDate, x.checkOutDate])

        return bookings.reduce((a, b) => {
          return a.concat(b)
        })
      }

      return this.disabledDates
    },
    paginateMonths() {
      const months = [this.months[this.activeMonthIndex]]

      if (!(this.showSingleMonth || (this.alwaysVisible && !this.isDesktop))) {
        months.push(this.months[this.activeMonthIndex + 1])
      }

      return months
    },
    customTooltipMessage() {
      let tooltip = ''

      if (this.hoveringDate && (this.customTooltip || this.customTooltipHalfday)) {
        if (this.customTooltip && this.customTooltipHalfday) {
          tooltip = `${this.customTooltipHalfday}. <br/> ${this.customTooltip}`
        } else if (this.customTooltipHalfday && !this.customTooltip) {
          tooltip = this.customTooltipHalfday
        } else {
          tooltip = this.customTooltip
        }

        return tooltip
      }

      return this.tooltipMessage
    },
    sortedPeriodDates() {
      let periodDates = []

      if (this.periodDates) {
        const sortFunction = (fecha1, fecha2) => {
          const v1 = fecha1.startAt.split('/').reverse().join() + fecha1.endAt.split('/').reverse().join()
          const v2 = fecha2.startAt.split('/').reverse().join() + fecha2.endAt.split('/').reverse().join()

          // eslint-disable-next-line no-nested-ternary
          return v1 < v2 ? -1 : v1 > v2 ? 1 : 0
        }

        periodDates = [...this.periodDates].sort(sortFunction)
      }

      return periodDates
    },
    sliceMonthMobile() {
      const nbMonthRenderDom = 4

      if (this.activeMonthIndex >= nbMonthRenderDom) {
        return this.months.slice(this.activeMonthIndex - 3, this.activeMonthIndex + 1)
      }

      return this.months.slice(0, nbMonthRenderDom)
    },
    isPreventedMaxMonth() {
      const lastIndexMonthAvailable = this.getMonthDiff(this.startDate, this.lastDateAvailable)

      return this.activeMonthIndex >= lastIndexMonthAvailable - 1
    },
    minNightCount() {
      return this.dynamicNightCounts || this.minNights
    },
    showClearSelectionButton() {
      return Boolean((this.checkIn || this.checkOut) && this.displayClearButton)
    },
    disabledWeekDaysObject() {
      const disabledDays = this.disabledDaysOfWeek.map((d) => d.toLowerCase())
      // const names = this.i18n['day-names']
      const names = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']
      const SUNDAY = names[0]
      const MONDAY = names[1]
      const TUESDAY = names[2]
      const WEDNESDAY = names[3]
      const THURSDAY = names[4]
      const FRIDAY = names[5]
      const SATURDAY = names[6]
      // The order of _default is important!
      const disabledWeekDaysObject = {
        sunday: disabledDays.includes(SUNDAY),
        monday: disabledDays.includes(MONDAY),
        tuesday: disabledDays.includes(TUESDAY),
        wednesday: disabledDays.includes(WEDNESDAY),
        thursday: disabledDays.includes(THURSDAY),
        friday: disabledDays.includes(FRIDAY),
        saturday: disabledDays.includes(SATURDAY),
      }

      return Object.assign(disabledWeekDaysObject, this.disabledWeekDays)
    },
    disabledWeekDaysArray() {
      const days = this.disabledWeekDaysObject
      // const names = this.i18n['day-names']
      const names = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']

      const fn = function fnDisabledWeekDaysArray(day, ix) {
        return day[1] ? names[ix] : false
      }

      return Object.entries(days)
        .map(fn)
        .filter((v) => v)
    },
    dayOptions() {
      return { ...this.$props, disabledWeekDaysObject: this.disabledWeekDaysObject }
    },
    numberOfMonths() {
      return this.showSingleMonth ? 1 : 2
    },
    isDesktop() {
      return this.screenSize === 'desktop'
    },
  },
  watch: {
    bookings() {
      this.createHalfDayDates(this.baseHalfDayDates)
      this.reRender()
    },
    checkIn(newDate) {
      this.$emit('check-in-changed', newDate)
      this.$emit('starting-date-changed', newDate)
      this.reRender()
    },
    checkOut(newDate) {
      this.$emit('ending-date-changed', newDate)

      if (this.checkOut !== null) {
        this.hoveringDate = null
        this.nextDisabledDate = null
        this.createHalfDayDates(this.baseHalfDayDates)
        this.reRender()
        this.showCustomTooltip = false
        this.isOpen = false
      }

      this.$emit('check-out-changed', newDate)
      this.reRender()
    },
    firstDayOfWeek(newDay) {
      this.$emit('first-day-of-week-changed', newDay)
      const startDate = new Date(this.startDate)
      const offset = this.numberOfMonths + this.activeMonthIndex

      this.generateInitialMonths()

      for (let i = this.numberOfMonths; i < offset; i++) {
        this.createMonth(new Date(startDate.getFullYear(), startDate.getMonth() + i, 1))
      }

      this.reRender()
    },
    startingDateValue(date) {
      this.setCheckIn(date)
    },
    endingDateValue(date) {
      this.setCheckOut(date)
    },
    singleDaySelection(single) {
      if (single) {
        this.setCheckOut(this.checkIn)
      } else {
        this.setCheckIn(this.checkIn)
        this.setCheckOut(null)
      }

      this.reRender()
    },
    yearBeforeMonth() {
      this.reRender()
    },
    i18n() {
      this.configureI18n()
    },
    disabledDates() {
      this.nextDisabledDate = null
      this.createHalfDayDates(this.baseHalfDayDates)
      this.reRender()
    },
  },
  created() {
    this.configureI18n()
    this.generateInitialMonths()
  },
  mounted() {
    this.handleWindowResize()

    window.addEventListener('resize', this.handleWindowResize)

    if (!this.isDesktop) {
      document.addEventListener('touchstart', this.handleTouchStart, false)
      document.addEventListener('touchmove', this.handleTouchMove, false)
      document.addEventListener('touchend', this.handleTouchEnd, false)
    } else {
      document.addEventListener('click', this.handleClickOutside, false)
      document.addEventListener('keyup', this.escFunction, false)
    }

    this.onElementHeightChange(document.body, () => {
      this.emitHeighChangeEvent()
    })
    this.createHalfDayDates(this.baseHalfDayDates)
  },
  destroyed() {
    window.removeEventListener('resize', this.handleWindowResize)

    if (!this.isDesktop) {
      document.removeEventListener('touchstart', this.handleTouchStart)
      document.removeEventListener('touchmove', this.handleTouchMove)
      document.removeEventListener('touchend', this.handleTouchEnd)
    } else {
      document.removeEventListener('keyup', this.escFunction, false)
      document.removeEventListener('click', this.handleClickOutside)
    }
  },
  methods: {
    ...Helpers,
    transformDisabledWeekDays() {},
    configureI18n() {
      fecha.setGlobalDateI18n({
        dayNames: this.i18n['day-names'],
        dayNamesShort: this.shortenString(this.i18n['day-names'], 3),
        monthNames: this.i18n['month-names'],
        monthNamesShort: this.shortenString(this.i18n['month-names'], 3),
        amPm: ['am', 'pm'],
        // D is the day of the month, function returns something like...  3rd or 11th
        DoFn(D) {
          return D + ['th', 'st', 'nd', 'rd'][D % 10 > 3 ? 0 : ((D - (D % 10) !== 10) * D) % 10]
        },
      })
    },
    generateInitialMonths() {
      this.months = []

      if (
        this.checkIn &&
        (this.getMonthDiff(this.getNextMonth(new Date(this.startDate)), this.checkIn) > 0 ||
          this.getMonthDiff(this.startDate, this.checkIn) > 0)
      ) {
        this.createMonth(new Date(this.startDate))
        const count = this.getMonthDiff(this.startDate, this.checkIn)
        const monthCount = this.showSingleMonth ? count - 1 : count
        let nextMonth = new Date(this.startDate)

        for (let i = 0; i <= monthCount; i++) {
          const tempNextMonth = this.getNextMonth(nextMonth)

          this.createMonth(tempNextMonth)
          nextMonth = tempNextMonth
{'fraction_non_alphanumeric': 0.12904370034052212, 'fraction_numerical': 0.007165153234960272, 'mean_word_length': 2.152601759355897, 'pattern_counts': {'":': 0, '<': 111, '<?xml version=': 0, '>': 145, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4534867', 'n_tokens_mistral': 13050, 'n_tokens_neox': 12556, 'n_words': 2062}