|
|
@@ -0,0 +1,230 @@
|
|
|
+
|
|
|
+/* Methods relate to limiting the number events for a given day on a DayGrid
|
|
|
+----------------------------------------------------------------------------------------------------------------------*/
|
|
|
+
|
|
|
+$.extend(DayGrid.prototype, {
|
|
|
+
|
|
|
+
|
|
|
+ // Limits the number of "levels" (vertically stacking layers of events) for each row of the grid.
|
|
|
+ // `levelLimit` can be false (don't limit), a number, or true (should be computed).
|
|
|
+ limitRows: function(levelLimit) {
|
|
|
+ var rowStructs = this.rowStructs || [];
|
|
|
+ var row; // row #
|
|
|
+ var rowLevelLimit;
|
|
|
+
|
|
|
+ for (row = 0; row < rowStructs.length; row++) {
|
|
|
+ this.unlimitRow(row);
|
|
|
+
|
|
|
+ if (!levelLimit) {
|
|
|
+ rowLevelLimit = false;
|
|
|
+ }
|
|
|
+ else if (typeof levelLimit === 'number') {
|
|
|
+ rowLevelLimit = levelLimit;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ rowLevelLimit = this.computeRowLevelLimit(row);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (levelLimit !== false) {
|
|
|
+ this.limitRow(row, rowLevelLimit);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // Computes the number of levels a row will accomodate without going outside its bounds.
|
|
|
+ // Assumes the row is "rigid" (maintains a constant height regardless of what is inside).
|
|
|
+ // `row` is the row number.
|
|
|
+ computeRowLevelLimit: function(row) {
|
|
|
+ var rowEl = this.rowEls.eq(row); // the containing "fake" row div
|
|
|
+ var rowHeight = rowEl.height(); // TODO: cache somehow?
|
|
|
+ var trEls = this.rowStructs[row].tbodyEl.children();
|
|
|
+ var i, trEl;
|
|
|
+
|
|
|
+ // Reveal one level <tr> at a time and stop when we find one out of bounds
|
|
|
+ for (i = 0; i < trEls.length; i++) {
|
|
|
+ trEl = trEls.eq(i).removeClass('fc-limited'); // get and reveal
|
|
|
+ if (trEl.position().top + trEl.outerHeight() > rowHeight) {
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false; // should not limit at all
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // Limits the given grid row to the maximum number of levels and injects "more" links if necessary.
|
|
|
+ // `row` is the row number.
|
|
|
+ // `levelLimit` is a number for the maximum (inclusive) number of levels allowed.
|
|
|
+ limitRow: function(row, levelLimit) {
|
|
|
+ var _this = this;
|
|
|
+ var view = this.view;
|
|
|
+ var rowStruct = this.rowStructs[row];
|
|
|
+ var moreNodes = []; // array of "more" <a> links and <td> DOM nodes
|
|
|
+ var col = 0; // col #
|
|
|
+ var cell;
|
|
|
+ var levelSegs; // array of segment objects in the last allowable level, ordered left-to-right
|
|
|
+ var cellMatrix; // a matrix (by level, then column) of all <td> jQuery elements in the row
|
|
|
+ var limitedNodes; // array of temporarily hidden level <tr> and segment <td> DOM nodes
|
|
|
+ var i, seg;
|
|
|
+ var segsBelow; // array of segment objects below `seg` in the current `col`
|
|
|
+ var totalSegsBelow; // total number of segments below `seg` in any of the columns `seg` occupies
|
|
|
+ var colSegsBelow; // array of segment arrays, below seg, one for each column (offset from segs's first column)
|
|
|
+ var td, rowspan;
|
|
|
+ var segMoreNodes; // array of "more" <td> cells that will stand-in for the current seg's cell
|
|
|
+ var j;
|
|
|
+ var moreTd, moreLink;
|
|
|
+
|
|
|
+ // Iterates through empty level cells and places "more" links inside if need be
|
|
|
+ function emptyCellsUntil(endCol) { // goes from current `col` to `endCol`
|
|
|
+ while (col < endCol) {
|
|
|
+ cell = { row: row, col: col };
|
|
|
+ segsBelow = _this.getCellSegs(cell, levelLimit);
|
|
|
+ if (segsBelow.length) {
|
|
|
+ td = cellMatrix[levelLimit - 1][col];
|
|
|
+ moreLink = _this.renderMoreLink(cell, segsBelow);
|
|
|
+ td.append(moreLink);
|
|
|
+ moreNodes.push(moreLink[0]);
|
|
|
+ }
|
|
|
+ col++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (levelLimit && levelLimit < rowStruct.segLevels.length) { // is it actually over the limit?
|
|
|
+ levelSegs = rowStruct.segLevels[levelLimit - 1];
|
|
|
+ cellMatrix = rowStruct.cellMatrix;
|
|
|
+
|
|
|
+ limitedNodes = rowStruct.tbodyEl.children().slice(levelLimit) // get level <tr> elements past the limit
|
|
|
+ .addClass('fc-limited').get(); // hide elements and get a simple DOM-nodes array
|
|
|
+
|
|
|
+ // iterate though segments in the last allowable level
|
|
|
+ for (i = 0; i < levelSegs.length; i++) {
|
|
|
+ seg = levelSegs[i];
|
|
|
+ emptyCellsUntil(seg.leftCol); // process empty cells before the segment
|
|
|
+
|
|
|
+ // determine *all* segments below `seg` that occupy the same columns
|
|
|
+ colSegsBelow = [];
|
|
|
+ totalSegsBelow = 0;
|
|
|
+ while (col <= seg.rightCol) {
|
|
|
+ cell = { row: row, col: col };
|
|
|
+ segsBelow = this.getCellSegs(cell, levelLimit);
|
|
|
+ colSegsBelow.push(segsBelow);
|
|
|
+ totalSegsBelow += segsBelow.length;
|
|
|
+ col++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (totalSegsBelow) { // do we need to replace this segment with one or many "more" links?
|
|
|
+ td = cellMatrix[levelLimit - 1][seg.leftCol]; // the segment's parent cell
|
|
|
+ rowspan = td.attr('rowspan') || 1;
|
|
|
+ segMoreNodes = [];
|
|
|
+
|
|
|
+ // make a replacement <td> for each column the segment occupies. will be one for each colspan
|
|
|
+ for (j = 0; j < colSegsBelow.length; j++) {
|
|
|
+ moreTd = $('<td class="fc-more-cell"/>').attr('rowspan', rowspan);
|
|
|
+ segsBelow = colSegsBelow[j];
|
|
|
+ cell = { row: row, col: seg.leftCol + j };
|
|
|
+ moreLink = this.renderMoreLink(cell, [ seg ].concat(segsBelow)); // count seg as hidden too
|
|
|
+ moreTd.append(moreLink);
|
|
|
+ segMoreNodes.push(moreTd[0]);
|
|
|
+ moreNodes.push(moreTd[0]);
|
|
|
+ }
|
|
|
+
|
|
|
+ td.addClass('fc-limited').after($(segMoreNodes)); // hide original <td> and inject replacements
|
|
|
+ limitedNodes.push(td[0]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ emptyCellsUntil(view.colCnt); // finish off the level
|
|
|
+ rowStruct.moreEls = $(moreNodes); // for easy undoing later
|
|
|
+ rowStruct.limitedEls = $(limitedNodes); // for easy undoing later
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // Reveals all levels and removes all "more"-related elements for a grid's row.
|
|
|
+ // `row` is a row number.
|
|
|
+ unlimitRow: function(row) {
|
|
|
+ var rowStruct = this.rowStructs[row];
|
|
|
+
|
|
|
+ if (rowStruct.moreEls) {
|
|
|
+ rowStruct.moreEls.remove();
|
|
|
+ rowStruct.moreEls = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rowStruct.limitedEls) {
|
|
|
+ rowStruct.limitedEls.removeClass('fc-limited');
|
|
|
+ rowStruct.limitedEls = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // Renders an <a> element that represents hidden event element for a cell.
|
|
|
+ // Responsible for attaching click handler as well.
|
|
|
+ renderMoreLink: function(cell, hiddenSegs) {
|
|
|
+ var _this = this;
|
|
|
+ var view = this.view;
|
|
|
+
|
|
|
+ return $('<a class="fc-more"/>')
|
|
|
+ .text(
|
|
|
+ this.getMoreLinkText(hiddenSegs.length)
|
|
|
+ )
|
|
|
+ .on('click', function() {
|
|
|
+ var date = view.cellToDate(cell);
|
|
|
+ var clickOption = view.opt('eventLimitClick');
|
|
|
+
|
|
|
+ if (typeof clickOption === 'string') { // a view name or 'auto'
|
|
|
+ view.calendar.zoomToDay(date, clickOption);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ view.trigger(
|
|
|
+ 'eventLimitClick',
|
|
|
+ null,
|
|
|
+ date,
|
|
|
+ _this.getCellDayEl(cell), // element for the cell's day
|
|
|
+ hiddenSegs,
|
|
|
+ _this.getCellSegs(cell) // all segments on that day
|
|
|
+ );
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // Generates the text that should be inside a "more" link, given the number of events it represents
|
|
|
+ getMoreLinkText: function(num) {
|
|
|
+ var view = this.view;
|
|
|
+ var opt = view.opt('eventLimitText');
|
|
|
+
|
|
|
+ if (typeof opt === 'function') {
|
|
|
+ return opt(num);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (view.opt('isRTL')) {
|
|
|
+ return opt + ' ' + num + '+';
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return '+' + num + ' ' + opt;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // Returns segments within a given cell.
|
|
|
+ // If `startLevel` is specified, returns only events including and below that level. Otherwise returns all segs.
|
|
|
+ getCellSegs: function(cell, startLevel) {
|
|
|
+ var segMatrix = this.rowStructs[cell.row].segMatrix;
|
|
|
+ var level = startLevel || 0;
|
|
|
+ var segs = [];
|
|
|
+ var seg;
|
|
|
+
|
|
|
+ while (level < segMatrix.length) {
|
|
|
+ seg = segMatrix[level][cell.col];
|
|
|
+ if (seg) {
|
|
|
+ segs.push(seg);
|
|
|
+ }
|
|
|
+ level++;
|
|
|
+ }
|
|
|
+
|
|
|
+ return segs;
|
|
|
+ }
|
|
|
+
|
|
|
+});
|