BasicView.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. setDefaults({
  2. weekMode: 'fixed'
  3. });
  4. function BasicView(element, calendar, viewName) {
  5. var t = this;
  6. // exports
  7. t.renderBasic = renderBasic;
  8. t.setHeight = setHeight;
  9. t.setWidth = setWidth;
  10. t.renderDayOverlay = renderDayOverlay;
  11. t.defaultSelectionEnd = defaultSelectionEnd;
  12. t.renderSelection = renderSelection;
  13. t.clearSelection = clearSelection;
  14. t.reportDayClick = reportDayClick; // for selection (kinda hacky)
  15. t.dragStart = dragStart;
  16. t.dragStop = dragStop;
  17. t.defaultEventEnd = defaultEventEnd;
  18. t.getHoverListener = function() { return hoverListener };
  19. t.colContentLeft = colContentLeft;
  20. t.colContentRight = colContentRight;
  21. t.dayOfWeekCol = dayOfWeekCol;
  22. t.dateCell = dateCell;
  23. t.cellDate = cellDate;
  24. t.cellIsAllDay = function() { return true };
  25. t.allDayRow = allDayRow;
  26. t.allDayBounds = allDayBounds;
  27. t.getRowCnt = function() { return rowCnt };
  28. t.getColCnt = function() { return colCnt };
  29. t.getColWidth = function() { return colWidth };
  30. t.getDaySegmentContainer = function() { return daySegmentContainer };
  31. // imports
  32. View.call(t, element, calendar, viewName);
  33. OverlayManager.call(t);
  34. SelectionManager.call(t);
  35. BasicEventRenderer.call(t);
  36. var opt = t.opt;
  37. var trigger = t.trigger;
  38. var clearEvents = t.clearEvents;
  39. var renderOverlay = t.renderOverlay;
  40. var clearOverlays = t.clearOverlays;
  41. var daySelectionMousedown = t.daySelectionMousedown;
  42. var formatDate = calendar.formatDate;
  43. // locals
  44. var head;
  45. var headCells;
  46. var body;
  47. var bodyRows;
  48. var bodyCells;
  49. var bodyFirstCells;
  50. var bodyCellTopInners;
  51. var daySegmentContainer;
  52. var viewWidth;
  53. var viewHeight;
  54. var colWidth;
  55. var weekNumberWidth;
  56. var rowCnt, colCnt;
  57. var coordinateGrid;
  58. var hoverListener;
  59. var colContentPositions;
  60. var rtl, dis, dit;
  61. var firstDay;
  62. var nwe; // no weekends? a 0 or 1 for easy computations
  63. var tm;
  64. var colFormat;
  65. var showWeekNumbers;
  66. var weekNumberTitle;
  67. var weekNumberFormat;
  68. /* Rendering
  69. ------------------------------------------------------------*/
  70. disableTextSelection(element.addClass('fc-grid'));
  71. function renderBasic(maxr, r, c, showNumbers) {
  72. rowCnt = r;
  73. colCnt = c;
  74. updateOptions();
  75. var firstTime = !body;
  76. if (firstTime) {
  77. buildSkeleton(maxr, showNumbers);
  78. }else{
  79. clearEvents();
  80. }
  81. updateCells(firstTime);
  82. }
  83. function updateOptions() {
  84. rtl = opt('isRTL');
  85. if (rtl) {
  86. dis = -1;
  87. dit = colCnt - 1;
  88. }else{
  89. dis = 1;
  90. dit = 0;
  91. }
  92. firstDay = opt('firstDay');
  93. nwe = opt('weekends') ? 0 : 1;
  94. tm = opt('theme') ? 'ui' : 'fc';
  95. colFormat = opt('columnFormat');
  96. // week # options. (TODO: bad, logic also in other views)
  97. showWeekNumbers = opt('weekNumbers');
  98. weekNumberTitle = opt('weekNumberTitle');
  99. if (opt('weekNumberCalculation') != 'iso') {
  100. weekNumberFormat = "w";
  101. }
  102. else {
  103. weekNumberFormat = "W";
  104. }
  105. }
  106. function buildSkeleton(maxRowCnt, showNumbers) {
  107. var s;
  108. var headerClass = tm + "-widget-header";
  109. var contentClass = tm + "-widget-content";
  110. var i, j;
  111. var table;
  112. s =
  113. "<table class='fc-border-separate' style='width:100%' cellspacing='0'>" +
  114. "<thead>" +
  115. "<tr>";
  116. if (showWeekNumbers) {
  117. s +=
  118. "<th class='fc-week-number " + headerClass + "'/>";
  119. }
  120. for (i=0; i<colCnt; i++) {
  121. s +=
  122. "<th class='fc- " + headerClass + "'/>"; // need fc- for setDayID
  123. }
  124. s +=
  125. "</tr>" +
  126. "</thead>" +
  127. "<tbody>";
  128. for (i=0; i<maxRowCnt; i++) {
  129. s +=
  130. "<tr class='fc-week" + i + "'>";
  131. if (showWeekNumbers) {
  132. s +=
  133. "<td class='fc-week-number " + contentClass + "'><div></div></td>";
  134. }
  135. for (j=0; j<colCnt; j++) {
  136. s +=
  137. "<td class='fc- " + contentClass + " fc-day" + (i*colCnt+j) + "'>" + // need fc- for setDayID
  138. "<div>" +
  139. (showNumbers ?
  140. "<div class='fc-day-number'/>" :
  141. ''
  142. ) +
  143. "<div class='fc-day-content'>" +
  144. "<div style='position:relative'>&nbsp;</div>" +
  145. "</div>" +
  146. "</div>" +
  147. "</td>";
  148. }
  149. s +=
  150. "</tr>";
  151. }
  152. s +=
  153. "</tbody>" +
  154. "</table>";
  155. table = $(s).appendTo(element);
  156. head = table.find('thead');
  157. headCells = head.find('th:not(.fc-week-number)');
  158. body = table.find('tbody');
  159. bodyRows = body.find('tr');
  160. bodyCells = body.find('td').filter(':not(.fc-week-number)');
  161. bodyFirstCells = bodyCells.filter(':first-child, td.fc-week-number + *'); // either first cell in each row, or immediately following week #
  162. bodyCellTopInners = bodyRows.eq(0).find('div.fc-day-content div');
  163. markFirstLast(head.add(head.find('tr'))); // marks first+last tr/th's
  164. markFirstLast(bodyRows); // marks first+last td's
  165. bodyRows.eq(0).addClass('fc-first'); // fc-last is done in updateCells
  166. dayBind(bodyCells);
  167. daySegmentContainer =
  168. $("<div style='position:absolute;z-index:8;top:0;left:0'/>")
  169. .appendTo(element);
  170. }
  171. function updateCells(firstTime) {
  172. var dowDirty = firstTime || rowCnt == 1; // could the cells' day-of-weeks need updating?
  173. var month = t.start.getMonth();
  174. var today = clearTime(new Date());
  175. var cell;
  176. var date;
  177. var row;
  178. if (showWeekNumbers) {
  179. head.find('.fc-week-number').text(weekNumberTitle);
  180. }
  181. if (dowDirty) {
  182. headCells.each(function(i, _cell) {
  183. cell = $(_cell);
  184. date = indexDate(i);
  185. cell.html(formatDate(date, colFormat));
  186. setDayID(cell, date);
  187. });
  188. }
  189. bodyCells.each(function(i, _cell) {
  190. cell = $(_cell);
  191. date = indexDate(i);
  192. if (date.getMonth() == month) {
  193. cell.removeClass('fc-other-month');
  194. }else{
  195. cell.addClass('fc-other-month');
  196. }
  197. if (+date == +today) {
  198. cell.addClass(tm + '-state-highlight fc-today');
  199. }else{
  200. cell.removeClass(tm + '-state-highlight fc-today');
  201. }
  202. cell.find('div.fc-day-number').text(date.getDate());
  203. cell.attr('data-date', $.fullCalendar.formatDate(date, "yyyyMMdd"));
  204. if (dowDirty) {
  205. setDayID(cell, date);
  206. }
  207. trigger('dayRender', t, date, cell);
  208. });
  209. bodyRows.each(function(i, _row) {
  210. row = $(_row);
  211. if (i < rowCnt) {
  212. if (showWeekNumbers) {
  213. var weekStartDate = indexDate(i*7);
  214. row.find('.fc-week-number > div').text(
  215. formatDate(weekStartDate, weekNumberFormat)
  216. );
  217. }
  218. row.show();
  219. if (i == rowCnt-1) {
  220. row.addClass('fc-last');
  221. }else{
  222. row.removeClass('fc-last');
  223. }
  224. }else{
  225. row.hide();
  226. }
  227. });
  228. }
  229. function setHeight(height) {
  230. viewHeight = height;
  231. var bodyHeight = viewHeight - head.height();
  232. var rowHeight;
  233. var rowHeightLast;
  234. var cell;
  235. if (opt('weekMode') == 'variable') {
  236. rowHeight = rowHeightLast = Math.floor(bodyHeight / (rowCnt==1 ? 2 : 6));
  237. }else{
  238. rowHeight = Math.floor(bodyHeight / rowCnt);
  239. rowHeightLast = bodyHeight - rowHeight * (rowCnt-1);
  240. }
  241. bodyFirstCells.each(function(i, _cell) {
  242. if (i < rowCnt) {
  243. cell = $(_cell);
  244. setMinHeight(
  245. cell.find('> div'),
  246. (i==rowCnt-1 ? rowHeightLast : rowHeight) - vsides(cell)
  247. );
  248. }
  249. });
  250. }
  251. function setWidth(width) {
  252. viewWidth = width;
  253. colContentPositions.clear();
  254. weekNumberWidth = 0;
  255. if (showWeekNumbers) {
  256. weekNumberWidth = head.find('th.fc-week-number').outerWidth();
  257. }
  258. colWidth = Math.floor((viewWidth - weekNumberWidth) / colCnt);
  259. setOuterWidth(headCells.slice(0, -1), colWidth);
  260. }
  261. /* Day clicking and binding
  262. -----------------------------------------------------------*/
  263. function dayBind(days) {
  264. days.click(dayClick)
  265. .mousedown(daySelectionMousedown);
  266. }
  267. function dayClick(ev) {
  268. if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
  269. var index = parseInt(this.className.match(/fc\-day(\d+)/)[1]); // TODO: maybe use .data
  270. var date = indexDate(index);
  271. trigger('dayClick', this, date, true, ev);
  272. }
  273. }
  274. /* Semi-transparent Overlay Helpers
  275. ------------------------------------------------------*/
  276. function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive
  277. if (refreshCoordinateGrid) {
  278. coordinateGrid.build();
  279. }
  280. var rowStart = cloneDate(t.visStart);
  281. var rowEnd = addDays(cloneDate(rowStart), colCnt);
  282. for (var i=0; i<rowCnt; i++) {
  283. var stretchStart = new Date(Math.max(rowStart, overlayStart));
  284. var stretchEnd = new Date(Math.min(rowEnd, overlayEnd));
  285. if (stretchStart < stretchEnd) {
  286. var colStart, colEnd;
  287. if (rtl) {
  288. colStart = dayDiff(stretchEnd, rowStart)*dis+dit+1;
  289. colEnd = dayDiff(stretchStart, rowStart)*dis+dit+1;
  290. }else{
  291. colStart = dayDiff(stretchStart, rowStart);
  292. colEnd = dayDiff(stretchEnd, rowStart);
  293. }
  294. dayBind(
  295. renderCellOverlay(i, colStart, i, colEnd-1)
  296. );
  297. }
  298. addDays(rowStart, 7);
  299. addDays(rowEnd, 7);
  300. }
  301. }
  302. function renderCellOverlay(row0, col0, row1, col1) { // row1,col1 is inclusive
  303. var rect = coordinateGrid.rect(row0, col0, row1, col1, element);
  304. return renderOverlay(rect, element);
  305. }
  306. /* Selection
  307. -----------------------------------------------------------------------*/
  308. function defaultSelectionEnd(startDate, allDay) {
  309. return cloneDate(startDate);
  310. }
  311. function renderSelection(startDate, endDate, allDay) {
  312. renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); // rebuild every time???
  313. }
  314. function clearSelection() {
  315. clearOverlays();
  316. }
  317. function reportDayClick(date, allDay, ev) {
  318. var cell = dateCell(date);
  319. var _element = bodyCells[cell.row*colCnt + cell.col];
  320. trigger('dayClick', _element, date, allDay, ev);
  321. }
  322. /* External Dragging
  323. -----------------------------------------------------------------------*/
  324. function dragStart(_dragElement, ev, ui) {
  325. hoverListener.start(function(cell) {
  326. clearOverlays();
  327. if (cell) {
  328. renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
  329. }
  330. }, ev);
  331. }
  332. function dragStop(_dragElement, ev, ui) {
  333. var cell = hoverListener.stop();
  334. clearOverlays();
  335. if (cell) {
  336. var d = cellDate(cell);
  337. trigger('drop', _dragElement, d, true, ev, ui);
  338. }
  339. }
  340. /* Utilities
  341. --------------------------------------------------------*/
  342. function defaultEventEnd(event) {
  343. return cloneDate(event.start);
  344. }
  345. coordinateGrid = new CoordinateGrid(function(rows, cols) {
  346. var e, n, p;
  347. headCells.each(function(i, _e) {
  348. e = $(_e);
  349. n = e.offset().left;
  350. if (i) {
  351. p[1] = n;
  352. }
  353. p = [n];
  354. cols[i] = p;
  355. });
  356. p[1] = n + e.outerWidth();
  357. bodyRows.each(function(i, _e) {
  358. if (i < rowCnt) {
  359. e = $(_e);
  360. n = e.offset().top;
  361. if (i) {
  362. p[1] = n;
  363. }
  364. p = [n];
  365. rows[i] = p;
  366. }
  367. });
  368. p[1] = n + e.outerHeight();
  369. });
  370. hoverListener = new HoverListener(coordinateGrid);
  371. colContentPositions = new HorizontalPositionCache(function(col) {
  372. return bodyCellTopInners.eq(col);
  373. });
  374. function colContentLeft(col) {
  375. return colContentPositions.left(col);
  376. }
  377. function colContentRight(col) {
  378. return colContentPositions.right(col);
  379. }
  380. function dateCell(date) {
  381. return {
  382. row: Math.floor(dayDiff(date, t.visStart) / 7),
  383. col: dayOfWeekCol(date.getDay())
  384. };
  385. }
  386. function cellDate(cell) {
  387. return _cellDate(cell.row, cell.col);
  388. }
  389. function _cellDate(row, col) {
  390. return addDays(cloneDate(t.visStart), row*7 + col*dis+dit);
  391. // what about weekends in middle of week?
  392. }
  393. function indexDate(index) {
  394. return _cellDate(Math.floor(index/colCnt), index%colCnt);
  395. }
  396. function dayOfWeekCol(dayOfWeek) {
  397. return ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt) * dis + dit;
  398. }
  399. function allDayRow(i) {
  400. return bodyRows.eq(i);
  401. }
  402. function allDayBounds(i) {
  403. var left = 0;
  404. if (showWeekNumbers) {
  405. left += weekNumberWidth;
  406. }
  407. return {
  408. left: left,
  409. right: viewWidth
  410. };
  411. }
  412. }