<commit_before>import { accessor as get } from './accessors'
import dates from './dates'

export function startsBefore(date, min) {
  return dates.lt(dates.merge(min, date), min, 'minutes')
}

export function positionFromDate(date, min, total) {
  if (startsBefore(date, min))
    return 0

  let diff = dates.diff(min, dates.merge(min, date), 'minutes')
  return Math.min(diff, total)
}

/**
 * Events will be sorted primarily according to earliest start time.
 * If two events start at the same time, the one with the longest duration will
 * be placed first.
 */
let sort = (events, { startAccessor, endAccessor }) => events.sort((a, b) => {
  let startA = +get(a, startAccessor)
  let startB = +get(b, startAccessor)

  if (startA === startB) {
    return +get(b, endAccessor) - +get(a, endAccessor)
  }

  return startA - startB
})

let getSlot = (event, accessor, min, totalMin) => event && positionFromDate(
  get(event, accessor), min, totalMin
)

/**
 * Two events are considered siblings if their times are overlapping
 */
let isSibling = (idx1, idx2, { events, startAccessor, endAccessor, min, totalMin }) => {
  let event1 = events[idx1]
  let event2 = events[idx2]

  if (!event1 || !event2) return false

  let start1 = getSlot(event1, startAccessor, min, totalMin)
  let start2 = getSlot(event2, startAccessor, min, totalMin)
  let end1 = getSlot(event1, endAccessor, min, totalMin)

  return (Math.abs(start1 - start2) < 60 && start2 < end1)
}

/**
 * An event is considered a child of another event if its start time is
 * more than 1 hour later than the other event's start time,
 * but before its end time.
 */
let isChild = (parentIdx, childIdx, {
  events, startAccessor, endAccessor, min, totalMin
}) => {
  if (isSibling(
    parentIdx, childIdx,
    { events, startAccessor, endAccessor, min, totalMin }
  )) return false

  let parentEnd = getSlot(events[parentIdx], endAccessor, min, totalMin)
  let childStart = getSlot(events[childIdx], startAccessor, min, totalMin)

  return parentEnd > childStart
}

/**
 * Given an event index, siblings directly following it will be found and
 * returned as an array of indexes.
 */
let getSiblings = (idx, {
  events, startAccessor, endAccessor, min, totalMin
}) => {
  let nextIdx = idx
  let siblings = []

  while (isSibling(
    idx, ++nextIdx, { events, startAccessor, endAccessor, min, totalMin })
  ) {
    siblings.push(nextIdx)
  }

  return siblings
}

/**
 * Given an event index, and a start search position, all child events to that
 * event will be found and placed into groups of siblings.
 * The return value is an array of child group arrays, as well as the largest
 * size of the child groups.
 */
let getChildGroups = (idx, nextIdx, {
  events, startAccessor, endAccessor, min, totalMin
}) => {
  let groups = []
  let nbrOfColumns = 0

  while (isChild(
    idx, nextIdx,
    { events, startAccessor, endAccessor, min, totalMin }
  )) {
    let childGroup = [nextIdx]
    let siblingIdx = nextIdx

    while (isSibling(
      nextIdx, ++siblingIdx,
      { events, startAccessor, endAccessor, min, totalMin }
    )) {
      childGroup.push(siblingIdx)
    }

    nbrOfColumns = Math.max(nbrOfColumns, childGroup.length)
    groups.push(childGroup)
    nextIdx = siblingIdx
  }

  return { childGroups: groups, nbrOfChildColumns: nbrOfColumns }
}

/**
 * Returns height and top offset, both in percentage, for an event at
 * the specified index.
 */
let getYStyles = (idx, {
  events, startAccessor, endAccessor, min, totalMin, step
}) => {
  let event = events[idx]

  let startDate = get(event, startAccessor) // start date
  let endDate = get(event, endAccessor) // end date
  let currentDate = new Date(min) // min is the current date at 00:00

  let s = new Date(startDate).getDate()
  let e = new Date(endDate).getDate()
  let c = new Date(currentDate).getDate()

  // if current day is at the start, but spans multiple days, correct the end
  if (c === s && c < e) {
    console.log('start day')
    // event.end = currentDate + 1
  }

  // if current day is in between start and end dates, span all day
  if (c > s && c < e) {
    console.log('middle day')
    event.start = currentDate
    event.end = currentDate + 1
  }

  // if current day is at the end of a multi day event, start at midnight
  if (c > s && c === e) {
    console.log('end day')
    event.start = currentDate
  }

  let start = getSlot(event, startAccessor, min, totalMin)
  let end = Math.max(getSlot(event, endAccessor, min, totalMin), start + step)
  let top = start / totalMin * 100
  let bottom = end / totalMin * 100

  let height = bottom - top

  event.start = startDate
  event.end = endDate

  return {
    top,
    height
  }
}

/**
 * Takes an array of unsorted events, and returns a sorted array
 * containing the same events, but with an additional style property.<commit_msg>Show multi-day events with start and end times properly<commit_after>import { accessor as get } from './accessors'
import dates from './dates'

export function startsBefore(date, min) {
  return dates.lt(dates.merge(min, date), min, 'minutes')
}

export function positionFromDate(date, min, total) {
  if (startsBefore(date, min))
    return 0

  let diff = dates.diff(min, dates.merge(min, date), 'minutes')
  return Math.min(diff, total)
}

/**
 * Events will be sorted primarily according to earliest start time.
 * If two events start at the same time, the one with the longest duration will
 * be placed first.
 */
let sort = (events, { startAccessor, endAccessor }) => events.sort((a, b) => {
  let startA = +get(a, startAccessor)
  let startB = +get(b, startAccessor)

  if (startA === startB) {
    return +get(b, endAccessor) - +get(a, endAccessor)
  }

  return startA - startB
})

let getSlot = (event, accessor, min, totalMin) => event && positionFromDate(
  get(event, accessor), min, totalMin
)

/**
 * Two events are considered siblings if their times are overlapping
 */
let isSibling = (idx1, idx2, { events, startAccessor, endAccessor, min, totalMin }) => {
  let event1 = events[idx1]
  let event2 = events[idx2]

  if (!event1 || !event2) return false

  let start1 = getSlot(event1, startAccessor, min, totalMin)
  let start2 = getSlot(event2, startAccessor, min, totalMin)
  let end1 = getSlot(event1, endAccessor, min, totalMin)

  return (Math.abs(start1 - start2) < 60 && start2 < end1)
}

/**
 * An event is considered a child of another event if its start time is
 * more than 1 hour later than the other event's start time,
 * but before its end time.
 */
let isChild = (parentIdx, childIdx, {
  events, startAccessor, endAccessor, min, totalMin
}) => {
  if (isSibling(
    parentIdx, childIdx,
    { events, startAccessor, endAccessor, min, totalMin }
  )) return false

  let parentEnd = getSlot(events[parentIdx], endAccessor, min, totalMin)
  let childStart = getSlot(events[childIdx], startAccessor, min, totalMin)

  return parentEnd > childStart
}

/**
 * Given an event index, siblings directly following it will be found and
 * returned as an array of indexes.
 */
let getSiblings = (idx, {
  events, startAccessor, endAccessor, min, totalMin
}) => {
  let nextIdx = idx
  let siblings = []

  while (isSibling(
    idx, ++nextIdx, { events, startAccessor, endAccessor, min, totalMin })
  ) {
    siblings.push(nextIdx)
  }

  return siblings
}

/**
 * Given an event index, and a start search position, all child events to that
 * event will be found and placed into groups of siblings.
 * The return value is an array of child group arrays, as well as the largest
 * size of the child groups.
 */
let getChildGroups = (idx, nextIdx, {
  events, startAccessor, endAccessor, min, totalMin
}) => {
  let groups = []
  let nbrOfColumns = 0

  while (isChild(
    idx, nextIdx,
    { events, startAccessor, endAccessor, min, totalMin }
  )) {
    let childGroup = [nextIdx]
    let siblingIdx = nextIdx

    while (isSibling(
      nextIdx, ++siblingIdx,
      { events, startAccessor, endAccessor, min, totalMin }
    )) {
      childGroup.push(siblingIdx)
    }

    nbrOfColumns = Math.max(nbrOfColumns, childGroup.length)
    groups.push(childGroup)
    nextIdx = siblingIdx
  }

  return { childGroups: groups, nbrOfChildColumns: nbrOfColumns }
}

let constructEvent = (title, start, end) => {
  return {
    title: title,
    start: start,
    end: end
  }
}

let handleMultiDayEvents = (title, start, end, current) => {
  let s = new Date(start).getDate()
  let e = new Date(end).getDate()
  let c = new Date(current).getDate()

  // if current day is at the start, but spans multiple days, correct the end
  if (c === s && c < e) {
    return constructEvent(title, start, new Date(current.getYear(), current.getMonth(), c, 23, 59, 59, 59))
  }

  // if current day is in between start and end dates, span all day
  else if (c > s && c < e) {
    return constructEvent(title, current, new Date(current.getYear(), current.getMonth(), c, 23, 59, 59, 59))
  }

  // if current day is at the end of a multi day event, start at midnight
  else if (c > s && c === e) {
    return constructEvent(title, current, end)
  }

  // else, construct a normal event
  else {
    return constructEvent(title, start, end)
  }
}

/**
 * Returns height and top offset, both in percentage, for an event at
 * the specified index.
 */
let getYStyles = (idx, {
  events, startAccessor, endAccessor, min, totalMin, step
}) => {
  let event = events[idx]

  let startDate = get(event, startAccessor) // start date
  let endDate = get(event, endAccessor) // end date
  let currentDate = new Date(min) // min is the current date at 00:00

  let newEvent = handleMultiDayEvents(event.title, startDate, endDate, currentDate)

  let start = getSlot(newEvent, startAccessor, min, totalMin)
  let end = Math.max(getSlot(newEvent, endAccessor, min, totalMin), start + step)
  let top = start / totalMin * 100
  let bottom = end / totalMin * 100

  let height = bottom - top

  return {
    top,
    height
  }
}

/**
 * Takes an array of unsorted events, and returns a sorted array
 * containing the same events, but with an additional style property.
{'fraction_non_alphanumeric': 0.11307699911040822, 'fraction_numerical': 0.008302856578036968, 'mean_word_length': 3.478972996901284, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, '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': '1974921', 'n_tokens_mistral': 3235, 'n_tokens_neox': 3142, 'n_words': 1151}