diff --git a/src/Calendar.js b/src/Calendar.js index 972b1f0..aff7f01 100644 --- a/src/Calendar.js +++ b/src/Calendar.js @@ -43,10 +43,8 @@ function Calendar(element, options, eventSources) { var content; var tm; // for making theme classes var currentView; - var viewInstances = {}; var elementOuterWidth; var suggestedViewHeight; - var absoluteViewElement; var resizeUID = 0; var ignoreWindowResize = 0; var date = new Date(); @@ -65,10 +63,10 @@ function Calendar(element, options, eventSources) { function render(inc) { if (!content) { initialRender(); - }else{ + } + else { + // mainly for the public API calcSize(); - markSizesDirty(); - markEventsDirty(); renderView(inc); } } @@ -86,13 +84,16 @@ function Calendar(element, options, eventSources) { if (options.theme) { element.addClass('ui-widget'); } + content = $("
") .prependTo(element); + header = new Header(t, options); headerElement = header.render(); if (headerElement) { element.prepend(headerElement); } + changeView(options.defaultView); if (options.handleWindowResize) { @@ -140,132 +141,88 @@ function Calendar(element, options, eventSources) { /* View Rendering -----------------------------------------------------------------------------*/ - // TODO: improve view switching (still weird transition in IE, and FF has whiteout problem) - + function changeView(newViewName) { if (!currentView || newViewName != currentView.name) { - ignoreWindowResize++; // because setMinHeight might change the height before render (and subsequently setSize) is reached - - unselect(); - - var oldView = currentView; - var newViewElement; - - if (oldView) { - (oldView.beforeHide || noop)(); // called before changing min-height. if called after, scroll state is reset (in Opera) - setMinHeight(content, content.height()); - oldView.element.hide(); - }else{ - setMinHeight(content, 1); // needs to be 1 (not 0) for IE7, or else view dimensions miscalculated - } - content.css('overflow', 'hidden'); - - currentView = viewInstances[newViewName]; - if (currentView) { - currentView.element.show(); - }else{ - currentView = viewInstances[newViewName] = new fcViews[newViewName]( - newViewElement = absoluteViewElement = - $("") - .appendTo(content), - t // the calendar object - ); - } - - if (oldView) { - header.deactivateButton(oldView.name); - } - header.activateButton(newViewName); - - renderView(); // after height has been set, will make absoluteViewElement's position=relative, then set to null - - content.css('overflow', ''); - if (oldView) { - setMinHeight(content, 1); - } - - if (!newViewElement) { - (currentView.afterShow || noop)(); // called after setting min-height/overflow, so in final scroll state (for Opera) - } - + ignoreWindowResize++; + _changeView(newViewName); ignoreWindowResize--; } } - - - + + + function _changeView(newViewName) { + + if (currentView) { + freezeContentHeight(); + currentView.element.remove(); + header.deactivateButton(currentView.name); + } + + header.activateButton(newViewName); + + currentView = new fcViews[newViewName]( + $("") + .appendTo(content), + t // the calendar object + ); + + _renderView(); + unfreezeContentHeight(); + } + + function renderView(inc) { if (elementVisible()) { - ignoreWindowResize++; // because renderEvents might temporarily change the height before setSize is reached - - unselect(); - - if (suggestedViewHeight === undefined) { - calcSize(); - } - - var forceEventRender = false; - if (!currentView.start || inc || date < currentView.start || date >= currentView.end) { - // view must render an entire new date range (and refetch/render events) - currentView.render(date, inc || 0); // responsible for clearing events - setSize(true); - forceEventRender = true; - } - else if (currentView.sizeDirty) { - // view must resize (and rerender events) - currentView.clearEvents(); - setSize(); - forceEventRender = true; - } - else if (currentView.eventsDirty) { - currentView.clearEvents(); - forceEventRender = true; - } - currentView.sizeDirty = false; - currentView.eventsDirty = false; - updateEvents(forceEventRender); - - elementOuterWidth = element.outerWidth(); - - header.updateTitle(currentView.title); - var today = new Date(); - if (today >= currentView.start && today < currentView.end) { - header.disableButton('today'); - }else{ - header.enableButton('today'); - } - + ignoreWindowResize++; + _renderView(inc); ignoreWindowResize--; - currentView.trigger('viewDisplay', _element); } } + + + function _renderView(inc) { + if (!currentView.start) { // has not been rendered before + renderViewDateRange(); + getAndRenderEvents(); + } + else if (inc || date < currentView.start || date >= currentView.end) { + unselect(); + clearEvents(); + renderViewDateRange(inc); + getAndRenderEvents(); + } + currentView.trigger('viewDisplay', _element); // deprecated + } + + + function renderViewDateRange(inc) { + freezeContentHeight(); + currentView.render(date, inc || 0); // the view's render method ONLY renders the skeleton, nothing else + setSize(); + unfreezeContentHeight(); + (currentView.afterRender || noop)(); + updateTitle(); + updateTodayButton(); + } - + /* Resizing -----------------------------------------------------------------------------*/ function updateSize() { - markSizesDirty(); if (elementVisible()) { + unselect(); + clearEvents(); calcSize(); setSize(); - unselect(); - currentView.clearEvents(); - currentView.renderEvents(events); - currentView.sizeDirty = false; + renderEvents(); } } - function markSizesDirty() { - $.each(viewInstances, function(i, inst) { - inst.sizeDirty = true; - }); - } - - function calcSize() { if (options.contentHeight) { suggestedViewHeight = options.contentHeight; @@ -279,15 +236,18 @@ function Calendar(element, options, eventSources) { } - function setSize(dateChanged) { // todo: dateChanged? - ignoreWindowResize++; - currentView.setHeight(suggestedViewHeight, dateChanged); - if (absoluteViewElement) { - absoluteViewElement.css('position', 'relative'); - absoluteViewElement = null; + function setSize() { + + if (suggestedViewHeight === undefined) { + calcSize(); // for first time } - currentView.setWidth(content.width(), dateChanged); + + ignoreWindowResize++; + currentView.setHeight(suggestedViewHeight); + currentView.setWidth(content.width()); ignoreWindowResize--; + + elementOuterWidth = element.outerWidth(); } @@ -316,52 +276,84 @@ function Calendar(element, options, eventSources) { /* Event Fetching/Rendering -----------------------------------------------------------------------------*/ + // TODO: going forward, most of this stuff should be directly handled by the view + + + function refetchEvents() { // can be called as an API method + clearEvents(); + fetchAndRenderEvents(); + } + + + function rerenderEvents(modifiedEventID) { // can be called as an API method + clearEvents(); + renderEvents(modifiedEventID); + } + + + function renderEvents(modifiedEventID) { // TODO: remove modifiedEventID hack + if (elementVisible()) { + currentView.setEventData(events); // for View.js, TODO: unify with renderEvents + currentView.renderEvents(events, modifiedEventID); // actually render the DOM elements + currentView.trigger('eventAfterAllRender'); + } + } + + + function clearEvents() { + currentView.clearEvents(); // actually remove the DOM elements + currentView.clearEventData(); // for View.js, TODO: unify with clearEvents + } - - // fetches events if necessary, rerenders events if necessary (or if forced) - function updateEvents(forceRender) { + + function getAndRenderEvents() { if (!options.lazyFetching || isFetchNeeded(currentView.visStart, currentView.visEnd)) { - refetchEvents(); + fetchAndRenderEvents(); } - else if (forceRender) { - rerenderEvents(); + else { + renderEvents(); } } - - - function refetchEvents() { - fetchEvents(currentView.visStart, currentView.visEnd); // will call reportEvents + + + function fetchAndRenderEvents() { + fetchEvents(currentView.visStart, currentView.visEnd); + // ... will call reportEvents + // ... which will call renderEvents } - + // called when event data arrives function reportEvents(_events) { events = _events; - rerenderEvents(); + renderEvents(); } - - + + // called when a single event's data has been changed function reportEventChange(eventID) { rerenderEvents(eventID); } - - - // attempts to rerenderEvents - function rerenderEvents(modifiedEventID) { - markEventsDirty(); - if (elementVisible()) { - currentView.clearEvents(); - currentView.renderEvents(events, modifiedEventID); - currentView.eventsDirty = false; - } + + + + /* Header Updating + -----------------------------------------------------------------------------*/ + + + function updateTitle() { + header.updateTitle(currentView.title); } - - - function markEventsDirty() { - $.each(viewInstances, function(i, inst) { - inst.eventsDirty = true; - }); + + + function updateTodayButton() { + var today = new Date(); + if (today >= currentView.start && today < currentView.end) { + header.disableButton('today'); + } + else { + header.enableButton('today'); + } } @@ -442,6 +434,29 @@ function Calendar(element, options, eventSources) { function getDate() { return cloneDate(date); } + + + + /* Height "Freezing" + -----------------------------------------------------------------------------*/ + + + function freezeContentHeight() { + content.css({ + width: '100%', + height: content.height(), + overflow: 'hidden' + }); + } + + + function unfreezeContentHeight() { + content.css({ + width: '', + height: '', + overflow: '' + }); + } diff --git a/src/agenda/AgendaEventRenderer.js b/src/agenda/AgendaEventRenderer.js index 30299f1..867e03c 100644 --- a/src/agenda/AgendaEventRenderer.js +++ b/src/agenda/AgendaEventRenderer.js @@ -16,8 +16,6 @@ function AgendaEventRenderer() { var isEventDraggable = t.isEventDraggable; var isEventResizable = t.isEventResizable; var eventEnd = t.eventEnd; - var reportEvents = t.reportEvents; - var reportEventClear = t.reportEventClear; var eventElementHandlers = t.eventElementHandlers; var setHeight = t.setHeight; var getDaySegmentContainer = t.getDaySegmentContainer; @@ -59,7 +57,6 @@ function AgendaEventRenderer() { function renderEvents(events, modifiedEventId) { - reportEvents(events); var i, len=events.length, dayEvents=[], slotEvents=[]; @@ -77,13 +74,10 @@ function AgendaEventRenderer() { } renderSlotSegs(compileSlotSegs(slotEvents), modifiedEventId); - - trigger('eventAfterAllRender'); } function clearEvents() { - reportEventClear(); getDaySegmentContainer().empty(); getSlotSegmentContainer().empty(); } diff --git a/src/agenda/AgendaView.js b/src/agenda/AgendaView.js index c83971b..54e7aed 100644 --- a/src/agenda/AgendaView.js +++ b/src/agenda/AgendaView.js @@ -29,8 +29,7 @@ function AgendaView(element, calendar, viewName) { t.renderAgenda = renderAgenda; t.setWidth = setWidth; t.setHeight = setHeight; - t.beforeHide = beforeHide; - t.afterShow = afterShow; + t.afterRender = afterRender; t.defaultEventEnd = defaultEventEnd; t.timePosition = timePosition; t.getIsCellAllDay = getIsCellAllDay; @@ -67,7 +66,6 @@ function AgendaView(element, calendar, viewName) { AgendaEventRenderer.call(t); var opt = t.opt; var trigger = t.trigger; - var clearEvents = t.clearEvents; var renderOverlay = t.renderOverlay; var clearOverlays = t.clearOverlays; var reportSelection = t.reportSelection; @@ -120,7 +118,6 @@ function AgendaView(element, calendar, viewName) { var colPositions; var colContentPositions; var slotTopCache = {}; - var savedScrollTop; var tm; var rtl; @@ -142,11 +139,12 @@ function AgendaView(element, calendar, viewName) { function renderAgenda(c) { colCnt = c; updateOptions(); - if (!dayTable) { + + if (!dayTable) { // first time rendering? buildSkeleton(); // builds day table, slot area, events containers - }else{ + } + else { buildDayTable(); // rebuilds day table - clearEvents(); } } @@ -426,7 +424,7 @@ function AgendaView(element, calendar, viewName) { -----------------------------------------------------------------------*/ - function setHeight(height, dateChanged) { + function setHeight(height) { if (height === undefined) { height = viewHeight; } @@ -451,10 +449,6 @@ function AgendaView(element, calendar, viewName) { snapRatio = opt('slotMinutes') / snapMinutes; snapHeight = slotHeight / snapRatio; - - if (dateChanged) { - resetScroll(); - } } @@ -521,15 +515,10 @@ function AgendaView(element, calendar, viewName) { scroll(); setTimeout(scroll, 0); // overrides any previous scroll state made by the browser } - - - function beforeHide() { - savedScrollTop = slotScroller.scrollTop(); - } - - - function afterShow() { - slotScroller.scrollTop(savedScrollTop); + + + function afterRender() { // after the view has been freshly rendered and sized + resetScroll(); } diff --git a/src/basic/BasicEventRenderer.js b/src/basic/BasicEventRenderer.js index 342e1de..5fc5ec8 100644 --- a/src/basic/BasicEventRenderer.js +++ b/src/basic/BasicEventRenderer.js @@ -13,14 +13,11 @@ function BasicEventRenderer() { function renderEvents(events, modifiedEventId) { - t.reportEvents(events); t.renderDayEvents(events, modifiedEventId); - t.trigger('eventAfterAllRender'); } function clearEvents() { - t.reportEventClear(); t.getDaySegmentContainer().empty(); } diff --git a/src/basic/BasicView.js b/src/basic/BasicView.js index 9350a79..c3ba0a3 100644 --- a/src/basic/BasicView.js +++ b/src/basic/BasicView.js @@ -40,7 +40,6 @@ function BasicView(element, calendar, viewName) { BasicEventRenderer.call(t); var opt = t.opt; var trigger = t.trigger; - var clearEvents = t.clearEvents; var renderOverlay = t.renderOverlay; var clearOverlays = t.clearOverlays; var daySelectionMousedown = t.daySelectionMousedown; @@ -95,12 +94,11 @@ function BasicView(element, calendar, viewName) { colCnt = _colCnt; showNumbers = _showNumbers; updateOptions(); - var firstTime = !body; - if (firstTime) { + + if (!body) { buildEventContainer(); - }else{ - clearEvents(); } + buildTable(); } @@ -131,7 +129,6 @@ function BasicView(element, calendar, viewName) { function buildTable() { var html = buildTableHTML(); - lockHeight(); // the unlock happens later, in setHeight()... if (table) { table.remove(); } @@ -318,14 +315,13 @@ function BasicView(element, calendar, viewName) { bodyFirstCells.each(function(i, _cell) { if (i < rowCnt) { cell = $(_cell); - setMinHeight( - cell.find('> div'), + cell.find('> div').css( + 'min-height', (i==rowCnt-1 ? rowHeightLast : rowHeight) - vsides(cell) ); } }); - unlockHeight(); } @@ -520,19 +516,5 @@ function BasicView(element, calendar, viewName) { function allDayRow(i) { return bodyRows.eq(i); } - - - - // makes sure height doesn't collapse while we destroy/render new cells - // (this causes a bad end-user scrollbar jump) - // TODO: generalize this for all view rendering. (also in Calendar.js) - - function lockHeight() { - setMinHeight(element, element.height()); - } - - function unlockHeight() { - setMinHeight(element, 1); - } } diff --git a/src/common/View.js b/src/common/View.js index b32c812..8c02927 100644 --- a/src/common/View.js +++ b/src/common/View.js @@ -12,10 +12,10 @@ function View(element, calendar, viewName) { t.trigger = trigger; t.isEventDraggable = isEventDraggable; t.isEventResizable = isEventResizable; - t.reportEvents = reportEvents; + t.setEventData = setEventData; + t.clearEventData = clearEventData; t.eventEnd = eventEnd; t.reportEventElement = reportEventElement; - t.reportEventClear = reportEventClear; t.eventElementHandlers = eventElementHandlers; t.showEvents = showEvents; t.hideEvents = hideEvents; @@ -95,8 +95,7 @@ function View(element, calendar, viewName) { ------------------------------------------------------------------------------*/ - // report when view receives new events - function reportEvents(events) { // events are already normalized at this point + function setEventData(events) { // events are already normalized at this point eventsByID = {}; var i, len=events.length, event; for (i=0; i