throw { name: 'FatalError', message: 'Do not call this file. If you want to use it, remove this line.' }; /** * @preserve * FullCalendar v1.4.6 * http://arshaw.com/fullcalendar/ * * Use fullcalendar.css for basic styling. * For event drag & drop, required jQuery UI draggable. * For event resizing, requires jQuery UI resizable. * * Copyright (c) 2009 Adam Shaw * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * * Date: Mon May 31 10:18:29 2010 -0700 * */ (function($) { var fc = $.fullCalendar = {}; var views = fc.views = {}; /* Defaults -----------------------------------------------------------------------------*/ var defaults = { // display defaultView: 'month', aspectRatio: 1.35, header: { left: 'title', center: '', right: 'today prev,next' }, weekends: true, // editing //editable: false, //disableDragging: false, //disableResizing: false, allDayDefault: true, // event ajax lazyFetching: true, startParam: 'start', endParam: 'end', // time formats titleFormat: { month: 'MMMM yyyy', week: "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", day: 'dddd, MMM d, yyyy' }, columnFormat: { month: 'ddd', week: 'ddd M/d', day: 'dddd M/d' }, timeFormat: { // for event elements '': 'h(:mm)t' // default }, // locale isRTL: false, firstDay: 0, monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'], monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'], dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], buttonText: { prev: ' ◄ ', next: ' ► ', prevYear: ' << ', nextYear: ' >> ', today: 'today', month: 'month', week: 'week', day: 'day' }, // jquery-ui theming theme: false, buttonIcons: { prev: 'circle-triangle-w', next: 'circle-triangle-e' }, //selectable: false, unselectAuto: true }; // right-to-left defaults var rtlDefaults = { header: { left: 'next,prev today', center: '', right: 'title' }, buttonText: { prev: ' ► ', next: ' ◄ ', prevYear: ' >> ', nextYear: ' << ' }, buttonIcons: { prev: 'circle-triangle-e', next: 'circle-triangle-w' } }; // function for adding/overriding defaults var setDefaults = fc.setDefaults = function(d) { $.extend(true, defaults, d); }; /* .fullCalendar jQuery function -----------------------------------------------------------------------------*/ $.fn.fullCalendar = function(options) { // method calling if (typeof options == 'string') { var args = Array.prototype.slice.call(arguments, 1), res; this.each(function() { var data = $.data(this, 'fullCalendar'); if (data) { var meth = data[options]; if (meth) { var r = meth.apply(this, args); if (res === undefined) { res = r; } } } }); if (res !== undefined) { return res; } return this; } // pluck the 'events' and 'eventSources' options var eventSources = options.eventSources || []; delete options.eventSources; if (options.events) { eventSources.push(options.events); delete options.events; } // first event source reserved for 'sticky' events eventSources.unshift([]); // initialize options options = $.extend(true, {}, defaults, (options.isRTL || options.isRTL===undefined && defaults.isRTL) ? rtlDefaults : {}, options ); var tm = options.theme ? 'ui' : 'fc'; // for making theme classes this.each(function() { /* Instance Initialization -----------------------------------------------------------------------------*/ // element var _element = this, element = $(_element).addClass('fc'), elementOuterWidth, content = $("
").prependTo(_element), suggestedViewHeight, resizeUID = 0, ignoreWindowResize = 0, date = new Date(), viewName, // the current view name (TODO: look into getting rid of) view, // the current view viewInstances = {}, absoluteViewElement; if (options.isRTL) { element.addClass('fc-rtl'); } if (options.theme) { element.addClass('ui-widget'); } if (options.year !== undefined && options.year != date.getFullYear()) { date.setDate(1); date.setMonth(0); date.setFullYear(options.year); } if (options.month !== undefined && options.month != date.getMonth()) { date.setDate(1); date.setMonth(options.month); } if (options.date !== undefined) { date.setDate(options.date); } /* View Rendering -----------------------------------------------------------------------------*/ function changeView(v) { if (v != viewName) { ignoreWindowResize++; // because setMinHeight might change the height before render (and subsequently setSize) is reached viewUnselect(); var oldView = view, newViewElement; if (oldView) { if (oldView.eventsChanged) { eventsDirty(); oldView.eventDirty = oldView.eventsChanged = false; } if (oldView.beforeHide) { oldView.beforeHide(); // 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'); if (viewInstances[v]) { (view = viewInstances[v]).element.show(); }else{ view = viewInstances[v] = fc.views[v]( newViewElement = absoluteViewElement = $("
") .appendTo(content), options ); } if (header) { // update 'active' view button header.find('div.fc-button-' + viewName).removeClass(tm + '-state-active'); header.find('div.fc-button-' + v).addClass(tm + '-state-active'); } view.name = viewName = v; render(); // 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 && view.afterShow) { view.afterShow(); // called after setting min-height/overflow, so in final scroll state (for Opera) } ignoreWindowResize--; } } function render(inc) { if (elementVisible()) { ignoreWindowResize++; // because view.renderEvents might temporarily change the height before setSize is reached viewUnselect(); if (suggestedViewHeight === undefined) { calcSize(); } if (!view.start || inc || date < view.start || date >= view.end) { view.render(date, inc || 0); // responsible for clearing events setSize(true); if (!eventStart || !options.lazyFetching || view.visStart < eventStart || view.visEnd > eventEnd) { fetchAndRenderEvents(); }else{ view.renderEvents(events); // don't refetch } } else if (view.sizeDirty || view.eventsDirty || !options.lazyFetching) { view.clearEvents(); if (view.sizeDirty) { setSize(); } if (options.lazyFetching) { view.renderEvents(events); // don't refetch }else{ fetchAndRenderEvents(); } } elementOuterWidth = element.outerWidth(); view.sizeDirty = false; view.eventsDirty = false; if (header) { // update title text header.find('h2.fc-header-title').html(view.title); // enable/disable 'today' button var today = new Date(); if (today >= view.start && today < view.end) { header.find('div.fc-button-today').addClass(tm + '-state-disabled'); }else{ header.find('div.fc-button-today').removeClass(tm + '-state-disabled'); } } ignoreWindowResize--; view.trigger('viewDisplay', _element); } } function elementVisible() { return _element.offsetWidth !== 0; } function bodyVisible() { return $('body')[0].offsetWidth !== 0; } function viewUnselect() { if (view) { view.unselect(); } } // called when any event objects have been added/removed/changed, rerenders function eventsChanged() { eventsDirty(); if (elementVisible()) { view.clearEvents(); view.renderEvents(events); view.eventsDirty = false; } } // marks other views' events as dirty function eventsDirty() { $.each(viewInstances, function() { this.eventsDirty = true; }); } // called when we know the element size has changed function sizeChanged() { sizesDirty(); if (elementVisible()) { calcSize(); setSize(); viewUnselect(); view.rerenderEvents(); view.sizeDirty = false; } } // marks other views' sizes as dirty function sizesDirty() { $.each(viewInstances, function() { this.sizeDirty = true; }); } /* Event Sources and Fetching -----------------------------------------------------------------------------*/ var events = [], eventStart, eventEnd; // Fetch from ALL sources. Clear 'events' array and populate function fetchEvents(callback) { events = []; eventStart = cloneDate(view.visStart); eventEnd = cloneDate(view.visEnd); var queued = eventSources.length, sourceDone = function() { if (!--queued) { if (callback) { callback(events); } } }, i=0; for (; i") .append($("") .append($("").append(buildSection(sections.left))) .append($("").append(buildSection(sections.center))) .append($("").append(buildSection(sections.right)))) .prependTo(element); } function buildSection(buttonStr) { if (buttonStr) { var tr = $(""); $.each(buttonStr.split(' '), function(i) { if (i > 0) { tr.append(""); } var prevButton; $.each(this.split(','), function(j, buttonName) { if (buttonName == 'title') { tr.append("

 

"); if (prevButton) { prevButton.addClass(tm + '-corner-right'); } prevButton = null; }else{ var buttonClick; if (publicMethods[buttonName]) { buttonClick = publicMethods[buttonName]; } else if (views[buttonName]) { buttonClick = function() { button.removeClass(tm + '-state-hover'); changeView(buttonName); }; } if (buttonClick) { if (prevButton) { prevButton.addClass(tm + '-no-right'); } var button, icon = options.theme ? smartProperty(options.buttonIcons, buttonName) : null, text = smartProperty(options.buttonText, buttonName); if (icon) { button = $("
" + "
"); } else if (text) { button = $(""); } if (button) { button .click(function() { if (!button.hasClass(tm + '-state-disabled')) { buttonClick(); } }) .mousedown(function() { button .not('.' + tm + '-state-active') .not('.' + tm + '-state-disabled') .addClass(tm + '-state-down'); }) .mouseup(function() { button.removeClass(tm + '-state-down'); }) .hover( function() { button .not('.' + tm + '-state-active') .not('.' + tm + '-state-disabled') .addClass(tm + '-state-hover'); }, function() { button .removeClass(tm + '-state-hover') .removeClass(tm + '-state-down'); } ) .appendTo($("").appendTo(tr)); if (prevButton) { prevButton.addClass(tm + '-no-right'); }else{ button.addClass(tm + '-corner-left'); } prevButton = button; } } } }); if (prevButton) { prevButton.addClass(tm + '-corner-right'); } }); return $("").append(tr); } } /* Resizing -----------------------------------------------------------------------------*/ function calcSize() { if (options.contentHeight) { suggestedViewHeight = options.contentHeight; } else if (options.height) { suggestedViewHeight = options.height - (header ? header.height() : 0) - vsides(content[0]); } else { suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5)); } } function setSize(dateChanged) { ignoreWindowResize++; view.setHeight(suggestedViewHeight, dateChanged); if (absoluteViewElement) { absoluteViewElement.css('position', 'relative'); absoluteViewElement = null; } view.setWidth(content.width(), dateChanged); ignoreWindowResize--; } function windowResize() { if (!ignoreWindowResize) { if (view.start) { // view has already been rendered var uid = ++resizeUID; setTimeout(function() { // add a delay if (uid == resizeUID && !ignoreWindowResize && elementVisible()) { if (elementOuterWidth != (elementOuterWidth = element.outerWidth())) { ignoreWindowResize++; // in case the windowResize callback changes the height sizeChanged(); view.trigger('windowResize', _element); ignoreWindowResize--; } } }, 200); }else{ // calendar must have been initialized in a 0x0 iframe that has just been resized lateRender(); } } } $(window).resize(windowResize); // let's begin... changeView(options.defaultView); // needed for IE in a 0x0 iframe, b/c when it is resized, never triggers a windowResize if (!bodyVisible()) { lateRender(); } // called when we know the calendar couldn't be rendered when it was initialized, // but we think it's ready now function lateRender() { setTimeout(function() { // IE7 needs this so dimensions are calculated correctly if (!view.start && bodyVisible()) { // !view.start makes sure this never happens more than once render(); } },0); } }); return this; }; /* Important Event Utilities -----------------------------------------------------------------------------*/ var fakeID = 0; function normalizeEvent(event, options) { event._id = event._id || (event.id === undefined ? '_fc' + fakeID++ : event.id + ''); if (event.date) { if (!event.start) { event.start = event.date; } delete event.date; } event._start = cloneDate(event.start = parseDate(event.start)); event.end = parseDate(event.end); if (event.end && event.end <= event.start) { event.end = null; } event._end = event.end ? cloneDate(event.end) : null; if (event.allDay === undefined) { event.allDay = options.allDayDefault; } if (event.className) { if (typeof event.className == 'string') { event.className = event.className.split(/\s+/); } }else{ event.className = []; } } // TODO: if there is no start date, return false to indicate an invalid event /* Grid-based Views: month, basicWeek, basicDay -----------------------------------------------------------------------------*/ setDefaults({ weekMode: 'fixed' }); views.month = function(element, options) { return new Grid(element, options, { render: function(date, delta) { if (delta) { addMonths(date, delta); date.setDate(1); } // start/end var start = this.start = cloneDate(date, true); start.setDate(1); this.end = addMonths(cloneDate(start), 1); // visStart/visEnd var visStart = this.visStart = cloneDate(start), visEnd = this.visEnd = cloneDate(this.end), nwe = options.weekends ? 0 : 1; if (nwe) { skipWeekend(visStart); skipWeekend(visEnd, -1, true); } addDays(visStart, -((visStart.getDay() - Math.max(options.firstDay, nwe) + 7) % 7)); addDays(visEnd, (7 - visEnd.getDay() + Math.max(options.firstDay, nwe)) % 7); // row count var rowCnt = Math.round((visEnd - visStart) / (DAY_MS * 7)); if (options.weekMode == 'fixed') { addDays(visEnd, (6 - rowCnt) * 7); rowCnt = 6; } // title this.title = formatDate( start, this.option('titleFormat'), options ); // render this.renderGrid( rowCnt, options.weekends ? 7 : 5, this.option('columnFormat'), true ); } }); }; views.basicWeek = function(element, options) { return new Grid(element, options, { render: function(date, delta) { if (delta) { addDays(date, delta * 7); } var visStart = this.visStart = cloneDate( this.start = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7)) ), visEnd = this.visEnd = cloneDate( this.end = addDays(cloneDate(visStart), 7) ); if (!options.weekends) { skipWeekend(visStart); skipWeekend(visEnd, -1, true); } this.title = formatDates( visStart, addDays(cloneDate(visEnd), -1), this.option('titleFormat'), options ); this.renderGrid( 1, options.weekends ? 7 : 5, this.option('columnFormat'), false ); } }); }; views.basicDay = function(element, options) { return new Grid(element, options, { render: function(date, delta) { if (delta) { addDays(date, delta); if (!options.weekends) { skipWeekend(date, delta < 0 ? -1 : 1); } } this.title = formatDate(date, this.option('titleFormat'), options); this.start = this.visStart = cloneDate(date, true); this.end = this.visEnd = addDays(cloneDate(this.start), 1); this.renderGrid( 1, 1, this.option('columnFormat'), false ); } }); }; // rendering bugs var tdHeightBug; function Grid(element, options, methods) { var tm, firstDay, nwe, // no weekends (int) rtl, dis, dit, // day index sign / translate viewWidth, viewHeight, rowCnt, colCnt, colWidth, thead, tbody, cachedEvents=[], segmentContainer, dayContentPositions = new HorizontalPositionCache(function(dayOfWeek) { return tbody.find('td:eq(' + ((dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt) + ') div div'); }), selectionManager, selectionMatrix, // ... // initialize superclass view = $.extend(this, viewMethods, methods, { renderGrid: renderGrid, renderEvents: renderEvents, rerenderEvents: rerenderEvents, clearEvents: clearEvents, setHeight: setHeight, setWidth: setWidth, defaultEventEnd: function(event) { // calculates an end if event doesnt have one, mostly for resizing return cloneDate(event.start); } }); view.init(element, options); /* Grid Rendering -----------------------------------------------------------------------------*/ disableTextSelection(element.addClass('fc-grid')); function renderGrid(r, c, colFormat, showNumbers) { rowCnt = r; colCnt = c; // update option-derived variables tm = options.theme ? 'ui' : 'fc'; nwe = options.weekends ? 0 : 1; firstDay = options.firstDay; if (rtl = options.isRTL) { dis = -1; dit = colCnt - 1; }else{ dis = 1; dit = 0; } var month = view.start.getMonth(), today = clearTime(new Date()), s, i, j, d = cloneDate(view.visStart); if (!tbody) { // first time, build all cells from scratch var table = $("
").appendTo(element); s = ""; for (i=0; i" + formatDate(d, colFormat, options) + ""; addDays(d, 1); if (nwe) { skipWeekend(d); } } thead = $(s + "").appendTo(table); s = ""; d = cloneDate(view.visStart); for (i=0; i"; for (j=0; j1 && d.getMonth() != month ? ' fc-other-month' : '') + (+d == +today ? ' fc-today '+tm+'-state-highlight' : ' fc-not-today') + "'>" + (showNumbers ? "
" + d.getDate() + "
" : '') + "
 
"; addDays(d, 1); if (nwe) { skipWeekend(d); } } s += ""; } tbody = $(s + "
").appendTo(table); dayBind(tbody.find('td')); segmentContainer = $("
").appendTo(element); }else{ // NOT first time, reuse as many cells as possible clearEvents(); var prevRowCnt = tbody.find('tr').length; if (rowCnt < prevRowCnt) { tbody.find('tr:gt(' + (rowCnt-1) + ')').remove(); // remove extra rows } else if (rowCnt > prevRowCnt) { // needs to create new rows... s = ''; for (i=prevRowCnt; i"; for (j=0; j" + (showNumbers ? "
" : '') + "
 
" + ""; addDays(d, 1); if (nwe) { skipWeekend(d); } } s += ""; } tbody.append(s); } dayBind(tbody.find('td.fc-new').removeClass('fc-new')); // re-label and re-class existing cells d = cloneDate(view.visStart); tbody.find('td').each(function() { var td = $(this); if (rowCnt > 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 (nwe) { skipWeekend(d); } }); if (rowCnt == 1) { // more changes likely (week or day view) // redo column header text and class d = cloneDate(view.visStart); thead.find('th').each(function() { $(this).text(formatDate(d, colFormat, options)); this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); addDays(d, 1); if (nwe) { skipWeekend(d); } }); // redo cell day-of-weeks d = cloneDate(view.visStart); tbody.find('td').each(function() { this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); addDays(d, 1); if (nwe) { skipWeekend(d); } }); } } } function setHeight(height) { viewHeight = height; var leftTDs = tbody.find('tr td:first-child'), tbodyHeight = viewHeight - thead.height(), 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 (tdHeightBug === undefined) { // bug in firefox where cell height includes padding var tr = tbody.find('tr:first'), td = tr.find('td:first'); 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); } } function setWidth(width) { viewWidth = width; dayContentPositions.clear(); setOuterWidth( thead.find('th').slice(0, -1), colWidth = Math.floor(viewWidth / colCnt) ); } /* Event Rendering -----------------------------------------------------------------------------*/ function renderEvents(events) { view.reportEvents(cachedEvents = events); renderSegs(compileSegs(events)); } function rerenderEvents(modifiedEventId) { clearEvents(); renderSegs(compileSegs(cachedEvents), modifiedEventId); } function clearEvents() { view._clearEvents(); // only clears the hashes segmentContainer.empty(); } function compileSegs(events) { var d1 = cloneDate(view.visStart), d2 = addDays(cloneDate(d1), colCnt), visEventsEnds = $.map(events, exclEndDay), i, row, j, level, k, seg, segs=[]; for (i=0; i" + "" + (!event.allDay && seg.isStart ? "" + htmlEscape(formatDates(event.start, event.end, view.option('timeFormat'), options)) + "" :'') + "" + htmlEscape(event.title) + "" + "" + ((event.editable || event.editable === undefined && options.editable) && !options.disableResizing && $.fn.resizable ? "
" : '') + "
"; seg.left = left; seg.outerWidth = right - left; } segmentContainer[0].innerHTML = html; // faster than html() eventElements = segmentContainer.children(); // retrieve elements, run through eventRender callback, bind handlers for (i=0; i div') // optimal selector? .height(top + levelHeight); } // calculate row tops for (rowI=0; rowI" + "" + ""; for (i=0; i" + formatDate(d, colFormat, options) + ""; addDays(d, dis); if (nwe) { skipWeekend(d, dis); } } s += ""; if (options.allDaySlot) { s += "" + "" + "" + "" + ""; } s+= "
  
" + options.allDayText + "" + "
 
 
"; head = $(s).appendTo(element); dayBind(head.find('td')); // all-day event container daySegmentContainer = $("
").appendTo(head); // body d = zeroDate(); var maxd = addMinutes(cloneDate(d), maxMinute); addMinutes(d, minMinute); s = ""; for (i=0; d < maxd; i++) { minutes = d.getMinutes(); s += ""; addMinutes(d, options.slotMinutes); slotCnt++; } s += "
" + ((!slotNormal || !minutes) ? formatDate(d, options.axisFormat) : ' ') + "
 
"; body = $("
") .append(bodyContent = $("
") .append(bodyTable = $(s))) .appendTo(element); slotBind(body.find('td')); // slot event container slotSegmentContainer = $("
").appendTo(bodyContent); // background stripes d = cloneDate(d0); s = "
" + ""; for (i=0; i
 
"; addDays(d, dis); if (nwe) { skipWeekend(d, dis); } } s += "
"; bg = $(s).appendTo(element); }else{ // skeleton already built, just modify it clearEvents(); // redo column header text and class head.find('tr:first th').slice(1, -1).each(function() { $(this).text(formatDate(d, colFormat, options)); this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); addDays(d, dis); if (nwe) { skipWeekend(d, dis); } }); // change classes of background stripes d = cloneDate(d0); bg.find('td').each(function() { this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); if (+d == +today) { $(this) .removeClass('fc-not-today') .addClass('fc-today') .addClass(tm + '-state-highlight'); }else{ $(this) .addClass('fc-not-today') .removeClass('fc-today') .removeClass(tm + '-state-highlight'); } addDays(d, dis); if (nwe) { skipWeekend(d, dis); } }); } } function resetScroll() { var d0 = zeroDate(), scrollDate = cloneDate(d0); scrollDate.setHours(options.firstHour); var top = timePosition(d0, scrollDate) + 1, // +1 for the border scroll = function() { body.scrollTop(top); }; scroll(); setTimeout(scroll, 0); // overrides any previous scroll state made by the browser } function setHeight(height, dateChanged) { viewHeight = height; slotTopCache = {}; body.height(height - head.height()); slotHeight = body.find('tr:first div').height() + 1; bg.css({ top: head.find('tr').height(), height: height }); if (dateChanged) { resetScroll(); } } function setWidth(width) { viewWidth = width; colContentPositions.clear(); body.width(width); bodyTable.width(''); var topTDs = head.find('tr:first th'), stripeTDs = bg.find('td'), clientWidth = body[0].clientWidth; bodyTable.width(clientWidth); // time-axis width axisWidth = 0; setOuterWidth( head.find('tr:lt(2) th:first').add(body.find('tr:first th')) .width('') .each(function() { axisWidth = Math.max(axisWidth, $(this).outerWidth()); }), axisWidth ); // column width colWidth = Math.floor((clientWidth - axisWidth) / colCnt); setOuterWidth(stripeTDs.slice(0, -1), colWidth); setOuterWidth(topTDs.slice(1, -2), colWidth); setOuterWidth(topTDs.slice(-2, -1), clientWidth - axisWidth - colWidth*(colCnt-1)); bg.css({ left: axisWidth, width: clientWidth - axisWidth }); } /* Slot/Day clicking and binding -----------------------------------------------------------------------*/ function dayBind(tds) { tds.click(slotClick) .mousedown(daySelectionMousedown); } function slotBind(tds) { tds.click(slotClick) .mousedown(slotSelectionMousedown); } function slotClick(ev) { if (!view.option('selectable')) { // SelectionManager will worry about dayClick var col = Math.floor((ev.pageX - bg.offset().left) / colWidth), date = addDays(cloneDate(view.visStart), dit + dis*col), rowMatch = this.className.match(/fc-slot(\d+)/); if (rowMatch) { var mins = parseInt(rowMatch[1]) * options.slotMinutes, hours = Math.floor(mins/60); date.setHours(hours); date.setMinutes(mins%60 + minMinute); view.trigger('dayClick', this, date, false, ev); }else{ view.trigger('dayClick', this, date, true, ev); } } } /* Event Rendering -----------------------------------------------------------------------------*/ function renderEvents(events, modifiedEventId) { view.reportEvents(cachedEvents = events); var i, len=events.length, dayEvents=[], slotEvents=[]; for (i=0; i" + "" + "" + "" + htmlEscape(formatDates(event.start, event.end, view.option('timeFormat'))) + "" + "" + htmlEscape(event.title) + "" + "" + ((event.editable || event.editable === undefined && options.editable) && !options.disableResizing && $.fn.resizable ? "
=
" : '') + "
"; } function daySegBind(event, eventElement, seg) { view.eventElementHandlers(event, eventElement); if (event.editable || event.editable === undefined && options.editable) { draggableDayEvent(event, eventElement, seg.isStart); if (seg.isEnd) { view.resizableDayEvent(event, eventElement, colWidth); } } } function slotSegBind(event, eventElement, seg) { view.eventElementHandlers(event, eventElement); if (event.editable || event.editable === undefined && options.editable) { var timeElement = eventElement.find('span.fc-event-time'); draggableSlotEvent(event, eventElement, timeElement); if (seg.isEnd) { resizableSlotEvent(event, eventElement, timeElement); } } } /* Event Dragging -----------------------------------------------------------------------------*/ // when event starts out FULL-DAY function draggableDayEvent(event, eventElement, isStart) { if (!options.disableDragging && eventElement.draggable) { var origPosition, origWidth, resetElement, allDay=true, matrix; eventElement.draggable({ zIndex: 9, opacity: view.option('dragOpacity', 'month'), // use whatever the month view was using revertDuration: options.dragRevertDuration, start: function(ev, ui) { view.hideEvents(event, eventElement); view.trigger('eventDragStart', eventElement, event, ev, ui); origPosition = eventElement.position(); origWidth = eventElement.width(); resetElement = function() { if (!allDay) { eventElement .width(origWidth) .height('') .draggable('option', 'grid', null); allDay = true; } }; matrix = buildDayMatrix(function(cell) { eventElement.draggable('option', 'revert', !cell || !cell.rowDelta && !cell.colDelta); view.clearOverlays(); if (cell) { if (!cell.row) { // on full-days renderDayOverlay( matrix, addDays(cloneDate(event.start), cell.colDelta), addDays(exclEndDay(event), cell.colDelta) ); resetElement(); }else{ // mouse is over bottom slots if (isStart && allDay) { // convert event to temporary slot-event setOuterHeight( eventElement.width(colWidth - 10), // don't use entire width slotHeight * Math.round( (event.end ? ((event.end - event.start)/MINUTE_MS) : options.defaultEventMinutes) /options.slotMinutes) ); eventElement.draggable('option', 'grid', [colWidth, 1]); allDay = false; } } } },true); matrix.mouse(ev); }, drag: function(ev, ui) { matrix.mouse(ev); }, stop: function(ev, ui) { view.trigger('eventDragStop', eventElement, event, ev, ui); view.clearOverlays(); var cell = matrix.cell; var dayDelta = dis * ( allDay ? // can't trust cell.colDelta when using slot grid (cell ? cell.colDelta : 0) : Math.floor((ui.position.left - origPosition.left) / colWidth) ); if (!cell || !dayDelta && !cell.rowDelta) { // over nothing (has reverted) resetElement(); if ($.browser.msie) { eventElement.css('filter', ''); // clear IE opacity side-effects } view.showEvents(event, eventElement); }else{ eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link view.eventDrop( this, event, dayDelta, allDay ? 0 : // minute delta Math.round((eventElement.offset().top - bodyContent.offset().top) / slotHeight) * options.slotMinutes + minMinute - (event.start.getHours() * 60 + event.start.getMinutes()), allDay, ev, ui ); } } }); } } // when event starts out IN TIMESLOTS function draggableSlotEvent(event, eventElement, timeElement) { if (!options.disableDragging && eventElement.draggable) { var origPosition, resetElement, prevSlotDelta, slotDelta, allDay=false, matrix; eventElement.draggable({ zIndex: 9, scroll: false, grid: [colWidth, slotHeight], axis: colCnt==1 ? 'y' : false, opacity: view.option('dragOpacity'), revertDuration: options.dragRevertDuration, start: function(ev, ui) { view.hideEvents(event, eventElement); view.trigger('eventDragStart', eventElement, event, ev, ui); if ($.browser.msie) { eventElement.find('span.fc-event-bg').hide(); // nested opacities mess up in IE, just hide } origPosition = eventElement.position(); resetElement = function() { // convert back to original slot-event if (allDay) { timeElement.css('display', ''); // show() was causing display=inline eventElement.draggable('option', 'grid', [colWidth, slotHeight]); allDay = false; } }; prevSlotDelta = 0; matrix = buildDayMatrix(function(cell) { eventElement.draggable('option', 'revert', !cell); view.clearOverlays(); if (cell) { if (!cell.row && options.allDaySlot) { // over full days if (!allDay) { // convert to temporary all-day event allDay = true; timeElement.hide(); eventElement.draggable('option', 'grid', null); } renderDayOverlay( matrix, addDays(cloneDate(event.start), cell.colDelta), addDays(exclEndDay(event), cell.colDelta) ); }else{ // on slots resetElement(); } } },true); matrix.mouse(ev); }, drag: function(ev, ui) { slotDelta = Math.round((ui.position.top - origPosition.top) / slotHeight); if (slotDelta != prevSlotDelta) { if (!allDay) { // update time header var minuteDelta = slotDelta*options.slotMinutes, newStart = addMinutes(cloneDate(event.start), minuteDelta), newEnd; if (event.end) { newEnd = addMinutes(cloneDate(event.end), minuteDelta); } timeElement.text(formatDates(newStart, newEnd, view.option('timeFormat'))); } prevSlotDelta = slotDelta; } matrix.mouse(ev); }, stop: function(ev, ui) { view.clearOverlays(); view.trigger('eventDragStop', eventElement, event, ev, ui); var cell = matrix.cell, dayDelta = dis * ( allDay ? // can't trust cell.colDelta when using slot grid (cell ? cell.colDelta : 0) : Math.floor((ui.position.left - origPosition.left) / colWidth) ); if (!cell || !slotDelta && !dayDelta) { resetElement(); if ($.browser.msie) { eventElement .css('filter', '') // clear IE opacity side-effects .find('span.fc-event-bg').css('display', ''); // .show() made display=inline } eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position view.showEvents(event, eventElement); }else{ view.eventDrop( this, event, dayDelta, allDay ? 0 : slotDelta * options.slotMinutes, // minute delta allDay, ev, ui ); } } }); } } /* Event Resizing -----------------------------------------------------------------------------*/ // for TIMESLOT events function resizableSlotEvent(event, eventElement, timeElement) { if (!options.disableResizing && eventElement.resizable) { var slotDelta, prevSlotDelta; eventElement.resizable({ handles: { s: 'div.ui-resizable-s' }, grid: slotHeight, start: function(ev, ui) { slotDelta = prevSlotDelta = 0; view.hideEvents(event, eventElement); if ($.browser.msie && $.browser.version == '6.0') { eventElement.css('overflow', 'hidden'); } eventElement.css('z-index', 9); view.trigger('eventResizeStart', this, event, ev, ui); }, resize: function(ev, ui) { // don't rely on ui.size.height, doesn't take grid into account slotDelta = Math.round((Math.max(slotHeight, eventElement.height()) - ui.originalSize.height) / slotHeight); if (slotDelta != prevSlotDelta) { timeElement.text( formatDates( event.start, (!slotDelta && !event.end) ? null : // no change, so don't display time range addMinutes(view.eventEnd(event), options.slotMinutes*slotDelta), view.option('timeFormat') ) ); prevSlotDelta = slotDelta; } }, stop: function(ev, ui) { view.trigger('eventResizeStop', this, event, ev, ui); if (slotDelta) { view.eventResize(this, event, 0, options.slotMinutes*slotDelta, ev, ui); }else{ eventElement.css('z-index', 8); view.showEvents(event, eventElement); // BUG: if event was really short, need to put title back in span } } }); } } /* Selecting -----------------------------------------------------------------------------*/ daySelectionManager = new SelectionManager( view, unselect, function(startDate, endDate, allDay) { renderDayOverlay( selectionMatrix, startDate, addDays(cloneDate(endDate), 1) ); }, clearSelection ); function daySelectionMousedown(ev) { if (view.option('selectable')) { selectionMatrix = buildDayMatrix(function(cell) { if (cell) { var d = dayColDate(cell.col); daySelectionManager.drag(d, d, true); }else{ daySelectionManager.drag(); } }); documentDragHelp( function(ev) { selectionMatrix.mouse(ev); }, function(ev) { daySelectionManager.dragStop(ev); } ); daySelectionManager.dragStart(ev); selectionMatrix.mouse(ev); return false; // prevent auto-unselect and text selection } } slotSelectionManager = new SelectionManager( view, unselect, renderSlotSelection, clearSelection ); function slotSelectionMousedown(ev) { if (view.option('selectable')) { selectionMatrix = buildSlotMatrix(function(cell) { if (cell) { var d = slotCellDate(cell.row, cell.origCol); slotSelectionManager.drag(d, addMinutes(cloneDate(d), options.slotMinutes), false); }else{ slotSelectionManager.drag(); } }); documentDragHelp( function(ev) { selectionMatrix.mouse(ev); }, function(ev) { slotSelectionManager.dragStop(ev); } ); slotSelectionManager.dragStart(ev); selectionMatrix.mouse(ev); return false; // prevent auto-unselect and text selection } } documentUnselectAuto(view, unselect); this.select = function(start, end, allDay) { if (allDay) { if (options.allDaySlot) { if (!end) { end = cloneDate(start); } selectionMatrix = buildDayMatrix(); daySelectionManager.select(start, end, allDay); } }else{ if (!end) { end = addMinutes(cloneDate(start), options.slotMinutes); } selectionMatrix = buildSlotMatrix(); slotSelectionManager.select(start, end, allDay); } }; function unselect() { slotSelectionManager.unselect(); daySelectionManager.unselect(); } this.unselect = unselect; /* Selecting drawing utils -----------------------------------------------------------------------------*/ function renderSlotSelection(startDate, endDate) { var helperOption = view.option('selectHelper'); if (helperOption) { var col = dayDiff(startDate, view.visStart); if (col >= 0 && col < colCnt) { // only works when times are on same day var rect = selectionMatrix.rect(0, col*dis+dit, 1, col*dis+dit+1, bodyContent); // only for horizontal coords var top = timePosition(startDate, startDate); var bottom = timePosition(startDate, endDate); if (bottom > top) { // protect against selections that are entirely before or after visible range rect.top = top; rect.height = bottom - top; rect.left += 2; rect.width -= 5; if ($.isFunction(helperOption)) { var helperRes = helperOption(startDate, endDate); if (helperRes) { rect.position = 'absolute'; rect.zIndex = 8; selectionHelper = $(helperRes) .css(rect) .appendTo(bodyContent); } }else{ selectionHelper = $(slotSegHtml( { title: '', start: startDate, end: endDate, className: [], editable: false }, rect, 'fc-event fc-event-vert fc-corner-top fc-corner-bottom ' )); if ($.browser.msie) { selectionHelper.find('span.fc-event-bg').hide(); // nested opacities mess up in IE, just hide } selectionHelper.css('opacity', view.option('dragOpacity')); } if (selectionHelper) { slotBind(selectionHelper); bodyContent.append(selectionHelper); setOuterWidth(selectionHelper, rect.width, true); // needs to be after appended setOuterHeight(selectionHelper, rect.height, true); } } } }else{ renderSlotOverlay(selectionMatrix, startDate, endDate); } } function clearSelection() { clearOverlays(); if (selectionHelper) { selectionHelper.remove(); selectionHelper = null; } } /* Semi-transparent Overlay Helpers -----------------------------------------------------*/ function renderDayOverlay(matrix, startDate, endDate) { var startCol, endCol; if (rtl) { startCol = dayDiff(endDate, view.visStart)*dis+dit+1; endCol = dayDiff(startDate, view.visStart)*dis+dit+1; }else{ startCol = dayDiff(startDate, view.visStart); endCol = dayDiff(endDate, view.visStart); } startCol = Math.max(0, startCol); endCol = Math.min(colCnt, endCol); if (startCol < endCol) { var rect = matrix.rect(0, startCol, 1, endCol, head); dayBind( view.renderOverlay(rect, head) ); } } function renderSlotOverlay(matrix, overlayStart, overlayEnd) { var dayStart = cloneDate(view.visStart); var dayEnd = addDays(cloneDate(dayStart), 1); for (var i=0; i= addMinutes(cloneDate(day), maxMinute)) { return bodyContent.height(); } var slotMinutes = options.slotMinutes, minutes = time.getHours()*60 + time.getMinutes() - minMinute, slotI = Math.floor(minutes / slotMinutes), slotTop = slotTopCache[slotI]; if (slotTop === undefined) { slotTop = slotTopCache[slotI] = body.find('tr:eq(' + slotI + ') td div')[0].offsetTop; } return Math.max(0, Math.round( slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes) )); } function buildDayMatrix(changeCallback, includeSlotArea) { var rowElements = options.allDaySlot ? head.find('td') : $([]); if (includeSlotArea) { rowElements = rowElements.add(body); } return new HoverMatrix(rowElements, bg.find('td'), changeCallback); } function buildSlotMatrix(changeCallback) { return new HoverMatrix(bodyTable.find('td'), bg.find('td'), changeCallback); } /* Date Utilities ----------------------------------------------------*/ function slotEventEnd(event) { if (event.end) { return cloneDate(event.end); }else{ return addMinutes(cloneDate(event.start), options.defaultEventMinutes); } } function dayOfWeekCol(dayOfWeek) { return ((dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt)*dis+dit; } // generating dates from cell row & columns function dayColDate(col) { return addDays(cloneDate(view.visStart), col*dis+dit); } function slotCellDate(row, col) { var d = dayColDate(col); addMinutes(d, minMinute + row*options.slotMinutes); return d; } } // count the number of colliding, higher-level segments (for event squishing) function countForwardSegs(levels) { var i, j, k, level, segForward, segBack; for (i=levels.length-1; i>0; i--) { level = levels[i]; for (j=0; j"); } if (e[0].parentNode != parent[0]) { e.appendTo(parent); } this.usedOverlays.push(e.css(rect).show()); return e; }, clearOverlays: function() { var e; while (e = this.usedOverlays.shift()) { this.unusedOverlays.push(e.hide().unbind()); } }, // common horizontal event resizing resizableDayEvent: function(event, eventElement, colWidth) { var view = this; if (!view.options.disableResizing && eventElement.resizable) { eventElement.resizable({ handles: view.options.isRTL ? {w:'div.ui-resizable-w'} : {e:'div.ui-resizable-e'}, grid: colWidth, minWidth: colWidth/2, // need this or else IE throws errors when too small containment: view.element.parent().parent(), // the main element... // ... a fix. wouldn't allow extending to last column in agenda views (jq ui bug?) start: function(ev, ui) { eventElement.css('z-index', 9); view.hideEvents(event, eventElement); view.trigger('eventResizeStart', this, event, ev, ui); }, stop: function(ev, ui) { view.trigger('eventResizeStop', this, event, ev, ui); // ui.size.width wasn't working with grid correctly, use .width() var dayDelta = Math.round((eventElement.width() - ui.originalSize.width) / colWidth); if (dayDelta) { view.eventResize(this, event, dayDelta, 0, ev, ui); }else{ eventElement.css('z-index', 8); view.showEvents(event, eventElement); } } }); } }, // attaches eventClick, eventMouseover, eventMouseout eventElementHandlers: function(event, eventElement) { var view = this; eventElement .click(function(ev) { if (!eventElement.hasClass('ui-draggable-dragging') && !eventElement.hasClass('ui-resizable-resizing')) { return view.trigger('eventClick', this, event, ev); } }) .hover( function(ev) { view.trigger('eventMouseover', this, event, ev); }, function(ev) { view.trigger('eventMouseout', this, event, ev); } ); }, // get a property from the 'options' object, using smart view naming option: function(name, viewName) { var v = this.options[name]; if (typeof v == 'object') { return smartProperty(v, viewName || this.name); } return v; }, // event rendering utilities sliceSegs: function(events, visEventEnds, start, end) { var segs = [], i, len=events.length, event, eventStart, eventEnd, segStart, segEnd, isStart, isEnd; for (i=0; i start && eventStart < end) { if (eventStart < start) { segStart = cloneDate(start); isStart = false; }else{ segStart = eventStart; isStart = true; } if (eventEnd > end) { segEnd = cloneDate(end); isEnd = false; }else{ segEnd = eventEnd; isEnd = true; } segs.push({ event: event, start: segStart, end: segEnd, isStart: isStart, isEnd: isEnd, msLength: segEnd - segStart }); } } return segs.sort(segCmp); } }; function lazySegBind(container, segs, bindHandlers) { container.unbind('mouseover').mouseover(function(ev) { var parent=ev.target, e, i, seg; while (parent != this) { e = parent; parent = parent.parentNode; } if ((i = e._fci) !== undefined) { e._fci = undefined; seg = segs[i]; bindHandlers(seg.event, seg.element, seg); $(ev.target).trigger(ev); } ev.stopPropagation(); }); } // event rendering calculation utilities function stackSegs(segs) { var levels = [], i, len = segs.length, seg, j, collide, k; for (i=0; i seg2.start && seg1.start < seg2.end; } function SelectionManager(view, initFunc, displayFunc, clearFunc) { var t = this; var selected = false; var initialElement; var initialRange; var start; var end; var allDay; t.dragStart = function(ev) { initFunc(); start = end = undefined; initialRange = undefined; initialElement = ev.currentTarget; }; t.drag = function(currentStart, currentEnd, currentAllDay) { if (currentStart) { var range = [currentStart, currentEnd]; if (!initialRange) { initialRange = range; } var dates = initialRange.concat(range).sort(cmp); start = dates[0]; end = dates[3]; allDay = currentAllDay; clearFunc(); displayFunc(cloneDate(start), cloneDate(end), allDay); }else{ // called with no arguments start = end = undefined; clearFunc(); } }; t.dragStop = function(ev) { if (start) { if (+initialRange[0] == +start && +initialRange[1] == +end) { view.trigger('dayClick', initialElement, start, allDay, ev); } _select(); } }; t.select = function(newStart, newEnd, newAllDay) { initFunc(); start = newStart; end = newEnd; allDay = newAllDay; displayFunc(cloneDate(start), cloneDate(end), allDay); _select(); }; function _select() { // just set the selected flag, and trigger selected = true; view.trigger('select', view, start, end, allDay); } function unselect() { if (selected) { selected = false; start = end = undefined; clearFunc(); view.trigger('unselect', view); } } t.unselect = unselect; } function documentDragHelp(mousemove, mouseup) { function _mouseup(ev) { mouseup(ev); $(document) .unbind('mousemove', mousemove) .unbind('mouseup', _mouseup); } $(document) .mousemove(mousemove) .mouseup(_mouseup); } function documentUnselectAuto(view, unselectFunc) { if (view.option('selectable') && view.option('unselectAuto')) { $(document).mousedown(function(ev) { var ignore = view.option('unselectCancel'); if (ignore) { if ($(ev.target).parents(ignore).length) { // could be optimized to stop after first match return; } } unselectFunc(); }); } } /* Date Math -----------------------------------------------------------------------------*/ var DAY_MS = 86400000, HOUR_MS = 3600000, MINUTE_MS = 60000; function addYears(d, n, keepTime) { d.setFullYear(d.getFullYear() + n); if (!keepTime) { clearTime(d); } return d; } function addMonths(d, n, keepTime) { // prevents day overflow/underflow if (+d) { // prevent infinite looping on invalid dates var m = d.getMonth() + n, check = cloneDate(d); check.setDate(1); check.setMonth(m); d.setMonth(m); if (!keepTime) { clearTime(d); } while (d.getMonth() != check.getMonth()) { d.setDate(d.getDate() + (d < check ? 1 : -1)); } } return d; } function addDays(d, n, keepTime) { // deals with daylight savings if (+d) { var dd = d.getDate() + n, check = cloneDate(d); check.setHours(9); // set to middle of day check.setDate(dd); d.setDate(dd); if (!keepTime) { clearTime(d); } fixDate(d, check); } return d; } fc.addDays = addDays; function fixDate(d, check) { // force d to be on check's YMD, for daylight savings purposes if (+d) { // prevent infinite looping on invalid dates while (d.getDate() != check.getDate()) { d.setTime(+d + (d < check ? 1 : -1) * HOUR_MS); } } } function addMinutes(d, n) { d.setMinutes(d.getMinutes() + n); return d; } function clearTime(d) { d.setHours(0); d.setMinutes(0); d.setSeconds(0); d.setMilliseconds(0); return d; } function cloneDate(d, dontKeepTime) { if (dontKeepTime) { return clearTime(new Date(+d)); } return new Date(+d); } fc.cloneDate = cloneDate; function zeroDate() { // returns a Date with time 00:00:00 and dateOfMonth=1 var i=0, d; do { d = new Date(1970, i++, 1); } while (d.getHours()); // != 0 return d; } function skipWeekend(date, inc, excl) { inc = inc || 1; while (!date.getDay() || (excl && date.getDay()==1 || !excl && date.getDay()==6)) { addDays(date, inc); } return date; } function dayDiff(d1, d2) { // d1 - d2 return Math.round((cloneDate(d1, true) - cloneDate(d2, true)) / DAY_MS); } /* Date Parsing -----------------------------------------------------------------------------*/ var parseDate = fc.parseDate = function(s) { if (typeof s == 'object') { // already a Date object return s; } if (typeof s == 'number') { // a UNIX timestamp return new Date(s * 1000); } if (typeof s == 'string') { if (s.match(/^\d+$/)) { // a UNIX timestamp return new Date(parseInt(s) * 1000); } return parseISO8601(s, true) || (s ? new Date(s) : null); } // TODO: never return invalid dates (like from new Date()), return null instead return null; }; var parseISO8601 = fc.parseISO8601 = function(s, ignoreTimezone) { // derived from http://delete.me.uk/2005/03/iso8601.html // TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?$/); if (!m) { return null; } var date = new Date(m[1], 0, 1), check = new Date(m[1], 0, 1, 9, 0), offset = 0; if (m[3]) { date.setMonth(m[3] - 1); check.setMonth(m[3] - 1); } if (m[5]) { date.setDate(m[5]); check.setDate(m[5]); } fixDate(date, check); if (m[7]) { date.setHours(m[7]); } if (m[8]) { date.setMinutes(m[8]); } if (m[10]) { date.setSeconds(m[10]); } if (m[12]) { date.setMilliseconds(Number("0." + m[12]) * 1000); } fixDate(date, check); if (!ignoreTimezone) { if (m[14]) { offset = Number(m[16]) * 60 + Number(m[17]); offset *= m[15] == '-' ? 1 : -1; } offset -= date.getTimezoneOffset(); } return new Date(+date + (offset * 60 * 1000)); }; var parseTime = fc.parseTime = function(s) { // returns minutes since start of day if (typeof s == 'number') { // an hour return s * 60; } if (typeof s == 'object') { // a Date object return s.getHours() * 60 + s.getMinutes(); } var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/); if (m) { var h = parseInt(m[1]); if (m[3]) { h %= 12; if (m[3].toLowerCase().charAt(0) == 'p') { h += 12; } } return h * 60 + (m[2] ? parseInt(m[2]) : 0); } }; /* Date Formatting -----------------------------------------------------------------------------*/ var formatDate = fc.formatDate = function(date, format, options) { return formatDates(date, null, format, options); }; var formatDates = fc.formatDates = function(date1, date2, format, options) { options = options || defaults; var date = date1, otherDate = date2, i, len = format.length, c, i2, formatter, res = ''; for (i=0; ii; i2--) { if (formatter = dateFormatters[format.substring(i, i2)]) { if (date) { res += formatter(date, options); } i = i2 - 1; break; } } if (i2 == i) { if (date) { res += c; } } } } return res; }; var dateFormatters = { s : function(d) { return d.getSeconds() }, ss : function(d) { return zeroPad(d.getSeconds()) }, m : function(d) { return d.getMinutes() }, mm : function(d) { return zeroPad(d.getMinutes()) }, h : function(d) { return d.getHours() % 12 || 12 }, hh : function(d) { return zeroPad(d.getHours() % 12 || 12) }, H : function(d) { return d.getHours() }, HH : function(d) { return zeroPad(d.getHours()) }, d : function(d) { return d.getDate() }, dd : function(d) { return zeroPad(d.getDate()) }, ddd : function(d,o) { return o.dayNamesShort[d.getDay()] }, dddd: function(d,o) { return o.dayNames[d.getDay()] }, M : function(d) { return d.getMonth() + 1 }, MM : function(d) { return zeroPad(d.getMonth() + 1) }, MMM : function(d,o) { return o.monthNamesShort[d.getMonth()] }, MMMM: function(d,o) { return o.monthNames[d.getMonth()] }, yy : function(d) { return (d.getFullYear()+'').substring(2) }, yyyy: function(d) { return d.getFullYear() }, t : function(d) { return d.getHours() < 12 ? 'a' : 'p' }, tt : function(d) { return d.getHours() < 12 ? 'am' : 'pm' }, T : function(d) { return d.getHours() < 12 ? 'A' : 'P' }, TT : function(d) { return d.getHours() < 12 ? 'AM' : 'PM' }, u : function(d) { return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") }, S : function(d) { var date = d.getDate(); if (date > 10 && date < 20) { return 'th'; } return ['st', 'nd', 'rd'][date%10-1] || 'th'; } }; /* Element Dimensions -----------------------------------------------------------------------------*/ function setOuterWidth(element, width, includeMargins) { element.each(function(i, _element) { _element.style.width = width - hsides(_element, includeMargins) + 'px'; }); } function setOuterHeight(element, height, includeMargins) { element.each(function(i, _element) { _element.style.height = height - vsides(_element, includeMargins) + 'px'; }); } function hsides(_element, includeMargins) { return (parseFloat(jQuery.curCSS(_element, 'paddingLeft', true)) || 0) + (parseFloat(jQuery.curCSS(_element, 'paddingRight', true)) || 0) + (parseFloat(jQuery.curCSS(_element, 'borderLeftWidth', true)) || 0) + (parseFloat(jQuery.curCSS(_element, 'borderRightWidth', true)) || 0) + (includeMargins ? hmargins(_element) : 0); } function hmargins(_element) { return (parseFloat(jQuery.curCSS(_element, 'marginLeft', true)) || 0) + (parseFloat(jQuery.curCSS(_element, 'marginRight', true)) || 0); } function vsides(_element, includeMargins) { return (parseFloat(jQuery.curCSS(_element, 'paddingTop', true)) || 0) + (parseFloat(jQuery.curCSS(_element, 'paddingBottom', true)) || 0) + (parseFloat(jQuery.curCSS(_element, 'borderTopWidth', true)) || 0) + (parseFloat(jQuery.curCSS(_element, 'borderBottomWidth', true)) || 0) + (includeMargins ? vmargins(_element) : 0); } function vmargins(_element) { return (parseFloat(jQuery.curCSS(_element, 'marginTop', true)) || 0) + (parseFloat(jQuery.curCSS(_element, 'marginBottom', true)) || 0); } function setMinHeight(element, h) { h = typeof h == 'number' ? h + 'px' : h; element[0].style.cssText += ';min-height:' + h + ';_height:' + h; } /* Position Calculation -----------------------------------------------------------------------------*/ // nasty bugs in opera 9.25 // position()'s top returning incorrectly with TR/TD or elements within TD var topBug; function topCorrect(tr) { // tr/th/td or anything else if (topBug !== false) { var cell; if (tr.is('th,td')) { tr = (cell = tr).parent(); } if (topBug === undefined && tr.is('tr')) { topBug = tr.position().top != tr.children().position().top; } if (topBug) { return tr.parent().position().top + (cell ? tr.position().top - cell.position().top : 0); } } return 0; } /* Hover Matrix -----------------------------------------------------------------------------*/ function HoverMatrix(rowElements, colElements, changeCallback) { var t=this, tops=[], lefts=[], origRow, origCol, currRow, currCol, e; $.each(rowElements, function(i, _e) { e = $(_e); tops.push(e.offset().top + topCorrect(e)); }); tops.push(tops[tops.length-1] + e.outerHeight()); $.each(colElements, function(i, _e) { e = $(_e); lefts.push(e.offset().left); }); lefts.push(lefts[lefts.length-1] + e.outerWidth()); t.mouse = function(ev) { var x = ev.pageX; var y = ev.pageY; var r, c; for (r=0; r=tops[r]; r++) {} for (c=0; c=lefts[c]; c++) {} r = r >= tops.length ? -1 : r - 1; c = c >= lefts.length ? -1 : c - 1; if (r != currRow || c != currCol) { currRow = r; currCol = c; if (r == -1 || c == -1) { t.cell = null; }else{ if (origRow === undefined) { origRow = r; origCol = c; } t.cell = { row: r, col: c, top: tops[r], left: lefts[c], width: lefts[c+1] - lefts[c], height: tops[r+1] - tops[r], origRow: origRow, origCol: origCol, isOrig: r==origRow && c==origCol, rowDelta: r-origRow, colDelta: c-origCol }; } changeCallback(t.cell); } }; t.rect = function(row0, col0, row1, col1, originElement) { // row1,col1 are exclusive var origin = originElement.offset(); return { top: tops[row0] - origin.top, left: lefts[col0] - origin.left, width: lefts[col1] - lefts[col0], height: tops[row1] - tops[row0] }; }; } /* Misc Utils -----------------------------------------------------------------------------*/ var undefined, dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; function zeroPad(n) { return (n < 10 ? '0' : '') + n; } function smartProperty(obj, name) { // get a camel-cased/namespaced property of an object if (obj[name] !== undefined) { return obj[name]; } var parts = name.split(/(?=[A-Z])/), i=parts.length-1, res; for (; i>=0; i--) { res = obj[parts[i].toLowerCase()]; if (res !== undefined) { return res; } } return obj['']; } function htmlEscape(s) { return s .replace(/&/g, '&') .replace(//g, '>') .replace(/'/g, ''') .replace(/"/g, '"'); } function HorizontalPositionCache(getElement) { var t = this, elements = {}, lefts = {}, rights = {}; function e(i) { return elements[i] = elements[i] || getElement(i); } t.left = function(i) { return lefts[i] = lefts[i] === undefined ? e(i).position().left : lefts[i]; }; t.right = function(i) { return rights[i] = rights[i] === undefined ? t.left(i) + e(i).width() : rights[i]; }; t.clear = function() { elements = {}; lefts = {}; rights = {}; }; } function cssKey(_element) { return _element.id + '/' + _element.className + '/' + _element.style.cssText.replace(/(^|;)\s*(top|left|width|height)\s*:[^;]*/ig, ''); } function cmp(a, b) { return a - b; } function exclEndDay(event) { if (event.end) { return _exclEndDay(event.end, event.allDay); }else{ return addDays(cloneDate(event.start), 1); } } function _exclEndDay(end, allDay) { end = cloneDate(end); return allDay || end.getHours() || end.getMinutes() ? addDays(end, 1) : end; } function disableTextSelection(element) { element .attr('unselectable', 'on') .css('MozUserSelect', 'none') .bind('selectstart.ui', function() { return false; }); } /* function enableTextSelection(element) { element .attr('unselectable', 'off') .css('MozUserSelect', '') .unbind('selectstart.ui'); } */ })(jQuery);