TimeGridEventRenderUtils.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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 coverage;
  53. var startTop = null;
  54. var endTop = null;
  55. var rects = [];
  56. for (dayI = 0; dayI < dayStructs.length; dayI++) {
  57. dayStruct = dayStructs[dayI];
  58. for (slotI = 0; slotI < slotStructs.length; slotI++) {
  59. slotStruct = slotStructs[slotI];
  60. slotStart = dayStruct.date.clone().time(0)
  61. .add(slotStruct.dayOffset, 'days')
  62. .add(slotStruct.startTime);
  63. slotEnd = dayStruct.date.clone().time(0)
  64. .add(slotStruct.dayOffset, 'days')
  65. .add(slotStruct.endTime);
  66. if (startTop === null) { // looking for the start
  67. coverage = (start - slotStart) / (slotEnd - slotStart);
  68. startTop = (coverage > 0 && coverage <= 1) ?
  69. (slotStruct.top + slotStruct.height * coverage) :
  70. null;
  71. }
  72. else { // looking for the end
  73. coverage = (end - slotStart) / (slotEnd - slotStart);
  74. endTop = (coverage >= 0 && coverage < 1) ? // exclusive
  75. (slotStruct.top + slotStruct.height * coverage) :
  76. null;
  77. if (endTop !== null) { // found end
  78. rects.push({
  79. left: dayStruct.left,
  80. right: dayStruct.right,
  81. top: startTop,
  82. bottom: endTop,
  83. width: dayStruct.right - dayStruct.left,
  84. height: endTop - startTop
  85. });
  86. startTop = null;
  87. }
  88. }
  89. }
  90. if (startTop !== null) { // could not find the start in this day
  91. rects.push({
  92. left: dayStruct.left,
  93. right: dayStruct.right,
  94. top: startTop,
  95. bottom: slotStruct.bottom,
  96. width: dayStruct.right - dayStruct.left,
  97. height: slotStruct.bottom - startTop
  98. });
  99. startTop = slotStructs[0].top; // top of next column
  100. }
  101. }
  102. return rects;
  103. }
  104. function computeDays() {
  105. var dayEls = $('.fc-day-header[data-date]');
  106. var days = dayEls.map(function(i, node) {
  107. var rect = node.getBoundingClientRect();
  108. return $.extend({}, rect, {
  109. date: $.fullCalendar.moment.parseZone(
  110. $(node).data('date')
  111. )
  112. });
  113. }).get();
  114. return days;
  115. }
  116. function computeSlots() {
  117. var slotEls = $('.fc-time-grid .fc-slats tr[data-time]');
  118. var slots = slotEls.map(function(i, node) {
  119. var rect = node.getBoundingClientRect();
  120. return $.extend({}, rect, {
  121. startTime: moment.duration(
  122. $(node).data('time')
  123. )
  124. });
  125. }).get();
  126. var len = slots.length;
  127. if (len < 3) {
  128. console.log('need at least 3 slots');
  129. return [];
  130. }
  131. var mid = Math.floor(len / 2);
  132. var i = mid - 1;
  133. var standardMs = slots[mid + 1].startTime - slots[mid].startTime;
  134. var ms;
  135. var dayOffset = 0;
  136. // iterate from one-before middle to beginning
  137. for (i = mid - 1; i >= 0; i--) {
  138. ms = slots[i + 1].startTime - slots[i].startTime;
  139. // big deviation? assume moved to previous day (b/c of special minTime)
  140. if (Math.abs(ms - standardMs) > standardMs * 2) {
  141. dayOffset--;
  142. slots[i].endTime = moment.duration(slots[i].startTime).add(standardMs);
  143. }
  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. }
  159. else { // otherwise, current slot's end is next slot's beginning
  160. slots[i].endTime = moment.duration(slots[i + 1].startTime);
  161. }
  162. }
  163. // assume last slot has the standard duration
  164. slots[i].endTime = moment.duration(slots[i].startTime).add(standardMs);
  165. slots[i].dayOffset = dayOffset;
  166. // if last slot went over the day threshold
  167. if (slots[i].endTime.as('days') > 1) {
  168. slots[i].endTime.subtract(1, 'day');
  169. slots[i].dayOffset++;
  170. }
  171. return slots;
  172. }