setDefaults({ weekMode: 'fixed' }); views.month = function(element, options) { return new Grid(element, options, { render: function(date, delta, fetchEvents) { if (delta) { addMonths(date, delta); } var start = this.start = cloneDate(date, true); start.setDate(1); this.title = formatDates( start, addDays(cloneDate(this.end = addMonths(cloneDate(start), 1)), -1), strProp(options.titleFormat, 'month'), options ); addDays(this.visStart = cloneDate(start), -((start.getDay() - options.weekStart + 7) % 7)); addDays(this.visEnd = cloneDate(this.end), (7 - this.visEnd.getDay() + options.weekStart) % 7); var rowCnt = Math.round((this.visEnd - this.visStart) / (DAY_MS * 7)); if (options.weekMode == 'fixed') { addDays(this.visEnd, (6 - rowCnt) * 7); rowCnt = 6; } this.renderGrid(rowCnt, 7, strProp(options.columnFormat, 'month'), true, fetchEvents); } }); } views.basicWeek = function(element, options) { return new Grid(element, options, { render: function(date, delta, fetchEvents) { if (delta) { addDays(date, delta * 7); } this.title = formatDates( this.start = this.visStart = addDays(cloneDate(date), -((date.getDay() - options.weekStart + 7) % 7)), addDays(cloneDate(this.end = this.visEnd = addDays(cloneDate(this.start), 7)), -1), strProp(options.titleFormat, 'week'), options ); this.renderGrid(1, 7, strProp(options.columnFormat, 'week'), false, fetchEvents); } }); }; views.basicDay = function(element, options) { return new Grid(element, options, { render: function(date, delta, fetchEvents) { if (delta) { addDays(date, delta); } this.title = formatDate(date, strProp(options.titleFormat, 'day'), options); this.start = this.visStart = cloneDate(date, true); this.end = this.visEnd = addDays(cloneDate(this.start), 1); this.renderGrid(1, 1, strProp(options.columnFormat, 'day'), false, fetchEvents); } }); } // flags for [Opera] rendering bugs var tdTopBug, trTopBug, tbodyTopBug, sniffBugs = true; var tdHeightBug; var sniffedEventLeftBug, eventLeftDiff=0; function Grid(element, options, methods) { var tm, weekStart, rtl, dis, dit, // day index sign / translate rowCnt, colCnt, colWidth, thead, tbody, cachedSegs, //... // initialize superclass view = $.extend(this, viewMethods, methods, { renderGrid: renderGrid, rerenderEvents: rerenderEvents, updateSize: updateSize, eventEnd: function(event) { return event.end || cloneDate(event.start); }, visEventEnd: function(event) { if (event.end) { var end = cloneDate(event.end); return (!event.hasTime || end.getHours() || end.getMinutes()) ? addDays(end, 1) : end; }else{ return addDays(cloneDate(event.start), 1); } } }); view.init(element, options); /********************************* grid rendering *************************************/ element.addClass('fc-grid').css('position', 'relative'); if (element.disableSelection) { element.disableSelection(); } function renderGrid(r, c, colFormat, showNumbers, fetchEvents) { //console.log('renderGrid!'); rowCnt = r; colCnt = c; var month = view.start.getMonth(), today = clearTime(new Date()), s, s2, s3, i, j, d = cloneDate(view.visStart); // update option-derived variables tm = options.theme ? 'ui' : 'fc'; weekStart = options.weekStart; if (rtl = options.isRTL) { dis = -1; dit = colCnt - 1; }else{ dis = 1; dit = 0; } if (!tbody) { // first time, build all cells from scratch var table = $("").appendTo(element); s = ''; for (i=0; i" + formatDate(d, colFormat, options) + ""; // TODO: optionize //if (rtl) { // s = s2 + s; //}else{ s += s2; //} addDays(d, 1); } thead = $("" + s + "").appendTo(table); s = ""; d = cloneDate(view.visStart); for (i=0; i1 && d.getMonth() != month ? ' fc-other-month' : '') + (+d == +today ? ' fc-today '+tm+'-state-highlight' : ' fc-not-today') + "'>" + (showNumbers ? "
" + d.getDate() + "
" : '') + "
 
"; //if (rtl) { // s2 = s3 + s2; //}else{ s2 += s3; //} addDays(d, 1); } s += "
" + s2 + ""; } tbody = $(s + "").appendTo(table); tbody.find('td').click(dayClick); }else{ // NOT first time, reuse as many cells as possible view.clearEvents(); var prevRowCnt = tbody.find('tr').length; if (rowCnt < prevRowCnt) { tbody.find('tr:gt(' + (rowCnt-1) + ')').remove(); // remove extra rows } else if (rowCnt > prevRowCnt) { s = ''; for (i=prevRowCnt; i" + (showNumbers ? "
" : '') + "
 
" + ""; //if (rtl) { // s2 = s3 + s2; //}else{ s2 += s3; //} } s += "
" + s2 + ""; } tbody.append(s); } tbody.find('td.fc-new').removeClass('fc-new').click(dayClick); // re-label and re-class existing cells tbody.find('tr').each(function() { for (i=0; i 1) { if (d.getMonth() == month) { td.removeClass('fc-other-month'); }else{ td.addClass('fc-other-month'); } } if (+d == +today) { td.removeClass('fc-not-today') .addClass('fc-today') .addClass(tm + '-state-highlight'); }else{ td.addClass('fc-not-today') .removeClass('fc-today') .removeClass(tm + '-state-highlight'); } td.find('div.fc-day-number').text(d.getDate()); addDays(d, 1); } }); if (colCnt == 1) { var startDay = this.visStart.getDay(); var td = tbody.find('td')[0]; td.className = td.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[startDay]); thead.find('th').text(formatDate(this.start, colFormat, options)); } } updateSize(); fetchEvents(renderEvents); }; function updateSize() { var width = element.width(); var height = Math.round(width / options.aspectRatio); setOuterWidth( thead.find('th').slice(0, -1), colWidth = Math.floor(width / colCnt) ); var leftTDs = tbody.find('tr td:first-child'); var tbodyHeight = height - thead.height(); var rowHeight1, rowHeight2; if (options.weekMode == 'variable') { rowHeight1 = rowHeight2 = Math.floor(tbodyHeight / (rowCnt==1 ? 2 : 6)); }else{ rowHeight1 = Math.floor(tbodyHeight / rowCnt); rowHeight2 = tbodyHeight - rowHeight1*(rowCnt-1); } if (sniffBugs) { // nasty bugs in opera 9.25 // position() returning relative to direct parent var tr = tbody.find('tr:first'); var td = tr.find('td:first'); var trTop = tr.position().top; var tdTop = td.position().top; tdTopBug = tdTop < 0; trTopBug = trTop != tdTop; tbodyTopBug = tbody.position().top != trTop; sniffBugs = false; // td.height(rowHeight1); tdHeightBug = rowHeight1 != td.height(); } if (tdHeightBug) { leftTDs.slice(0, -1).height(rowHeight1); leftTDs.slice(-1).height(rowHeight2); }else{ setOuterHeight(leftTDs.slice(0, -1), rowHeight1); setOuterHeight(leftTDs.slice(-1), rowHeight2); } //alert(tbodyHeight + ' === ' + tbody.height()); } /******************************** event rendering *****************************/ function renderEvents(events) { view.reportEvents(events); renderSegs(cachedSegs = compileSegs(events)); // view.visibleEvents( } function rerenderEvents(skipCompile) { //console.log('rerender events'); view.clearEvents(); if (skipCompile) { renderSegs(cachedSegs); }else{ renderEvents(view.cachedEvents); } } function compileSegs(events) { var d1 = cloneDate(view.visStart); var d2 = addDays(cloneDate(d1), colCnt); var rows = []; for (var i=0; i") .append(eventAnchor = $("") .append(event.hasTime ? $("") .html(formatDate(event.start, options.eventTimeFormat, options)) : null) .append($("") .text(event.title))); if (event.url) { eventAnchor.attr('href', event.url); } triggerRes = view.trigger('eventRender', event, event, eventElement); if (triggerRes !== false) { if (triggerRes && typeof triggerRes != 'boolean') { eventElement = $(triggerRes); } eventElement .css({ position: 'absolute', top: top, left: left1 + eventLeftDiff, zIndex: 3 }) .appendTo(element); setOuterWidth(eventElement, left2-left1, true); if (!sniffedEventLeftBug) { if (rtl) { eventLeftDiff = left1 - eventElement.position().left; if (eventLeftDiff) { eventElement.css('left', left1 + eventLeftDiff); } } sniffedEventLeftBug = true; } eventElementHandlers(event, eventElement); if (event.editable || typeof event.editable == 'undefined' && options.editable) { draggableEvent(event, eventElement); resizableEvent(event, eventElement); } view.reportEventElement(event, eventElement); levelHeight = Math.max(levelHeight, eventElement.outerHeight(true)); } } weekHeight += levelHeight; top += levelHeight; } innerDiv.height(weekHeight); } } function eventElementHandlers(event, eventElement) { eventElement .click(function(ev) { if (!eventElement.hasClass('ui-draggable-dragging')) { return view.trigger('eventClick', this, event, ev); } }) .hover( function(ev) { view.trigger('eventMouseover', this, event, ev); }, function(ev) { view.trigger('eventMouseover', this, event, ev); } ); } /***************************** draggable *********************************/ function draggableEvent(event, eventElement) { if (!options.disableDragging && eventElement.draggable) { var matrix; eventElement.draggable({ zIndex: 4, delay: 50, opacity: options.dragOpacity, revertDuration: options.dragRevertDuration, start: function(ev, ui) { matrix = new HoverMatrix(function(cell) { eventElement.draggable('option', 'revert', !cell || !cell.rowDelta && !cell.colDelta); if (cell) { view.showOverlay(cell); }else{ view.hideOverlay(); } }); tbody.find('tr').each(function() { matrix.row(this); }); var tds = tbody.find('tr:first td'); if (rtl) { tds = $(tds.get().reverse()); } tds.each(function() { matrix.col(this); }); matrix.start(); view.hideEvents(event, eventElement); view.trigger('eventDragStart', eventElement, event, ev, ui); }, drag: function(ev) { matrix.mouse(ev.pageX, ev.pageY); }, stop: function(ev, ui) { view.hideOverlay(); view.trigger('eventDragStop', eventElement, event, ev, ui); var cell = matrix.cell; if (!cell || !cell.rowDelta && !cell.colDelta) { view.showEvents(event, eventElement); }else{ var dayDelta = cell.rowDelta*7 + cell.colDelta*dis; view.moveEvent(event, dayDelta); view.trigger('eventDrop', this, event, dayDelta, 0, ev, ui); rerenderEvents(); } } }); } } /******************************* resizable *****************************/ function resizableEvent(event, eventElement) { if (!options.disableResizing && eventElement.resizable) { eventElement.resizable({ handles: rtl ? 'w' : 'e', grid: [colWidth, 0], containment: element, start: function(ev, ui) { eventElement.css('z-index', 4); view.hideEvents(event, eventElement); view.trigger('eventResizeStart', this, event, ev, ui); }, stop: function(ev, ui) { view.trigger('eventResizeStop', this, event, ev, ui); var dayDelta = Math.round((Math.max(colWidth, ui.size.width) - ui.originalSize.width) / colWidth); if (dayDelta) { view.resizeEvent(event, dayDelta); view.trigger('eventResize', this, event, dayDelta, 0, ev, ui); rerenderEvents(); }else{ view.showEvents(event, eventElement); } eventElement.css('z-index', 3); } }); } } // function dayClick() { var dayIndex = parseInt(this.className.match(/fc\-day(\d+)/)[1]); var date = addDays(cloneDate(view.visStart), dayIndex); view.trigger('dayClick', this, date); } };