BasicView.js 12 KB

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