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