TimeGridEventRenderUtils.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. export function getTimeTexts() {
  2. return $('.fc-event').map(function(i, eventEl) {
  3. return $(eventEl).find('.fc-time').text()
  4. }).get()
  5. }
  6. /*
  7. Returns a boolean.
  8. TODO: check isStart/isEnd.
  9. */
  10. export function checkEventRendering(start, end) {
  11. start = $.fullCalendar.moment.parseZone(start)
  12. end = $.fullCalendar.moment.parseZone(end)
  13. var expectedRects = computeSpanRects(start, end)
  14. var eventEls = $('.fc-event') // sorted by DOM order. not good for RTL
  15. var isMatch = checkEventRenderingMatch(expectedRects, eventEls)
  16. return {
  17. rects: expectedRects,
  18. els: eventEls,
  19. length: eventEls.length,
  20. isMatch: isMatch
  21. }
  22. }
  23. function checkEventRenderingMatch(expectedRects, eventEls) {
  24. var expectedLength = expectedRects.length
  25. var i, expectedRect
  26. var elRect
  27. if (eventEls.length !== expectedLength) {
  28. console.log('does not match element count')
  29. return false
  30. }
  31. for (i = 0; i < expectedLength; i++) {
  32. expectedRect = expectedRects[i]
  33. elRect = eventEls[i].getBoundingClientRect()
  34. // horizontally contained AND vertically really similar?
  35. if (!(
  36. elRect.left >= expectedRect.left &&
  37. elRect.right <= expectedRect.right &&
  38. Math.abs(elRect.top - expectedRect.top) < 1 &&
  39. Math.abs(elRect.bottom - expectedRect.bottom) < 1
  40. )) {
  41. console.log('rects do not match')
  42. return false
  43. }
  44. }
  45. return true
  46. }
  47. export function computeSpanRects(start, end) {
  48. var dayStructs = computeDays()
  49. var slotStructs = computeSlots()
  50. var dayI, dayStruct
  51. var slotI, slotStruct
  52. var slotStart
  53. var slotEnd
  54. var coverage
  55. var startTop = null
  56. var endTop = null
  57. var rects = []
  58. for (dayI = 0; dayI < dayStructs.length; dayI++) {
  59. dayStruct = dayStructs[dayI]
  60. for (slotI = 0; slotI < slotStructs.length; slotI++) {
  61. slotStruct = slotStructs[slotI]
  62. slotStart = dayStruct.date.clone().time(0)
  63. .add(slotStruct.dayOffset, 'days')
  64. .add(slotStruct.startTime)
  65. slotEnd = dayStruct.date.clone().time(0)
  66. .add(slotStruct.dayOffset, 'days')
  67. .add(slotStruct.endTime)
  68. if (startTop === null) { // looking for the start
  69. coverage = (start - slotStart) / (slotEnd - slotStart)
  70. startTop = (coverage > 0 && coverage <= 1)
  71. ? (slotStruct.top + slotStruct.height * coverage)
  72. : null
  73. } else { // looking for the end
  74. coverage = (end - slotStart) / (slotEnd - slotStart)
  75. endTop = (coverage >= 0 && coverage < 1) // exclusive
  76. ? (slotStruct.top + slotStruct.height * coverage)
  77. : null
  78. if (endTop !== null) { // found end
  79. rects.push({
  80. left: dayStruct.left,
  81. right: dayStruct.right,
  82. top: startTop,
  83. bottom: endTop,
  84. width: dayStruct.right - dayStruct.left,
  85. height: endTop - startTop
  86. })
  87. startTop = null
  88. }
  89. }
  90. }
  91. if (startTop !== null) { // could not find the start in this day
  92. rects.push({
  93. left: dayStruct.left,
  94. right: dayStruct.right,
  95. top: startTop,
  96. bottom: slotStruct.bottom,
  97. width: dayStruct.right - dayStruct.left,
  98. height: slotStruct.bottom - startTop
  99. })
  100. startTop = slotStructs[0].top // top of next column
  101. }
  102. }
  103. return rects
  104. }
  105. function computeDays() {
  106. var dayEls = $('.fc-day-header[data-date]')
  107. var days = dayEls.map(function(i, node) {
  108. var rect = node.getBoundingClientRect()
  109. return $.extend({}, rect, {
  110. date: $.fullCalendar.moment.parseZone(
  111. $(node).data('date')
  112. )
  113. })
  114. }).get()
  115. return days
  116. }
  117. function computeSlots() {
  118. var slotEls = $('.fc-time-grid .fc-slats tr[data-time]')
  119. var slots = slotEls.map(function(i, node) {
  120. var rect = node.getBoundingClientRect()
  121. return $.extend({}, rect, {
  122. startTime: moment.duration(
  123. $(node).data('time')
  124. )
  125. })
  126. }).get()
  127. var len = slots.length
  128. if (len < 3) {
  129. console.log('need at least 3 slots')
  130. return []
  131. }
  132. var mid = Math.floor(len / 2)
  133. var i = mid - 1
  134. var standardMs = slots[mid + 1].startTime - slots[mid].startTime
  135. var ms
  136. var dayOffset = 0
  137. // iterate from one-before middle to beginning
  138. for (i = mid - 1; i >= 0; i--) {
  139. ms = slots[i + 1].startTime - slots[i].startTime
  140. // big deviation? assume moved to previous day (b/c of special minTime)
  141. if (Math.abs(ms - standardMs) > standardMs * 2) {
  142. dayOffset--
  143. slots[i].endTime = moment.duration(slots[i].startTime).add(standardMs)
  144. } else { // otherwise, current slot's end is next slot's beginning
  145. slots[i].endTime = moment.duration(slots[i + 1].startTime)
  146. }
  147. slots[i].dayOffset = dayOffset
  148. }
  149. dayOffset = 0
  150. // iterate from middle to one-before last
  151. for (i = mid; i < len - 1; i++) {
  152. ms = slots[i + 1].startTime - slots[i].startTime
  153. slots[i].dayOffset = dayOffset
  154. // big deviation? assume moved to next day (b/c of special maxTime)
  155. if (Math.abs(ms - standardMs) > standardMs * 2) {
  156. dayOffset++ // will apply to the next slotStruct
  157. slots[i].endTime = moment.duration(slots[i].startTime).add(standardMs)
  158. } else { // otherwise, current slot's end is next slot's beginning
  159. slots[i].endTime = moment.duration(slots[i + 1].startTime)
  160. }
  161. }
  162. // assume last slot has the standard duration
  163. slots[i].endTime = moment.duration(slots[i].startTime).add(standardMs)
  164. slots[i].dayOffset = dayOffset
  165. // if last slot went over the day threshold
  166. if (slots[i].endTime.as('days') > 1) {
  167. slots[i].endTime.subtract(1, 'day')
  168. slots[i].dayOffset++
  169. }
  170. return slots
  171. }