TimeGridEventRenderUtils.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. var TimeGridEventRenderUtils = {
  2. getTimeTexts: function() {
  3. return $('.fc-event').map(function(i, eventEl) {
  4. return $(eventEl).find('.fc-time').text();
  5. }).get();
  6. },
  7. /*
  8. Returns a boolean.
  9. TODO: check isStart/isEnd.
  10. */
  11. checkEventRendering: function(start, end) {
  12. start = $.fullCalendar.moment.parseZone(start);
  13. end = $.fullCalendar.moment.parseZone(end);
  14. var expectedRects = this.computeSpanRects(start, end);
  15. var eventEls = $('.fc-event'); // sorted by DOM order. not good for RTL
  16. var isMatch = this.checkEventRenderingMatch(expectedRects, eventEls);
  17. return {
  18. rects: expectedRects,
  19. els: eventEls,
  20. length: eventEls.length,
  21. isMatch: isMatch
  22. };
  23. },
  24. checkEventRenderingMatch: function(expectedRects, eventEls) {
  25. var expectedLength = expectedRects.length;
  26. var i, expectedRect;
  27. var elRect;
  28. if (eventEls.length != expectedLength) {
  29. console.log('does not match element count');
  30. return false;
  31. }
  32. for (i = 0; i < expectedLength; i++) {
  33. expectedRect = expectedRects[i];
  34. elRect = eventEls[i].getBoundingClientRect();
  35. // horizontally contained AND vertically really similar?
  36. if (!(
  37. elRect.left >= expectedRect.left &&
  38. elRect.right <= expectedRect.right &&
  39. Math.abs(elRect.top - expectedRect.top) < 1 &&
  40. Math.abs(elRect.bottom - expectedRect.bottom) < 1
  41. )) {
  42. console.log('rects do not match');
  43. return false;
  44. }
  45. }
  46. return true;
  47. },
  48. computeSpanRects: function(start, end) {
  49. var dayStructs = this.computeDays();
  50. var slotStructs = this.computeSlots();
  51. var dayI, dayStruct;
  52. var slotI, slotStruct;
  53. var coverage;
  54. var startTop = null;
  55. var endTop = null;
  56. var rects = [];
  57. for (dayI = 0; dayI < dayStructs.length; dayI++) {
  58. dayStruct = dayStructs[dayI];
  59. for (slotI = 0; slotI < slotStructs.length; slotI++) {
  60. slotStruct = slotStructs[slotI];
  61. slotStart = dayStruct.date.clone().time(0)
  62. .add(slotStruct.dayOffset, 'days')
  63. .add(slotStruct.startTime);
  64. slotEnd = dayStruct.date.clone().time(0)
  65. .add(slotStruct.dayOffset, 'days')
  66. .add(slotStruct.endTime);
  67. if (startTop === null) { // looking for the start
  68. coverage = (start - slotStart) / (slotEnd - slotStart);
  69. startTop = (coverage > 0 && coverage <= 1) ?
  70. (slotStruct.top + slotStruct.height * coverage) :
  71. null;
  72. }
  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. computeDays: function() {
  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. computeSlots: function() {
  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. }
  145. else { // otherwise, current slot's end is next slot's beginning
  146. slots[i].endTime = moment.duration(slots[i + 1].startTime);
  147. }
  148. slots[i].dayOffset = dayOffset;
  149. }
  150. dayOffset = 0;
  151. // iterate from middle to one-before last
  152. for (i = mid; i < len - 1; i++) {
  153. ms = slots[i + 1].startTime - slots[i].startTime;
  154. slots[i].dayOffset = dayOffset;
  155. // big deviation? assume moved to next day (b/c of special maxTime)
  156. if (Math.abs(ms - standardMs) > standardMs * 2) {
  157. dayOffset++; // will apply to the next slotStruct
  158. slots[i].endTime = moment.duration(slots[i].startTime).add(standardMs);
  159. }
  160. else { // otherwise, current slot's end is next slot's beginning
  161. slots[i].endTime = moment.duration(slots[i + 1].startTime);
  162. }
  163. }
  164. // assume last slot has the standard duration
  165. slots[i].endTime = moment.duration(slots[i].startTime).add(standardMs);
  166. slots[i].dayOffset = dayOffset;
  167. // if last slot went over the day threshold
  168. if (slots[i].endTime.as('days') > 1) {
  169. slots[i].endTime.subtract(1, 'day');
  170. slots[i].dayOffset++;
  171. }
  172. return slots;
  173. }
  174. };