|
|
@@ -9,6 +9,7 @@ var Calendar = FC.Calendar = Class.extend({
|
|
|
viewSpecCache: null, // cache of view definitions
|
|
|
view: null, // current View object
|
|
|
header: null,
|
|
|
+ footer: null,
|
|
|
loadingLevel: 0, // number of simultaneous loading tasks
|
|
|
|
|
|
|
|
|
@@ -483,7 +484,7 @@ function Calendar_constructor(element, overrides) {
|
|
|
};
|
|
|
|
|
|
|
|
|
-
|
|
|
+
|
|
|
// Imports
|
|
|
// -----------------------------------------------------------------------------------
|
|
|
|
|
|
@@ -500,7 +501,9 @@ function Calendar_constructor(element, overrides) {
|
|
|
|
|
|
|
|
|
var _element = element[0];
|
|
|
+ var toolbarsManager;
|
|
|
var header;
|
|
|
+ var footer;
|
|
|
var content;
|
|
|
var tm; // for making theme classes
|
|
|
var currentView; // NOTE: keep this in sync with this.view
|
|
|
@@ -510,9 +513,9 @@ function Calendar_constructor(element, overrides) {
|
|
|
var ignoreWindowResize = 0;
|
|
|
var events = [];
|
|
|
var date; // unzoned
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
// Main Rendering
|
|
|
// -----------------------------------------------------------------------------------
|
|
|
|
|
|
@@ -524,8 +527,8 @@ function Calendar_constructor(element, overrides) {
|
|
|
else {
|
|
|
date = t.getNow(); // getNow already returns unzoned
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function render() {
|
|
|
if (!content) {
|
|
|
initialRender();
|
|
|
@@ -536,8 +539,8 @@ function Calendar_constructor(element, overrides) {
|
|
|
renderView();
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function initialRender() {
|
|
|
element.addClass('fc');
|
|
|
|
|
|
@@ -578,9 +581,14 @@ function Calendar_constructor(element, overrides) {
|
|
|
|
|
|
content = $("<div class='fc-view-container'/>").prependTo(element);
|
|
|
|
|
|
- header = t.header = new Header(t);
|
|
|
- renderHeader();
|
|
|
+ var toolbars = buildToolbars();
|
|
|
+ toolbarsManager = new Iterator(toolbars);
|
|
|
+
|
|
|
+ header = t.header = toolbars[0];
|
|
|
+ footer = t.footer = toolbars[1];
|
|
|
|
|
|
+ renderHeader();
|
|
|
+ renderFooter();
|
|
|
renderView(t.options.defaultView);
|
|
|
|
|
|
if (t.options.handleWindowResize) {
|
|
|
@@ -590,15 +598,50 @@ function Calendar_constructor(element, overrides) {
|
|
|
}
|
|
|
|
|
|
|
|
|
+ function buildToolbars() {
|
|
|
+ return [
|
|
|
+ new Toolbar(t, computeHeaderOptions()),
|
|
|
+ new Toolbar(t, computeFooterOptions())
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ function computeHeaderOptions() {
|
|
|
+ return {
|
|
|
+ extraClasses: 'fc-header-toolbar',
|
|
|
+ layout: t.options.header
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ function computeFooterOptions() {
|
|
|
+ return {
|
|
|
+ extraClasses: 'fc-footer-toolbar',
|
|
|
+ layout: t.options.footer
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
// can be called repeatedly and Header will rerender
|
|
|
function renderHeader() {
|
|
|
+ header.setToolbarOptions(computeHeaderOptions());
|
|
|
header.render();
|
|
|
if (header.el) {
|
|
|
element.prepend(header.el);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
+ // can be called repeatedly and Footer will rerender
|
|
|
+ function renderFooter() {
|
|
|
+ footer.setToolbarOptions(computeFooterOptions());
|
|
|
+ footer.render();
|
|
|
+ if (footer.el) {
|
|
|
+ element.append(footer.el);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
function destroy() {
|
|
|
|
|
|
if (currentView) {
|
|
|
@@ -608,7 +651,7 @@ function Calendar_constructor(element, overrides) {
|
|
|
// It is still the "current" view, just not rendered.
|
|
|
}
|
|
|
|
|
|
- header.removeElement();
|
|
|
+ toolbarsManager.proxyCall('removeElement');
|
|
|
content.remove();
|
|
|
element.removeClass('fc fc-ltr fc-rtl fc-unthemed ui-widget');
|
|
|
|
|
|
@@ -618,13 +661,13 @@ function Calendar_constructor(element, overrides) {
|
|
|
$(window).unbind('resize', windowResizeProxy);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function elementVisible() {
|
|
|
return element.is(':visible');
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
|
|
|
// View Rendering
|
|
|
// -----------------------------------------------------------------------------------
|
|
|
@@ -651,7 +694,7 @@ function Calendar_constructor(element, overrides) {
|
|
|
currentView.setElement(
|
|
|
$("<div class='fc-view fc-" + viewType + "-view' />").appendTo(content)
|
|
|
);
|
|
|
- header.activateButton(viewType);
|
|
|
+ toolbarsManager.proxyCall('activateButton', viewType);
|
|
|
}
|
|
|
|
|
|
if (currentView) {
|
|
|
@@ -673,8 +716,8 @@ function Calendar_constructor(element, overrides) {
|
|
|
unfreezeContentHeight(); // immediately unfreeze regardless of whether display is async
|
|
|
|
|
|
// need to do this after View::render, so dates are calculated
|
|
|
- updateHeaderTitle();
|
|
|
- updateTodayButton();
|
|
|
+ updateToolbarsTitle();
|
|
|
+ updateToolbarsTodayButton();
|
|
|
|
|
|
getAndRenderEvents();
|
|
|
}
|
|
|
@@ -689,7 +732,7 @@ function Calendar_constructor(element, overrides) {
|
|
|
// Unrenders the current view and reflects this change in the Header.
|
|
|
// Unregsiters the `currentView`, but does not remove from viewByType hash.
|
|
|
function clearView() {
|
|
|
- header.deactivateButton(currentView.type);
|
|
|
+ toolbarsManager.proxyCall('deactivateButton', currentView.type);
|
|
|
currentView.removeElement();
|
|
|
currentView = t.view = null;
|
|
|
}
|
|
|
@@ -711,7 +754,7 @@ function Calendar_constructor(element, overrides) {
|
|
|
ignoreWindowResize--;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+
|
|
|
|
|
|
// Resizing
|
|
|
// -----------------------------------------------------------------------------------
|
|
|
@@ -728,8 +771,8 @@ function Calendar_constructor(element, overrides) {
|
|
|
t.isHeightAuto = function() {
|
|
|
return t.options.contentHeight === 'auto' || t.options.height === 'auto';
|
|
|
};
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function updateSize(shouldRecalc) {
|
|
|
if (elementVisible()) {
|
|
|
|
|
|
@@ -751,8 +794,8 @@ function Calendar_constructor(element, overrides) {
|
|
|
_calcSize();
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function _calcSize() { // assumes elementVisible
|
|
|
var contentHeightInput = t.options.contentHeight;
|
|
|
var heightInput = t.options.height;
|
|
|
@@ -764,13 +807,13 @@ function Calendar_constructor(element, overrides) {
|
|
|
suggestedViewHeight = contentHeightInput();
|
|
|
}
|
|
|
else if (typeof heightInput === 'number') { // exists and not 'auto'
|
|
|
- suggestedViewHeight = heightInput - queryHeaderHeight();
|
|
|
+ suggestedViewHeight = heightInput - queryToolbarsHeight();
|
|
|
}
|
|
|
else if (typeof heightInput === 'function') { // exists and is a function
|
|
|
- suggestedViewHeight = heightInput() - queryHeaderHeight();
|
|
|
+ suggestedViewHeight = heightInput() - queryToolbarsHeight();
|
|
|
}
|
|
|
else if (heightInput === 'parent') { // set to height of parent element
|
|
|
- suggestedViewHeight = element.parent().height() - queryHeaderHeight();
|
|
|
+ suggestedViewHeight = element.parent().height() - queryToolbarsHeight();
|
|
|
}
|
|
|
else {
|
|
|
suggestedViewHeight = Math.round(content.width() / Math.max(t.options.aspectRatio, .5));
|
|
|
@@ -778,11 +821,14 @@ function Calendar_constructor(element, overrides) {
|
|
|
}
|
|
|
|
|
|
|
|
|
- function queryHeaderHeight() {
|
|
|
- return header.el ? header.el.outerHeight(true) : 0; // includes margin
|
|
|
+ function queryToolbarsHeight() {
|
|
|
+ return toolbarsManager.items.reduce(function(accumulator, toolbar) {
|
|
|
+ var toolbarHeight = toolbar.el ? toolbar.el.outerHeight(true) : 0; // includes margin
|
|
|
+ return accumulator + toolbarHeight;
|
|
|
+ }, 0);
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function windowResize(ev) {
|
|
|
if (
|
|
|
!ignoreWindowResize &&
|
|
|
@@ -794,9 +840,9 @@ function Calendar_constructor(element, overrides) {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
/* Event Fetching/Rendering
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
// TODO: going forward, most of this stuff should be directly handled by the view
|
|
|
@@ -820,7 +866,7 @@ function Calendar_constructor(element, overrides) {
|
|
|
unfreezeContentHeight();
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
|
|
|
function getAndRenderEvents() {
|
|
|
if (!t.options.lazyFetching || isFetchNeeded(currentView.start, currentView.end)) {
|
|
|
@@ -838,7 +884,7 @@ function Calendar_constructor(element, overrides) {
|
|
|
// ... which will call renderEvents
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+
|
|
|
// called when event data arrives
|
|
|
function reportEvents(_events) {
|
|
|
events = _events;
|
|
|
@@ -853,31 +899,30 @@ function Calendar_constructor(element, overrides) {
|
|
|
|
|
|
|
|
|
|
|
|
- /* Header Updating
|
|
|
+ /* Toolbars Updating
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
- function updateHeaderTitle() {
|
|
|
- header.updateTitle(currentView.title);
|
|
|
+ function updateToolbarsTitle() {
|
|
|
+ toolbarsManager.proxyCall('updateTitle', currentView.title);
|
|
|
}
|
|
|
|
|
|
|
|
|
- function updateTodayButton() {
|
|
|
+ function updateToolbarsTodayButton() {
|
|
|
var now = t.getNow();
|
|
|
-
|
|
|
if (now >= currentView.intervalStart && now < currentView.intervalEnd) {
|
|
|
- header.disableButton('today');
|
|
|
+ toolbarsManager.proxyCall('disableButton', 'today');
|
|
|
}
|
|
|
else {
|
|
|
- header.enableButton('today');
|
|
|
+ toolbarsManager.proxyCall('enableButton', 'today');
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
|
|
|
|
|
|
/* Selection
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
-
|
|
|
+
|
|
|
|
|
|
// this public method receives start/end dates in any format, with any timezone
|
|
|
function select(zonedStartInput, zonedEndInput) {
|
|
|
@@ -885,56 +930,56 @@ function Calendar_constructor(element, overrides) {
|
|
|
t.buildSelectSpan.apply(t, arguments)
|
|
|
);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
|
|
|
function unselect() { // safe to be called before renderView
|
|
|
if (currentView) {
|
|
|
currentView.unselect();
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
/* Date
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function prev() {
|
|
|
date = currentView.computePrevDate(date);
|
|
|
renderView();
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function next() {
|
|
|
date = currentView.computeNextDate(date);
|
|
|
renderView();
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function prevYear() {
|
|
|
date.add(-1, 'years');
|
|
|
renderView();
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function nextYear() {
|
|
|
date.add(1, 'years');
|
|
|
renderView();
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function today() {
|
|
|
date = t.getNow();
|
|
|
renderView();
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function gotoDate(zonedDateInput) {
|
|
|
date = t.moment(zonedDateInput).stripZone();
|
|
|
renderView();
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function incrementDate(delta) {
|
|
|
date.add(moment.duration(delta));
|
|
|
renderView();
|
|
|
@@ -952,8 +997,8 @@ function Calendar_constructor(element, overrides) {
|
|
|
date = newDate.clone();
|
|
|
renderView(spec ? spec.type : null);
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
// for external API
|
|
|
function getDate() {
|
|
|
return t.applyTimezone(date); // infuse the calendar's timezone
|
|
|
@@ -985,23 +1030,23 @@ function Calendar_constructor(element, overrides) {
|
|
|
overflow: ''
|
|
|
});
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
/* Misc
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
-
|
|
|
+
|
|
|
|
|
|
function getCalendar() {
|
|
|
return t;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+
|
|
|
function getView() {
|
|
|
return currentView;
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function option(name, value) {
|
|
|
var newOptionHash;
|
|
|
|
|
|
@@ -1062,13 +1107,14 @@ function Calendar_constructor(element, overrides) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // catch-all. rerender the header and rebuild/rerender the current view
|
|
|
+ // catch-all. rerender the header and footer and rebuild/rerender the current view
|
|
|
renderHeader();
|
|
|
+ renderFooter();
|
|
|
viewsByType = {}; // even non-current views will be affected by this option change. do before rerender
|
|
|
reinitView();
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
function trigger(name, thisObj) { // overrides the Emitter's trigger method :(
|
|
|
var args = Array.prototype.slice.call(arguments, 2);
|
|
|
|