BasicView.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /* An abstract class for the "basic" views, as well as month view. Renders one or more rows of day cells.
  2. ----------------------------------------------------------------------------------------------------------------------*/
  3. // It is a manager for a DayGrid subcomponent, which does most of the heavy lifting.
  4. // It is responsible for managing width/height.
  5. var BasicView = View.extend({
  6. dayGrid: null, // the main subcomponent that does most of the heavy lifting
  7. dayNumbersVisible: false, // display day numbers on each day cell?
  8. weekNumbersVisible: false, // display week numbers along the side?
  9. weekNumberWidth: null, // width of all the week-number cells running down the side
  10. headRowEl: null, // the fake row element of the day-of-week header
  11. initialize: function() {
  12. this.dayGrid = new DayGrid(this);
  13. this.coordMap = this.dayGrid.coordMap; // the view's date-to-cell mapping is identical to the subcomponent's
  14. },
  15. // Sets the display range and computes all necessary dates
  16. setRange: function(range) {
  17. View.prototype.setRange.call(this, range); // call the super-method
  18. this.dayGrid.breakOnWeeks = /year|month|week/.test(this.intervalUnit); // do before setRange
  19. this.dayGrid.setRange(range);
  20. },
  21. // Compute the value to feed into setRange. Overrides superclass.
  22. computeRange: function(date) {
  23. var range = View.prototype.computeRange.call(this, date); // get value from the super-method
  24. // year and month views should be aligned with weeks. this is already done for week
  25. if (/year|month/.test(range.intervalUnit)) {
  26. range.start.startOf('week');
  27. range.start = this.skipHiddenDays(range.start);
  28. // make end-of-week if not already
  29. if (range.end.weekday()) {
  30. range.end.add(1, 'week').startOf('week');
  31. range.end = this.skipHiddenDays(range.end, -1, true); // exclusively move backwards
  32. }
  33. }
  34. return range;
  35. },
  36. // Renders the view into `this.el`, which should already be assigned
  37. render: function() {
  38. this.dayNumbersVisible = this.dayGrid.rowCnt > 1; // TODO: make grid responsible
  39. this.weekNumbersVisible = this.opt('weekNumbers');
  40. this.dayGrid.numbersVisible = this.dayNumbersVisible || this.weekNumbersVisible;
  41. this.el.addClass('fc-basic-view').html(this.renderHtml());
  42. this.headRowEl = this.el.find('thead .fc-row');
  43. this.scrollerEl = this.el.find('.fc-day-grid-container');
  44. this.dayGrid.coordMap.containerEl = this.scrollerEl; // constrain clicks/etc to the dimensions of the scroller
  45. this.dayGrid.setElement(this.el.find('.fc-day-grid'));
  46. this.dayGrid.renderDates(this.hasRigidRows());
  47. },
  48. // Unrenders the content of the view. Since we haven't separated skeleton rendering from date rendering,
  49. // always completely kill the dayGrid's rendering.
  50. destroy: function() {
  51. this.dayGrid.destroyDates();
  52. this.dayGrid.removeElement();
  53. },
  54. renderBusinessHours: function() {
  55. this.dayGrid.renderBusinessHours();
  56. },
  57. // Builds the HTML skeleton for the view.
  58. // The day-grid component will render inside of a container defined by this HTML.
  59. renderHtml: function() {
  60. return '' +
  61. '<table>' +
  62. '<thead class="fc-head">' +
  63. '<tr>' +
  64. '<td class="' + this.widgetHeaderClass + '">' +
  65. this.dayGrid.headHtml() + // render the day-of-week headers
  66. '</td>' +
  67. '</tr>' +
  68. '</thead>' +
  69. '<tbody class="fc-body">' +
  70. '<tr>' +
  71. '<td class="' + this.widgetContentClass + '">' +
  72. '<div class="fc-day-grid-container">' +
  73. '<div class="fc-day-grid"/>' +
  74. '</div>' +
  75. '</td>' +
  76. '</tr>' +
  77. '</tbody>' +
  78. '</table>';
  79. },
  80. // Generates the HTML that will go before the day-of week header cells.
  81. // Queried by the DayGrid subcomponent when generating rows. Ordering depends on isRTL.
  82. headIntroHtml: function() {
  83. if (this.weekNumbersVisible) {
  84. return '' +
  85. '<th class="fc-week-number ' + this.widgetHeaderClass + '" ' + this.weekNumberStyleAttr() + '>' +
  86. '<span>' + // needed for matchCellWidths
  87. htmlEscape(this.opt('weekNumberTitle')) +
  88. '</span>' +
  89. '</th>';
  90. }
  91. },
  92. // Generates the HTML that will go before content-skeleton cells that display the day/week numbers.
  93. // Queried by the DayGrid subcomponent. Ordering depends on isRTL.
  94. numberIntroHtml: function(row) {
  95. if (this.weekNumbersVisible) {
  96. return '' +
  97. '<td class="fc-week-number" ' + this.weekNumberStyleAttr() + '>' +
  98. '<span>' + // needed for matchCellWidths
  99. this.dayGrid.getCell(row, 0).start.format('w') +
  100. '</span>' +
  101. '</td>';
  102. }
  103. },
  104. // Generates the HTML that goes before the day bg cells for each day-row.
  105. // Queried by the DayGrid subcomponent. Ordering depends on isRTL.
  106. dayIntroHtml: function() {
  107. if (this.weekNumbersVisible) {
  108. return '<td class="fc-week-number ' + this.widgetContentClass + '" ' +
  109. this.weekNumberStyleAttr() + '></td>';
  110. }
  111. },
  112. // Generates the HTML that goes before every other type of row generated by DayGrid. Ordering depends on isRTL.
  113. // Affects helper-skeleton and highlight-skeleton rows.
  114. introHtml: function() {
  115. if (this.weekNumbersVisible) {
  116. return '<td class="fc-week-number" ' + this.weekNumberStyleAttr() + '></td>';
  117. }
  118. },
  119. // Generates the HTML for the <td>s of the "number" row in the DayGrid's content skeleton.
  120. // The number row will only exist if either day numbers or week numbers are turned on.
  121. numberCellHtml: function(cell) {
  122. var date = cell.start;
  123. var classes;
  124. if (!this.dayNumbersVisible) { // if there are week numbers but not day numbers
  125. return '<td/>'; // will create an empty space above events :(
  126. }
  127. classes = this.dayGrid.getDayClasses(date);
  128. classes.unshift('fc-day-number');
  129. return '' +
  130. '<td class="' + classes.join(' ') + '" data-date="' + date.format() + '">' +
  131. date.date() +
  132. '</td>';
  133. },
  134. // Generates an HTML attribute string for setting the width of the week number column, if it is known
  135. weekNumberStyleAttr: function() {
  136. if (this.weekNumberWidth !== null) {
  137. return 'style="width:' + this.weekNumberWidth + 'px"';
  138. }
  139. return '';
  140. },
  141. // Determines whether each row should have a constant height
  142. hasRigidRows: function() {
  143. var eventLimit = this.opt('eventLimit');
  144. return eventLimit && typeof eventLimit !== 'number';
  145. },
  146. /* Dimensions
  147. ------------------------------------------------------------------------------------------------------------------*/
  148. // Refreshes the horizontal dimensions of the view
  149. updateWidth: function() {
  150. if (this.weekNumbersVisible) {
  151. // Make sure all week number cells running down the side have the same width.
  152. // Record the width for cells created later.
  153. this.weekNumberWidth = matchCellWidths(
  154. this.el.find('.fc-week-number')
  155. );
  156. }
  157. },
  158. // Adjusts the vertical dimensions of the view to the specified values
  159. setHeight: function(totalHeight, isAuto) {
  160. var eventLimit = this.opt('eventLimit');
  161. var scrollerHeight;
  162. // reset all heights to be natural
  163. unsetScroller(this.scrollerEl);
  164. uncompensateScroll(this.headRowEl);
  165. this.dayGrid.destroySegPopover(); // kill the "more" popover if displayed
  166. // is the event limit a constant level number?
  167. if (eventLimit && typeof eventLimit === 'number') {
  168. this.dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after
  169. }
  170. scrollerHeight = this.computeScrollerHeight(totalHeight);
  171. this.setGridHeight(scrollerHeight, isAuto);
  172. // is the event limit dynamically calculated?
  173. if (eventLimit && typeof eventLimit !== 'number') {
  174. this.dayGrid.limitRows(eventLimit); // limit the levels after the grid's row heights have been set
  175. }
  176. if (!isAuto && setPotentialScroller(this.scrollerEl, scrollerHeight)) { // using scrollbars?
  177. compensateScroll(this.headRowEl, getScrollbarWidths(this.scrollerEl));
  178. // doing the scrollbar compensation might have created text overflow which created more height. redo
  179. scrollerHeight = this.computeScrollerHeight(totalHeight);
  180. this.scrollerEl.height(scrollerHeight);
  181. }
  182. },
  183. // Sets the height of just the DayGrid component in this view
  184. setGridHeight: function(height, isAuto) {
  185. if (isAuto) {
  186. undistributeHeight(this.dayGrid.rowEls); // let the rows be their natural height with no expanding
  187. }
  188. else {
  189. distributeHeight(this.dayGrid.rowEls, height, true); // true = compensate for height-hogging rows
  190. }
  191. },
  192. /* Events
  193. ------------------------------------------------------------------------------------------------------------------*/
  194. // Renders the given events onto the view and populates the segments array
  195. renderEvents: function(events) {
  196. this.dayGrid.renderEvents(events);
  197. this.updateHeight(); // must compensate for events that overflow the row
  198. },
  199. // Retrieves all segment objects that are rendered in the view
  200. getEventSegs: function() {
  201. return this.dayGrid.getEventSegs();
  202. },
  203. // Unrenders all event elements and clears internal segment data
  204. destroyEvents: function() {
  205. this.dayGrid.destroyEvents();
  206. // we DON'T need to call updateHeight() because:
  207. // A) a renderEvents() call always happens after this, which will eventually call updateHeight()
  208. // B) in IE8, this causes a flash whenever events are rerendered
  209. },
  210. /* Dragging (for both events and external elements)
  211. ------------------------------------------------------------------------------------------------------------------*/
  212. // A returned value of `true` signals that a mock "helper" event has been rendered.
  213. renderDrag: function(dropLocation, seg) {
  214. return this.dayGrid.renderDrag(dropLocation, seg);
  215. },
  216. destroyDrag: function() {
  217. this.dayGrid.destroyDrag();
  218. },
  219. /* Selection
  220. ------------------------------------------------------------------------------------------------------------------*/
  221. // Renders a visual indication of a selection
  222. renderSelection: function(range) {
  223. this.dayGrid.renderSelection(range);
  224. },
  225. // Unrenders a visual indications of a selection
  226. destroySelection: function() {
  227. this.dayGrid.destroySelection();
  228. }
  229. });