diff --git a/bower.json b/bower.json index 6278156..c68dc85 100644 --- a/bower.json +++ b/bower.json @@ -1,12 +1,20 @@ { "name": "fullcalendar", "version": "2.1.1", +<<<<<<< HEAD "description": "Full-sized drag & drop event calendar with resources", "keywords": [ "calendar", "event", "full-sized", "Resources" +======= + "description": "Full-sized drag & drop event calendar", + "keywords": [ + "calendar", + "event", + "full-sized" +>>>>>>> upstream/master ], "homepage": "http://arshaw.com/fullcalendar/", "dependencies": { @@ -35,4 +43,8 @@ "!/license.*", "!/readme.*" ] -} \ No newline at end of file +<<<<<<< HEAD +} +======= +} +>>>>>>> upstream/master diff --git a/build/tasks/generateLanguages.js b/build/tasks/generateLanguages.js index dee8510..d234b0b 100644 --- a/build/tasks/generateLanguages.js +++ b/build/tasks/generateLanguages.js @@ -208,4 +208,8 @@ module.exports = function(grunt) { } -}; \ No newline at end of file +<<<<<<< HEAD +}; +======= +}; +>>>>>>> upstream/master diff --git a/demos/agenda-views.html b/demos/agenda-views.html index 4a41578..eb2adf0 100644 --- a/demos/agenda-views.html +++ b/demos/agenda-views.html @@ -102,4 +102,4 @@
- + \ No newline at end of file diff --git a/demos/default.html b/demos/default.html index 21591c2..db70acb 100644 --- a/demos/default.html +++ b/demos/default.html @@ -97,4 +97,4 @@
- + \ No newline at end of file diff --git a/demos/external-dragging.html b/demos/external-dragging.html index e6020a6..2a9df06 100644 --- a/demos/external-dragging.html +++ b/demos/external-dragging.html @@ -150,4 +150,4 @@ - + \ No newline at end of file diff --git a/demos/json/events.json b/demos/json/events.json index 4a71a12..25a2186 100644 --- a/demos/json/events.json +++ b/demos/json/events.json @@ -1,3 +1,4 @@ +<<<<<<< HEAD [{ "title": "R1-R2: Lunch 12.15-14.45", "start": "2014-08-19T12:15:00", @@ -33,3 +34,61 @@ "allDay": false, "resources": "resource2" }] +======= +[ + { + "title": "All Day Event", + "start": "2014-09-01" + }, + { + "title": "Long Event", + "start": "2014-09-07", + "end": "2014-09-10" + }, + { + "id": "999", + "title": "Repeating Event", + "start": "2014-09-09T16:00:00-05:00" + }, + { + "id": "999", + "title": "Repeating Event", + "start": "2014-09-16T16:00:00-05:00" + }, + { + "title": "Conference", + "start": "2014-09-11", + "end": "2014-09-13" + }, + { + "title": "Meeting", + "start": "2014-09-12T10:30:00-05:00", + "end": "2014-09-12T12:30:00-05:00" + }, + { + "title": "Lunch", + "start": "2014-09-12T12:00:00-05:00" + }, + { + "title": "Meeting", + "start": "2014-09-12T14:30:00-05:00" + }, + { + "title": "Happy Hour", + "start": "2014-09-12T17:30:00-05:00" + }, + { + "title": "Dinner", + "start": "2014-09-12T20:00:00" + }, + { + "title": "Birthday Party", + "start": "2014-09-13T07:00:00-05:00" + }, + { + "title": "Click for Google", + "url": "http://google.com/", + "start": "2014-09-28" + } +] +>>>>>>> upstream/master diff --git a/demos/languages.html b/demos/languages.html index e1427ac..ac60939 100644 --- a/demos/languages.html +++ b/demos/languages.html @@ -146,4 +146,4 @@
- + \ No newline at end of file diff --git a/demos/selectable.html b/demos/selectable.html index 425c4e7..63cc72e 100644 --- a/demos/selectable.html +++ b/demos/selectable.html @@ -117,4 +117,4 @@
- + \ No newline at end of file diff --git a/demos/timezones.html b/demos/timezones.html index 349ff16..00ed2e7 100644 --- a/demos/timezones.html +++ b/demos/timezones.html @@ -125,4 +125,4 @@
- + \ No newline at end of file diff --git a/dist/fullcalendar.css b/dist/fullcalendar.css new file mode 100644 index 0000000..a5b9d33 --- /dev/null +++ b/dist/fullcalendar.css @@ -0,0 +1,933 @@ +/*! + * FullCalendar v2.1.1 Stylesheet + * Docs & License: http://arshaw.com/fullcalendar/ + * (c) 2013 Adam Shaw + */ + + +.fc { + direction: ltr; + text-align: left; +} + +.fc-rtl { + text-align: right; +} + +body .fc { /* extra precedence to overcome jqui */ + font-size: 1em; +} + + +/* Colors +--------------------------------------------------------------------------------------------------*/ + +.fc-unthemed th, +.fc-unthemed td, +.fc-unthemed hr, +.fc-unthemed thead, +.fc-unthemed tbody, +.fc-unthemed .fc-row, +.fc-unthemed .fc-popover { + border-color: #ddd; +} + +.fc-unthemed .fc-popover { + background-color: #fff; +} + +.fc-unthemed hr, +.fc-unthemed .fc-popover .fc-header { + background: #eee; +} + +.fc-unthemed .fc-popover .fc-header .fc-close { + color: #666; +} + +.fc-unthemed .fc-today { + background: #fcf8e3; +} + +.fc-highlight { /* when user is selecting cells */ + background: #bce8f1; + opacity: .3; + filter: alpha(opacity=30); /* for IE */ +} + + +/* Icons (inline elements with styled text that mock arrow icons) +--------------------------------------------------------------------------------------------------*/ + +.fc-icon { + display: inline-block; + font-size: 2em; + line-height: .5em; + height: .5em; /* will make the total height 1em */ + font-family: "Courier New", Courier, monospace; +} + +.fc-icon-left-single-arrow:after { + content: "\02039"; + font-weight: bold; +} + +.fc-icon-right-single-arrow:after { + content: "\0203A"; + font-weight: bold; +} + +.fc-icon-left-double-arrow:after { + content: "\000AB"; +} + +.fc-icon-right-double-arrow:after { + content: "\000BB"; +} + +.fc-icon-x:after { + content: "\000D7"; +} + + +/* Buttons (styled ' + ) + .click(function() { + // don't process clicks for disabled buttons + if (!button.hasClass(tm + '-state-disabled')) { + + buttonClick(); + + // after the click action, if the button becomes the "active" tab, or disabled, + // it should never have a hover class, so remove it now. + if ( + button.hasClass(tm + '-state-active') || + button.hasClass(tm + '-state-disabled') + ) { + button.removeClass(tm + '-state-hover'); + } + } + }) + .mousedown(function() { + // the *down* effect (mouse pressed in). + // only on buttons that are not the "active" tab, or disabled + button + .not('.' + tm + '-state-active') + .not('.' + tm + '-state-disabled') + .addClass(tm + '-state-down'); + }) + .mouseup(function() { + // undo the *down* effect + button.removeClass(tm + '-state-down'); + }) + .hover( + function() { + // the *hover* effect. + // only on buttons that are not the "active" tab, or disabled + button + .not('.' + tm + '-state-active') + .not('.' + tm + '-state-disabled') + .addClass(tm + '-state-hover'); + }, + function() { + // undo the *hover* effect + button + .removeClass(tm + '-state-hover') + .removeClass(tm + '-state-down'); // if mouseleave happens before mouseup + } + ); + + groupChildren = groupChildren.add(button); + } + } + }); + + if (isOnlyButtons) { + groupChildren + .first().addClass(tm + '-corner-left').end() + .last().addClass(tm + '-corner-right').end(); + } + + if (groupChildren.length > 1) { + groupEl = $('
'); + if (isOnlyButtons) { + groupEl.addClass('fc-button-group'); + } + groupEl.append(groupChildren); + sectionEl.append(groupEl); + } + else { + sectionEl.append(groupChildren); // 1 or 0 children + } + }); + } + + return sectionEl; + } + + + function updateTitle(text) { + el.find('h2').text(text); + } + + + function activateButton(buttonName) { + el.find('.fc-' + buttonName + '-button') + .addClass(tm + '-state-active'); + } + + + function deactivateButton(buttonName) { + el.find('.fc-' + buttonName + '-button') + .removeClass(tm + '-state-active'); + } + + + function disableButton(buttonName) { + el.find('.fc-' + buttonName + '-button') + .attr('disabled', 'disabled') + .addClass(tm + '-state-disabled'); + } + + + function enableButton(buttonName) { + el.find('.fc-' + buttonName + '-button') + .removeAttr('disabled') + .removeClass(tm + '-state-disabled'); + } + + + function getViewsWithButtons() { + return viewsWithButtons; + } + +} + +;; + +fc.sourceNormalizers = []; +fc.sourceFetchers = []; + +var ajaxDefaults = { + dataType: 'json', + cache: false +}; + +var eventGUID = 1; + + +function EventManager(options) { // assumed to be a calendar + var t = this; + + + // exports + t.isFetchNeeded = isFetchNeeded; + t.fetchEvents = fetchEvents; + t.addEventSource = addEventSource; + t.removeEventSource = removeEventSource; + t.updateEvent = updateEvent; + t.renderEvent = renderEvent; + t.removeEvents = removeEvents; + t.clientEvents = clientEvents; + t.mutateEvent = mutateEvent; + + + // imports + var trigger = t.trigger; + var getView = t.getView; + var reportEvents = t.reportEvents; + var getEventEnd = t.getEventEnd; + + + // locals + var stickySource = { events: [] }; + var sources = [ stickySource ]; + var rangeStart, rangeEnd; + var currentFetchID = 0; + var pendingSourceCnt = 0; + var loadingLevel = 0; + var cache = []; + + + $.each( + (options.events ? [ options.events ] : []).concat(options.eventSources || []), + function(i, sourceInput) { + var source = buildEventSource(sourceInput); + if (source) { + sources.push(source); + } + } + ); + + + + /* Fetching + -----------------------------------------------------------------------------*/ + + + function isFetchNeeded(start, end) { + return !rangeStart || // nothing has been fetched yet? + // or, a part of the new range is outside of the old range? (after normalizing) + start.clone().stripZone() < rangeStart.clone().stripZone() || + end.clone().stripZone() > rangeEnd.clone().stripZone(); + } + + + function fetchEvents(start, end) { + rangeStart = start; + rangeEnd = end; + cache = []; + var fetchID = ++currentFetchID; + var len = sources.length; + pendingSourceCnt = len; + for (var i=0; i cells, find the cell with the largest natural width and set the widths of all the +// cells to be that width. +// PREREQUISITE: if you want a cell to take up width, it needs to have a single inner element w/ display:inline +function matchCellWidths(els) { + var maxInnerWidth = 0; + + els.find('> *').each(function(i, innerEl) { + var innerWidth = $(innerEl).outerWidth(); + if (innerWidth > maxInnerWidth) { + maxInnerWidth = innerWidth; + } + }); + + maxInnerWidth++; // sometimes not accurate of width the text needs to stay on one line. insurance + + els.width(maxInnerWidth); + + return maxInnerWidth; +} + + +// Turns a container element into a scroller if its contents is taller than the allotted height. +// Returns true if the element is now a scroller, false otherwise. +// NOTE: this method is best because it takes weird zooming dimensions into account +function setPotentialScroller(containerEl, height) { + containerEl.height(height).addClass('fc-scroller'); + + // are scrollbars needed? + if (containerEl[0].scrollHeight - 1 > containerEl[0].clientHeight) { // !!! -1 because IE is often off-by-one :( + return true; + } + + unsetScroller(containerEl); // undo + return false; +} + + +// Takes an element that might have been a scroller, and turns it back into a normal element. +function unsetScroller(containerEl) { + containerEl.height('').removeClass('fc-scroller'); +} + + +/* General DOM Utilities +----------------------------------------------------------------------------------------------------------------------*/ + + +// borrowed from https://github.com/jquery/jquery-ui/blob/1.11.0/ui/core.js#L51 +function getScrollParent(el) { + var position = el.css('position'), + scrollParent = el.parents().filter(function() { + var parent = $(this); + return (/(auto|scroll)/).test( + parent.css('overflow') + parent.css('overflow-y') + parent.css('overflow-x') + ); + }).eq(0); + + return position === 'fixed' || !scrollParent.length ? $(el[0].ownerDocument || document) : scrollParent; +} + + +// Given a container element, return an object with the pixel values of the left/right scrollbars. +// Left scrollbars might occur on RTL browsers (IE maybe?) but I have not tested. +// PREREQUISITE: container element must have a single child with display:block +function getScrollbarWidths(container) { + var containerLeft = container.offset().left; + var containerRight = containerLeft + container.width(); + var inner = container.children(); + var innerLeft = inner.offset().left; + var innerRight = innerLeft + inner.outerWidth(); + + return { + left: innerLeft - containerLeft, + right: containerRight - innerRight + }; +} + + +// Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac) +function isPrimaryMouseButton(ev) { + return ev.which == 1 && !ev.ctrlKey; +} + + +/* FullCalendar-specific Misc Utilities +----------------------------------------------------------------------------------------------------------------------*/ + + +// Creates a basic segment with the intersection of the two ranges. Returns undefined if no intersection. +// Expects all dates to be normalized to the same timezone beforehand. +function intersectionToSeg(subjectStart, subjectEnd, intervalStart, intervalEnd) { + var segStart, segEnd; + var isStart, isEnd; + + if (subjectEnd > intervalStart && subjectStart < intervalEnd) { // in bounds at all? + + if (subjectStart >= intervalStart) { + segStart = subjectStart.clone(); + isStart = true; + } + else { + segStart = intervalStart.clone(); + isStart = false; + } + + if (subjectEnd <= intervalEnd) { + segEnd = subjectEnd.clone(); + isEnd = true; + } + else { + segEnd = intervalEnd.clone(); + isEnd = false; + } + + return { + start: segStart, + end: segEnd, + isStart: isStart, + isEnd: isEnd + }; + } +} + + +function smartProperty(obj, name) { // get a camel-cased/namespaced property of an object + obj = obj || {}; + 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['default']; +} + + +/* Date Utilities +----------------------------------------------------------------------------------------------------------------------*/ + +var dayIDs = [ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ]; + + +// Diffs the two moments into a Duration where full-days are recorded first, then the remaining time. +// Moments will have their timezones normalized. +function dayishDiff(a, b) { + return moment.duration({ + days: a.clone().stripTime().diff(b.clone().stripTime(), 'days'), + ms: a.time() - b.time() + }); +} + + +function isNativeDate(input) { + return Object.prototype.toString.call(input) === '[object Date]' || input instanceof Date; +} + + +function dateCompare(a, b) { // works with Moments and native Dates + return a - b; +} + + +/* General Utilities +----------------------------------------------------------------------------------------------------------------------*/ + +fc.applyAll = applyAll; // export + + +// Create an object that has the given prototype. Just like Object.create +function createObject(proto) { + var f = function() {}; + f.prototype = proto; + return new f(); +} + + +// Copies specifically-owned (non-protoype) properties of `b` onto `a`. +// FYI, $.extend would copy *all* properties of `b` onto `a`. +function extend(a, b) { + for (var i in b) { + if (b.hasOwnProperty(i)) { + a[i] = b[i]; + } + } +} + + +function applyAll(functions, thisObj, args) { + if ($.isFunction(functions)) { + functions = [ functions ]; + } + if (functions) { + var i; + var ret; + for (i=0; i/g, '>') + .replace(/'/g, ''') + .replace(/"/g, '"') + .replace(/\n/g, '
'); +} + + +function stripHtmlEntities(text) { + return text.replace(/&.*?;/g, ''); +} + + +function capitaliseFirstLetter(str) { + return str.charAt(0).toUpperCase() + str.slice(1); +} + + +// Returns a function, that, as long as it continues to be invoked, will not +// be triggered. The function will be called after it stops being called for +// N milliseconds. +// https://github.com/jashkenas/underscore/blob/1.6.0/underscore.js#L714 +function debounce(func, wait) { + var timeoutId; + var args; + var context; + var timestamp; // of most recent call + var later = function() { + var last = +new Date() - timestamp; + if (last < wait && last > 0) { + timeoutId = setTimeout(later, wait - last); + } + else { + timeoutId = null; + func.apply(context, args); + if (!timeoutId) { + context = args = null; + } + } + }; + + return function() { + context = this; + args = arguments; + timestamp = +new Date(); + if (!timeoutId) { + timeoutId = setTimeout(later, wait); + } + }; +} + +;; + +var ambigDateOfMonthRegex = /^\s*\d{4}-\d\d$/; +var ambigTimeOrZoneRegex = + /^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?)?$/; + + +// Creating +// ------------------------------------------------------------------------------------------------- + +// Creates a new moment, similar to the vanilla moment(...) constructor, but with +// extra features (ambiguous time, enhanced formatting). When gived an existing moment, +// it will function as a clone (and retain the zone of the moment). Anything else will +// result in a moment in the local zone. +fc.moment = function() { + return makeMoment(arguments); +}; + +// Sames as fc.moment, but forces the resulting moment to be in the UTC timezone. +fc.moment.utc = function() { + var mom = makeMoment(arguments, true); + + // Force it into UTC because makeMoment doesn't guarantee it. + if (mom.hasTime()) { // don't give ambiguously-timed moments a UTC zone + mom.utc(); + } + + return mom; +}; + +// Same as fc.moment, but when given an ISO8601 string, the timezone offset is preserved. +// ISO8601 strings with no timezone offset will become ambiguously zoned. +fc.moment.parseZone = function() { + return makeMoment(arguments, true, true); +}; + +// Builds an FCMoment from args. When given an existing moment, it clones. When given a native +// Date, or called with no arguments (the current time), the resulting moment will be local. +// Anything else needs to be "parsed" (a string or an array), and will be affected by: +// parseAsUTC - if there is no zone information, should we parse the input in UTC? +// parseZone - if there is zone information, should we force the zone of the moment? +function makeMoment(args, parseAsUTC, parseZone) { + var input = args[0]; + var isSingleString = args.length == 1 && typeof input === 'string'; + var isAmbigTime; + var isAmbigZone; + var ambigMatch; + var output; // an object with fields for the new FCMoment object + + if (moment.isMoment(input)) { + output = moment.apply(null, args); // clone it + + // the ambig properties have not been preserved in the clone, so reassign them + if (input._ambigTime) { + output._ambigTime = true; + } + if (input._ambigZone) { + output._ambigZone = true; + } + } + else if (isNativeDate(input) || input === undefined) { + output = moment.apply(null, args); // will be local + } + else { // "parsing" is required + isAmbigTime = false; + isAmbigZone = false; + + if (isSingleString) { + if (ambigDateOfMonthRegex.test(input)) { + // accept strings like '2014-05', but convert to the first of the month + input += '-01'; + args = [ input ]; // for when we pass it on to moment's constructor + isAmbigTime = true; + isAmbigZone = true; + } + else if ((ambigMatch = ambigTimeOrZoneRegex.exec(input))) { + isAmbigTime = !ambigMatch[5]; // no time part? + isAmbigZone = true; + } + } + else if ($.isArray(input)) { + // arrays have no timezone information, so assume ambiguous zone + isAmbigZone = true; + } + // otherwise, probably a string with a format + + if (parseAsUTC) { + output = moment.utc.apply(moment, args); + } + else { + output = moment.apply(null, args); + } + + if (isAmbigTime) { + output._ambigTime = true; + output._ambigZone = true; // ambiguous time always means ambiguous zone + } + else if (parseZone) { // let's record the inputted zone somehow + if (isAmbigZone) { + output._ambigZone = true; + } + else if (isSingleString) { + output.zone(input); // if not a valid zone, will assign UTC + } + } + } + + return new FCMoment(output); +} + +// Our subclass of Moment. +// Accepts an object with the internal Moment properties that should be copied over to +// `this` object (most likely another Moment object). The values in this data must not +// be referenced by anything else (two moments sharing a Date object for example). +function FCMoment(internalData) { + extend(this, internalData); +} + +// Chain the prototype to Moment's +FCMoment.prototype = createObject(moment.fn); + +// We need this because Moment's implementation won't create an FCMoment, +// nor will it copy over the ambig flags. +FCMoment.prototype.clone = function() { + return makeMoment([ this ]); +}; + + +// Time-of-day +// ------------------------------------------------------------------------------------------------- + +// GETTER +// Returns a Duration with the hours/minutes/seconds/ms values of the moment. +// If the moment has an ambiguous time, a duration of 00:00 will be returned. +// +// SETTER +// You can supply a Duration, a Moment, or a Duration-like argument. +// When setting the time, and the moment has an ambiguous time, it then becomes unambiguous. +FCMoment.prototype.time = function(time) { + if (time == null) { // getter + return moment.duration({ + hours: this.hours(), + minutes: this.minutes(), + seconds: this.seconds(), + milliseconds: this.milliseconds() + }); + } + else { // setter + + delete this._ambigTime; // mark that the moment now has a time + + if (!moment.isDuration(time) && !moment.isMoment(time)) { + time = moment.duration(time); + } + + // The day value should cause overflow (so 24 hours becomes 00:00:00 of next day). + // Only for Duration times, not Moment times. + var dayHours = 0; + if (moment.isDuration(time)) { + dayHours = Math.floor(time.asDays()) * 24; + } + + // We need to set the individual fields. + // Can't use startOf('day') then add duration. In case of DST at start of day. + return this.hours(dayHours + time.hours()) + .minutes(time.minutes()) + .seconds(time.seconds()) + .milliseconds(time.milliseconds()); + } +}; + +// Converts the moment to UTC, stripping out its time-of-day and timezone offset, +// but preserving its YMD. A moment with a stripped time will display no time +// nor timezone offset when .format() is called. +FCMoment.prototype.stripTime = function() { + var a = this.toArray(); // year,month,date,hours,minutes,seconds as an array + + // set the internal UTC flag + moment.fn.utc.call(this); // call the original method, because we don't want to affect _ambigZone + + this.year(a[0]) // TODO: find a way to do this in one shot + .month(a[1]) + .date(a[2]) + .hours(0) + .minutes(0) + .seconds(0) + .milliseconds(0); + + // Mark the time as ambiguous. This needs to happen after the .utc() call, which calls .zone(), which + // clears all ambig flags. Same concept with the .year/month/date calls in the case of moment-timezone. + this._ambigTime = true; + this._ambigZone = true; // if ambiguous time, also ambiguous timezone offset + + return this; // for chaining +}; + +// Returns if the moment has a non-ambiguous time (boolean) +FCMoment.prototype.hasTime = function() { + return !this._ambigTime; +}; + + +// Timezone +// ------------------------------------------------------------------------------------------------- + +// Converts the moment to UTC, stripping out its timezone offset, but preserving its +// YMD and time-of-day. A moment with a stripped timezone offset will display no +// timezone offset when .format() is called. +FCMoment.prototype.stripZone = function() { + var a = this.toArray(); // year,month,date,hours,minutes,seconds as an array + var wasAmbigTime = this._ambigTime; + + moment.fn.utc.call(this); // set the internal UTC flag + + this.year(a[0]) // TODO: find a way to do this in one shot + .month(a[1]) + .date(a[2]) + .hours(a[3]) + .minutes(a[4]) + .seconds(a[5]) + .milliseconds(a[6]); + + if (wasAmbigTime) { + // the above call to .utc()/.zone() unfortunately clears the ambig flags, so reassign + this._ambigTime = true; + } + + // Mark the zone as ambiguous. This needs to happen after the .utc() call, which calls .zone(), which + // clears all ambig flags. Same concept with the .year/month/date calls in the case of moment-timezone. + this._ambigZone = true; + + return this; // for chaining +}; + +// Returns of the moment has a non-ambiguous timezone offset (boolean) +FCMoment.prototype.hasZone = function() { + return !this._ambigZone; +}; + +// this method implicitly marks a zone +FCMoment.prototype.zone = function(tzo) { + + if (tzo != null) { + // FYI, the delete statements need to be before the .zone() call or else chaos ensues + // for reasons I don't understand. + delete this._ambigTime; + delete this._ambigZone; + } + + return moment.fn.zone.apply(this, arguments); +}; + +// this method implicitly marks a zone +FCMoment.prototype.local = function() { + var a = this.toArray(); // year,month,date,hours,minutes,seconds as an array + var wasAmbigZone = this._ambigZone; + + // will happen anyway via .local()/.zone(), but don't want to rely on internal implementation + delete this._ambigTime; + delete this._ambigZone; + + moment.fn.local.apply(this, arguments); + + if (wasAmbigZone) { + // If the moment was ambiguously zoned, the date fields were stored as UTC. + // We want to preserve these, but in local time. + this.year(a[0]) // TODO: find a way to do this in one shot + .month(a[1]) + .date(a[2]) + .hours(a[3]) + .minutes(a[4]) + .seconds(a[5]) + .milliseconds(a[6]); + } + + return this; // for chaining +}; + +// this method implicitly marks a zone +FCMoment.prototype.utc = function() { + + // will happen anyway via .local()/.zone(), but don't want to rely on internal implementation + delete this._ambigTime; + delete this._ambigZone; + + return moment.fn.utc.apply(this, arguments); +}; + + +// Formatting +// ------------------------------------------------------------------------------------------------- + +FCMoment.prototype.format = function() { + if (arguments[0]) { + return formatDate(this, arguments[0]); // our extended formatting + } + if (this._ambigTime) { + return momentFormat(this, 'YYYY-MM-DD'); + } + if (this._ambigZone) { + return momentFormat(this, 'YYYY-MM-DD[T]HH:mm:ss'); + } + return momentFormat(this); // default moment original formatting +}; + +FCMoment.prototype.toISOString = function() { + if (this._ambigTime) { + return momentFormat(this, 'YYYY-MM-DD'); + } + if (this._ambigZone) { + return momentFormat(this, 'YYYY-MM-DD[T]HH:mm:ss'); + } + return moment.fn.toISOString.apply(this, arguments); +}; + + +// Querying +// ------------------------------------------------------------------------------------------------- + +// Is the moment within the specified range? `end` is exclusive. +FCMoment.prototype.isWithin = function(start, end) { + var a = commonlyAmbiguate([ this, start, end ]); + return a[0] >= a[1] && a[0] < a[2]; +}; + +// When isSame is called with units, timezone ambiguity is normalized before the comparison happens. +// If no units are specified, the two moments must be identically the same, with matching ambig flags. +FCMoment.prototype.isSame = function(input, units) { + var a; + + if (units) { + a = commonlyAmbiguate([ this, input ], true); // normalize timezones but don't erase times + return moment.fn.isSame.call(a[0], a[1], units); + } + else { + input = fc.moment.parseZone(input); // normalize input + return moment.fn.isSame.call(this, input) && + Boolean(this._ambigTime) === Boolean(input._ambigTime) && + Boolean(this._ambigZone) === Boolean(input._ambigZone); + } +}; + +// Make these query methods work with ambiguous moments +$.each([ + 'isBefore', + 'isAfter' +], function(i, methodName) { + FCMoment.prototype[methodName] = function(input, units) { + var a = commonlyAmbiguate([ this, input ]); + return moment.fn[methodName].call(a[0], a[1], units); + }; +}); + + +// Misc Internals +// ------------------------------------------------------------------------------------------------- + +// given an array of moment-like inputs, return a parallel array w/ moments similarly ambiguated. +// for example, of one moment has ambig time, but not others, all moments will have their time stripped. +// set `preserveTime` to `true` to keep times, but only normalize zone ambiguity. +function commonlyAmbiguate(inputs, preserveTime) { + var outputs = []; + var anyAmbigTime = false; + var anyAmbigZone = false; + var i; + + for (i=0; i "MMMM D YYYY" + formatStr = localeData.longDateFormat(formatStr) || formatStr; + // BTW, this is not important for `formatDate` because it is impossible to put custom tokens + // or non-zero areas in Moment's localized format strings. + + separator = separator || ' - '; + + return formatRangeWithChunks( + date1, + date2, + getFormatStringChunks(formatStr), + separator, + isRTL + ); +} +fc.formatRange = formatRange; // expose + + +function formatRangeWithChunks(date1, date2, chunks, separator, isRTL) { + var chunkStr; // the rendering of the chunk + var leftI; + var leftStr = ''; + var rightI; + var rightStr = ''; + var middleI; + var middleStr1 = ''; + var middleStr2 = ''; + var middleStr = ''; + + // Start at the leftmost side of the formatting string and continue until you hit a token + // that is not the same between dates. + for (leftI=0; leftIleftI; rightI--) { + chunkStr = formatSimilarChunk(date1, date2, chunks[rightI]); + if (chunkStr === false) { + break; + } + rightStr = chunkStr + rightStr; + } + + // The area in the middle is different for both of the dates. + // Collect them distinctly so we can jam them together later. + for (middleI=leftI; middleI<=rightI; middleI++) { + middleStr1 += formatDateWithChunk(date1, chunks[middleI]); + middleStr2 += formatDateWithChunk(date2, chunks[middleI]); + } + + if (middleStr1 || middleStr2) { + if (isRTL) { + middleStr = middleStr2 + separator + middleStr1; + } + else { + middleStr = middleStr1 + separator + middleStr2; + } + } + + return leftStr + middleStr + rightStr; +} + + +var similarUnitMap = { + Y: 'year', + M: 'month', + D: 'day', // day of month + d: 'day', // day of week + // prevents a separator between anything time-related... + A: 'second', // AM/PM + a: 'second', // am/pm + T: 'second', // A/P + t: 'second', // a/p + H: 'second', // hour (24) + h: 'second', // hour (12) + m: 'second', // minute + s: 'second' // second +}; +// TODO: week maybe? + + +// Given a formatting chunk, and given that both dates are similar in the regard the +// formatting chunk is concerned, format date1 against `chunk`. Otherwise, return `false`. +function formatSimilarChunk(date1, date2, chunk) { + var token; + var unit; + + if (typeof chunk === 'string') { // a literal string + return chunk; + } + else if ((token = chunk.token)) { + unit = similarUnitMap[token.charAt(0)]; + // are the dates the same for this unit of measurement? + if (unit && date1.isSame(date2, unit)) { + return momentFormat(date1, token); // would be the same if we used `date2` + // BTW, don't support custom tokens + } + } + + return false; // the chunk is NOT the same for the two dates + // BTW, don't support splitting on non-zero areas +} + + +// Chunking Utils +// ------------------------------------------------------------------------------------------------- + + +var formatStringChunkCache = {}; + + +function getFormatStringChunks(formatStr) { + if (formatStr in formatStringChunkCache) { + return formatStringChunkCache[formatStr]; + } + return (formatStringChunkCache[formatStr] = chunkFormatString(formatStr)); +} + + +// Break the formatting string into an array of chunks +function chunkFormatString(formatStr) { + var chunks = []; + var chunker = /\[([^\]]*)\]|\(([^\)]*)\)|(LT|(\w)\4*o?)|([^\w\[\(]+)/g; // TODO: more descrimination + var match; + + while ((match = chunker.exec(formatStr))) { + if (match[1]) { // a literal string inside [ ... ] + chunks.push(match[1]); + } + else if (match[2]) { // non-zero formatting inside ( ... ) + chunks.push({ maybe: chunkFormatString(match[2]) }); + } + else if (match[3]) { // a formatting token + chunks.push({ token: match[3] }); + } + else if (match[5]) { // an unenclosed literal string + chunks.push(match[5]); + } + } + + return chunks; +} + +;; + +/* A rectangular panel that is absolutely positioned over other content +------------------------------------------------------------------------------------------------------------------------ +Options: + - className (string) + - content (HTML string or jQuery element set) + - parentEl + - top + - left + - right (the x coord of where the right edge should be. not a "CSS" right) + - autoHide (boolean) + - show (callback) + - hide (callback) +*/ + +function Popover(options) { + this.options = options || {}; +} + + +Popover.prototype = { + + isHidden: true, + options: null, + el: null, // the container element for the popover. generated by this object + documentMousedownProxy: null, // document mousedown handler bound to `this` + margin: 10, // the space required between the popover and the edges of the scroll container + + + // Shows the popover on the specified position. Renders it if not already + show: function() { + if (this.isHidden) { + if (!this.el) { + this.render(); + } + this.el.show(); + this.position(); + this.isHidden = false; + this.trigger('show'); + } + }, + + + // Hides the popover, through CSS, but does not remove it from the DOM + hide: function() { + if (!this.isHidden) { + this.el.hide(); + this.isHidden = true; + this.trigger('hide'); + } + }, + + + // Creates `this.el` and renders content inside of it + render: function() { + var _this = this; + var options = this.options; + + this.el = $('
') + .addClass(options.className || '') + .css({ + // position initially to the top left to avoid creating scrollbars + top: 0, + left: 0 + }) + .append(options.content) + .appendTo(options.parentEl); + + // when a click happens on anything inside with a 'fc-close' className, hide the popover + this.el.on('click', '.fc-close', function() { + _this.hide(); + }); + + if (options.autoHide) { + $(document).on('mousedown', this.documentMousedownProxy = $.proxy(this, 'documentMousedown')); + } + }, + + + // Triggered when the user clicks *anywhere* in the document, for the autoHide feature + documentMousedown: function(ev) { + // only hide the popover if the click happened outside the popover + if (this.el && !$(ev.target).closest(this.el).length) { + this.hide(); + } + }, + + + // Hides and unregisters any handlers + destroy: function() { + this.hide(); + + if (this.el) { + this.el.remove(); + this.el = null; + } + + $(document).off('mousedown', this.documentMousedownProxy); + }, + + + // Positions the popover optimally, using the top/left/right options + position: function() { + var options = this.options; + var origin = this.el.offsetParent().offset(); + var width = this.el.outerWidth(); + var height = this.el.outerHeight(); + var windowEl = $(window); + var viewportEl = getScrollParent(this.el); + var viewportTop; + var viewportLeft; + var viewportOffset; + var top; // the "position" (not "offset") values for the popover + var left; // + + // compute top and left + top = options.top || 0; + if (options.left !== undefined) { + left = options.left; + } + else if (options.right !== undefined) { + left = options.right - width; // derive the left value from the right value + } + else { + left = 0; + } + + if (viewportEl.is(window) || viewportEl.is(document)) { // normalize getScrollParent's result + viewportEl = windowEl; + viewportTop = 0; // the window is always at the top left + viewportLeft = 0; // (and .offset() won't work if called here) + } + else { + viewportOffset = viewportEl.offset(); + viewportTop = viewportOffset.top; + viewportLeft = viewportOffset.left; + } + + // if the window is scrolled, it causes the visible area to be further down + viewportTop += windowEl.scrollTop(); + viewportLeft += windowEl.scrollLeft(); + + // constrain to the view port. if constrained by two edges, give precedence to top/left + if (options.viewportConstrain !== false) { + top = Math.min(top, viewportTop + viewportEl.outerHeight() - height - this.margin); + top = Math.max(top, viewportTop + this.margin); + left = Math.min(left, viewportLeft + viewportEl.outerWidth() - width - this.margin); + left = Math.max(left, viewportLeft + this.margin); + } + + this.el.css({ + top: top - origin.top, + left: left - origin.left + }); + }, + + + // Triggers a callback. Calls a function in the option hash of the same name. + // Arguments beyond the first `name` are forwarded on. + // TODO: better code reuse for this. Repeat code + trigger: function(name) { + if (this.options[name]) { + this.options[name].apply(this, Array.prototype.slice.call(arguments, 1)); + } + } + +}; + +;; + +/* A "coordinate map" converts pixel coordinates into an associated cell, which has an associated date +------------------------------------------------------------------------------------------------------------------------ +Common interface: + + CoordMap.prototype = { + build: function() {}, + getCell: function(x, y) {} + }; + +*/ + +/* Coordinate map for a grid component +----------------------------------------------------------------------------------------------------------------------*/ + +function GridCoordMap(grid) { + this.grid = grid; +} + + +GridCoordMap.prototype = { + + grid: null, // reference to the Grid + rows: null, // the top-to-bottom y coordinates. including the bottom of the last item + cols: null, // the left-to-right x coordinates. including the right of the last item + + containerEl: null, // container element that all coordinates are constrained to. optionally assigned + minX: null, + maxX: null, // exclusive + minY: null, + maxY: null, // exclusive + + + // Queries the grid for the coordinates of all the cells + build: function() { + this.grid.buildCoords( + this.rows = [], + this.cols = [] + ); + this.computeBounds(); + }, + + + // Given a coordinate of the document, gets the associated cell. If no cell is underneath, returns null + getCell: function(x, y) { + var cell = null; + var rows = this.rows; + var cols = this.cols; + var r = -1; + var c = -1; + var i; + + if (this.inBounds(x, y)) { + + for (i = 0; i < rows.length; i++) { + if (y >= rows[i][0] && y < rows[i][1]) { + r = i; + break; + } + } + + for (i = 0; i < cols.length; i++) { + if (x >= cols[i][0] && x < cols[i][1]) { + c = i; + break; + } + } + + if (r >= 0 && c >= 0) { + cell = { row: r, col: c }; + cell.grid = this.grid; + cell.date = this.grid.getCellDate(cell); + } + } + + return cell; + }, + + + // If there is a containerEl, compute the bounds into min/max values + computeBounds: function() { + var containerOffset; + + if (this.containerEl) { + containerOffset = this.containerEl.offset(); + this.minX = containerOffset.left; + this.maxX = containerOffset.left + this.containerEl.outerWidth(); + this.minY = containerOffset.top; + this.maxY = containerOffset.top + this.containerEl.outerHeight(); + } + }, + + + // Determines if the given coordinates are in bounds. If no `containerEl`, always true + inBounds: function(x, y) { + if (this.containerEl) { + return x >= this.minX && x < this.maxX && y >= this.minY && y < this.maxY; + } + return true; + } + +}; + + +/* Coordinate map that is a combination of multiple other coordinate maps +----------------------------------------------------------------------------------------------------------------------*/ + +function ComboCoordMap(coordMaps) { + this.coordMaps = coordMaps; +} + + +ComboCoordMap.prototype = { + + coordMaps: null, // an array of CoordMaps + + + // Builds all coordMaps + build: function() { + var coordMaps = this.coordMaps; + var i; + + for (i = 0; i < coordMaps.length; i++) { + coordMaps[i].build(); + } + }, + + + // Queries all coordMaps for the cell underneath the given coordinates, returning the first result + getCell: function(x, y) { + var coordMaps = this.coordMaps; + var cell = null; + var i; + + for (i = 0; i < coordMaps.length && !cell; i++) { + cell = coordMaps[i].getCell(x, y); + } + + return cell; + } + +}; + +;; + +/* Tracks mouse movements over a CoordMap and raises events about which cell the mouse is over. +----------------------------------------------------------------------------------------------------------------------*/ +// TODO: implement scrolling + +function DragListener(coordMap, options) { + this.coordMap = coordMap; + this.options = options || {}; +} + + +DragListener.prototype = { + + coordMap: null, + options: null, + + isListening: false, + isDragging: false, + + // the cell/date the mouse was over when listening started + origCell: null, + origDate: null, + + // the cell/date the mouse is over + cell: null, + date: null, + + // coordinates of the initial mousedown + mouseX0: null, + mouseY0: null, + + // handler attached to the document, bound to the DragListener's `this` + mousemoveProxy: null, + mouseupProxy: null, + + scrollEl: null, + scrollBounds: null, // { top, bottom, left, right } + scrollTopVel: null, // pixels per second + scrollLeftVel: null, // pixels per second + scrollIntervalId: null, // ID of setTimeout for scrolling animation loop + scrollHandlerProxy: null, // this-scoped function for handling when scrollEl is scrolled + + scrollSensitivity: 30, // pixels from edge for scrolling to start + scrollSpeed: 200, // pixels per second, at maximum speed + scrollIntervalMs: 50, // millisecond wait between scroll increment + + + // Call this when the user does a mousedown. Will probably lead to startListening + mousedown: function(ev) { + if (isPrimaryMouseButton(ev)) { + + ev.preventDefault(); // prevents native selection in most browsers + + this.startListening(ev); + + // start the drag immediately if there is no minimum distance for a drag start + if (!this.options.distance) { + this.startDrag(ev); + } + } + }, + + + // Call this to start tracking mouse movements + startListening: function(ev) { + var scrollParent; + var cell; + + if (!this.isListening) { + + // grab scroll container and attach handler + if (ev && this.options.scroll) { + scrollParent = getScrollParent($(ev.target)); + if (!scrollParent.is(window) && !scrollParent.is(document)) { + this.scrollEl = scrollParent; + + // scope to `this`, and use `debounce` to make sure rapid calls don't happen + this.scrollHandlerProxy = debounce($.proxy(this, 'scrollHandler'), 100); + this.scrollEl.on('scroll', this.scrollHandlerProxy); + } + } + + this.computeCoords(); // relies on `scrollEl` + + // get info on the initial cell, date, and coordinates + if (ev) { + cell = this.getCell(ev); + this.origCell = cell; + this.origDate = cell ? cell.date : null; + + this.mouseX0 = ev.pageX; + this.mouseY0 = ev.pageY; + } + + $(document) + .on('mousemove', this.mousemoveProxy = $.proxy(this, 'mousemove')) + .on('mouseup', this.mouseupProxy = $.proxy(this, 'mouseup')) + .on('selectstart', this.preventDefault); // prevents native selection in IE<=8 + + this.isListening = true; + this.trigger('listenStart', ev); + } + }, + + + // Recomputes the drag-critical positions of elements + computeCoords: function() { + this.coordMap.build(); + this.computeScrollBounds(); + }, + + + // Called when the user moves the mouse + mousemove: function(ev) { + var minDistance; + var distanceSq; // current distance from mouseX0/mouseY0, squared + + if (!this.isDragging) { // if not already dragging... + // then start the drag if the minimum distance criteria is met + minDistance = this.options.distance || 1; + distanceSq = Math.pow(ev.pageX - this.mouseX0, 2) + Math.pow(ev.pageY - this.mouseY0, 2); + if (distanceSq >= minDistance * minDistance) { // use pythagorean theorem + this.startDrag(ev); + } + } + + if (this.isDragging) { + this.drag(ev); // report a drag, even if this mousemove initiated the drag + } + }, + + + // Call this to initiate a legitimate drag. + // This function is called internally from this class, but can also be called explicitly from outside + startDrag: function(ev) { + var cell; + + if (!this.isListening) { // startDrag must have manually initiated + this.startListening(); + } + + if (!this.isDragging) { + this.isDragging = true; + this.trigger('dragStart', ev); + + // report the initial cell the mouse is over + cell = this.getCell(ev); + if (cell) { + this.cellOver(cell, true); + } + } + }, + + + // Called while the mouse is being moved and when we know a legitimate drag is taking place + drag: function(ev) { + var cell; + + if (this.isDragging) { + cell = this.getCell(ev); + + if (!isCellsEqual(cell, this.cell)) { // a different cell than before? + if (this.cell) { + this.cellOut(); + } + if (cell) { + this.cellOver(cell); + } + } + + this.dragScroll(ev); // will possibly cause scrolling + } + }, + + + // Called when a the mouse has just moved over a new cell + cellOver: function(cell) { + this.cell = cell; + this.date = cell.date; + this.trigger('cellOver', cell, cell.date); + }, + + + // Called when the mouse has just moved out of a cell + cellOut: function() { + if (this.cell) { + this.trigger('cellOut', this.cell); + this.cell = null; + this.date = null; + } + }, + + + // Called when the user does a mouseup + mouseup: function(ev) { + this.stopDrag(ev); + this.stopListening(ev); + }, + + + // Called when the drag is over. Will not cause listening to stop however. + // A concluding 'cellOut' event will NOT be triggered. + stopDrag: function(ev) { + if (this.isDragging) { + this.stopScrolling(); + this.trigger('dragStop', ev); + this.isDragging = false; + } + }, + + + // Call this to stop listening to the user's mouse events + stopListening: function(ev) { + if (this.isListening) { + + // remove the scroll handler if there is a scrollEl + if (this.scrollEl) { + this.scrollEl.off('scroll', this.scrollHandlerProxy); + this.scrollHandlerProxy = null; + } + + $(document) + .off('mousemove', this.mousemoveProxy) + .off('mouseup', this.mouseupProxy) + .off('selectstart', this.preventDefault); + + this.mousemoveProxy = null; + this.mouseupProxy = null; + + this.isListening = false; + this.trigger('listenStop', ev); + + this.origCell = this.cell = null; + this.origDate = this.date = null; + } + }, + + + // Gets the cell underneath the coordinates for the given mouse event + getCell: function(ev) { + return this.coordMap.getCell(ev.pageX, ev.pageY); + }, + + + // Triggers a callback. Calls a function in the option hash of the same name. + // Arguments beyond the first `name` are forwarded on. + trigger: function(name) { + if (this.options[name]) { + this.options[name].apply(this, Array.prototype.slice.call(arguments, 1)); + } + }, + + + // Stops a given mouse event from doing it's native browser action. In our case, text selection. + preventDefault: function(ev) { + ev.preventDefault(); + }, + + + /* Scrolling + ------------------------------------------------------------------------------------------------------------------*/ + + + // Computes and stores the bounding rectangle of scrollEl + computeScrollBounds: function() { + var el = this.scrollEl; + var offset; + + if (el) { + offset = el.offset(); + this.scrollBounds = { + top: offset.top, + left: offset.left, + bottom: offset.top + el.outerHeight(), + right: offset.left + el.outerWidth() + }; + } + }, + + + // Called when the dragging is in progress and scrolling should be updated + dragScroll: function(ev) { + var sensitivity = this.scrollSensitivity; + var bounds = this.scrollBounds; + var topCloseness, bottomCloseness; + var leftCloseness, rightCloseness; + var topVel = 0; + var leftVel = 0; + + if (bounds) { // only scroll if scrollEl exists + + // compute closeness to edges. valid range is from 0.0 - 1.0 + topCloseness = (sensitivity - (ev.pageY - bounds.top)) / sensitivity; + bottomCloseness = (sensitivity - (bounds.bottom - ev.pageY)) / sensitivity; + leftCloseness = (sensitivity - (ev.pageX - bounds.left)) / sensitivity; + rightCloseness = (sensitivity - (bounds.right - ev.pageX)) / sensitivity; + + // translate vertical closeness into velocity. + // mouse must be completely in bounds for velocity to happen. + if (topCloseness >= 0 && topCloseness <= 1) { + topVel = topCloseness * this.scrollSpeed * -1; // negative. for scrolling up + } + else if (bottomCloseness >= 0 && bottomCloseness <= 1) { + topVel = bottomCloseness * this.scrollSpeed; + } + + // translate horizontal closeness into velocity + if (leftCloseness >= 0 && leftCloseness <= 1) { + leftVel = leftCloseness * this.scrollSpeed * -1; // negative. for scrolling left + } + else if (rightCloseness >= 0 && rightCloseness <= 1) { + leftVel = rightCloseness * this.scrollSpeed; + } + } + + this.setScrollVel(topVel, leftVel); + }, + + + // Sets the speed-of-scrolling for the scrollEl + setScrollVel: function(topVel, leftVel) { + + this.scrollTopVel = topVel; + this.scrollLeftVel = leftVel; + + this.constrainScrollVel(); // massages into realistic values + + // if there is non-zero velocity, and an animation loop hasn't already started, then START + if ((this.scrollTopVel || this.scrollLeftVel) && !this.scrollIntervalId) { + this.scrollIntervalId = setInterval( + $.proxy(this, 'scrollIntervalFunc'), // scope to `this` + this.scrollIntervalMs + ); + } + }, + + + // Forces scrollTopVel and scrollLeftVel to be zero if scrolling has already gone all the way + constrainScrollVel: function() { + var el = this.scrollEl; + + if (this.scrollTopVel < 0) { // scrolling up? + if (el.scrollTop() <= 0) { // already scrolled all the way up? + this.scrollTopVel = 0; + } + } + else if (this.scrollTopVel > 0) { // scrolling down? + if (el.scrollTop() + el[0].clientHeight >= el[0].scrollHeight) { // already scrolled all the way down? + this.scrollTopVel = 0; + } + } + + if (this.scrollLeftVel < 0) { // scrolling left? + if (el.scrollLeft() <= 0) { // already scrolled all the left? + this.scrollLeftVel = 0; + } + } + else if (this.scrollLeftVel > 0) { // scrolling right? + if (el.scrollLeft() + el[0].clientWidth >= el[0].scrollWidth) { // already scrolled all the way right? + this.scrollLeftVel = 0; + } + } + }, + + + // This function gets called during every iteration of the scrolling animation loop + scrollIntervalFunc: function() { + var el = this.scrollEl; + var frac = this.scrollIntervalMs / 1000; // considering animation frequency, what the vel should be mult'd by + + // change the value of scrollEl's scroll + if (this.scrollTopVel) { + el.scrollTop(el.scrollTop() + this.scrollTopVel * frac); + } + if (this.scrollLeftVel) { + el.scrollLeft(el.scrollLeft() + this.scrollLeftVel * frac); + } + + this.constrainScrollVel(); // since the scroll values changed, recompute the velocities + + // if scrolled all the way, which causes the vels to be zero, stop the animation loop + if (!this.scrollTopVel && !this.scrollLeftVel) { + this.stopScrolling(); + } + }, + + + // Kills any existing scrolling animation loop + stopScrolling: function() { + if (this.scrollIntervalId) { + clearInterval(this.scrollIntervalId); + this.scrollIntervalId = null; + + // when all done with scrolling, recompute positions since they probably changed + this.computeCoords(); + } + }, + + + // Get called when the scrollEl is scrolled (NOTE: this is delayed via debounce) + scrollHandler: function() { + // recompute all coordinates, but *only* if this is *not* part of our scrolling animation + if (!this.scrollIntervalId) { + this.computeCoords(); + } + } + +}; + + +// Returns `true` if the cells are identically equal. `false` otherwise. +// They must have the same row, col, and be from the same grid. +// Two null values will be considered equal, as two "out of the grid" states are the same. +function isCellsEqual(cell1, cell2) { + + if (!cell1 && !cell2) { + return true; + } + + if (cell1 && cell2) { + return cell1.grid === cell2.grid && + cell1.row === cell2.row && + cell1.col === cell2.col; + } + + return false; +} + +;; + +/* Creates a clone of an element and lets it track the mouse as it moves +----------------------------------------------------------------------------------------------------------------------*/ + +function MouseFollower(sourceEl, options) { + this.options = options = options || {}; + this.sourceEl = sourceEl; + this.parentEl = options.parentEl ? $(options.parentEl) : sourceEl.parent(); // default to sourceEl's parent +} + + +MouseFollower.prototype = { + + options: null, + + sourceEl: null, // the element that will be cloned and made to look like it is dragging + el: null, // the clone of `sourceEl` that will track the mouse + parentEl: null, // the element that `el` (the clone) will be attached to + + // the initial position of el, relative to the offset parent. made to match the initial offset of sourceEl + top0: null, + left0: null, + + // the initial position of the mouse + mouseY0: null, + mouseX0: null, + + // the number of pixels the mouse has moved from its initial position + topDelta: null, + leftDelta: null, + + mousemoveProxy: null, // document mousemove handler, bound to the MouseFollower's `this` + + isFollowing: false, + isHidden: false, + isAnimating: false, // doing the revert animation? + + + // Causes the element to start following the mouse + start: function(ev) { + if (!this.isFollowing) { + this.isFollowing = true; + + this.mouseY0 = ev.pageY; + this.mouseX0 = ev.pageX; + this.topDelta = 0; + this.leftDelta = 0; + + if (!this.isHidden) { + this.updatePosition(); + } + + $(document).on('mousemove', this.mousemoveProxy = $.proxy(this, 'mousemove')); + } + }, + + + // Causes the element to stop following the mouse. If shouldRevert is true, will animate back to original position. + // `callback` gets invoked when the animation is complete. If no animation, it is invoked immediately. + stop: function(shouldRevert, callback) { + var _this = this; + var revertDuration = this.options.revertDuration; + + function complete() { + this.isAnimating = false; + _this.destroyEl(); + + this.top0 = this.left0 = null; // reset state for future updatePosition calls + + if (callback) { + callback(); + } + } + + if (this.isFollowing && !this.isAnimating) { // disallow more than one stop animation at a time + this.isFollowing = false; + + $(document).off('mousemove', this.mousemoveProxy); + + if (shouldRevert && revertDuration && !this.isHidden) { // do a revert animation? + this.isAnimating = true; + this.el.animate({ + top: this.top0, + left: this.left0 + }, { + duration: revertDuration, + complete: complete + }); + } + else { + complete(); + } + } + }, + + + // Gets the tracking element. Create it if necessary + getEl: function() { + var el = this.el; + + if (!el) { + this.sourceEl.width(); // hack to force IE8 to compute correct bounding box + el = this.el = this.sourceEl.clone() + .css({ + position: 'absolute', + visibility: '', // in case original element was hidden (commonly through hideEvents()) + display: this.isHidden ? 'none' : '', // for when initially hidden + margin: 0, + right: 'auto', // erase and set width instead + bottom: 'auto', // erase and set height instead + width: this.sourceEl.width(), // explicit height in case there was a 'right' value + height: this.sourceEl.height(), // explicit width in case there was a 'bottom' value + opacity: this.options.opacity || '', + zIndex: this.options.zIndex + }) + .appendTo(this.parentEl); + } + + return el; + }, + + + // Removes the tracking element if it has already been created + destroyEl: function() { + if (this.el) { + this.el.remove(); + this.el = null; + } + }, + + + // Update the CSS position of the tracking element + updatePosition: function() { + var sourceOffset; + var origin; + + this.getEl(); // ensure this.el + + // make sure origin info was computed + if (this.top0 === null) { + this.sourceEl.width(); // hack to force IE8 to compute correct bounding box + sourceOffset = this.sourceEl.offset(); + origin = this.el.offsetParent().offset(); + this.top0 = sourceOffset.top - origin.top; + this.left0 = sourceOffset.left - origin.left; + } + + this.el.css({ + top: this.top0 + this.topDelta, + left: this.left0 + this.leftDelta + }); + }, + + + // Gets called when the user moves the mouse + mousemove: function(ev) { + this.topDelta = ev.pageY - this.mouseY0; + this.leftDelta = ev.pageX - this.mouseX0; + + if (!this.isHidden) { + this.updatePosition(); + } + }, + + + // Temporarily makes the tracking element invisible. Can be called before following starts + hide: function() { + if (!this.isHidden) { + this.isHidden = true; + if (this.el) { + this.el.hide(); + } + } + }, + + + // Show the tracking element after it has been temporarily hidden + show: function() { + if (this.isHidden) { + this.isHidden = false; + this.updatePosition(); + this.getEl().show(); + } + } + +}; + +;; + +/* A utility class for rendering rows. +----------------------------------------------------------------------------------------------------------------------*/ +// It leverages methods of the subclass and the View to determine custom rendering behavior for each row "type" +// (such as highlight rows, day rows, helper rows, etc). + +function RowRenderer(view) { + this.view = view; +} + + +RowRenderer.prototype = { + + view: null, // a View object + cellHtml: '', // plain default HTML used for a cell when no other is available + + + // Renders the HTML for a row, leveraging custom cell-HTML-renderers based on the `rowType`. + // Also applies the "intro" and "outro" cells, which are specified by the subclass and views. + // `row` is an optional row number. + rowHtml: function(rowType, row) { + var view = this.view; + var renderCell = this.getHtmlRenderer('cell', rowType); + var cellHtml = ''; + var col; + var date; + + row = row || 0; + + for (col = 0; col < view.colCnt; col++) { + date = view.cellToDate(row, col); + cellHtml += renderCell(row, col, date); + } + + cellHtml = this.bookendCells(cellHtml, rowType, row); // apply intro and outro + + return '' + cellHtml + ''; + }, + + + // Applies the "intro" and "outro" HTML to the given cells. + // Intro means the leftmost cell when the calendar is LTR and the rightmost cell when RTL. Vice-versa for outro. + // `cells` can be an HTML string of 's or a jQuery element + // `row` is an optional row number. + bookendCells: function(cells, rowType, row) { + var view = this.view; + var intro = this.getHtmlRenderer('intro', rowType)(row || 0); + var outro = this.getHtmlRenderer('outro', rowType)(row || 0); + var isRTL = view.opt('isRTL'); + var prependHtml = isRTL ? outro : intro; + var appendHtml = isRTL ? intro : outro; + + if (typeof cells === 'string') { + return prependHtml + cells + appendHtml; + } + else { // a jQuery element + return cells.prepend(prependHtml).append(appendHtml); + } + }, + + + // Returns an HTML-rendering function given a specific `rendererName` (like cell, intro, or outro) and a specific + // `rowType` (like day, eventSkeleton, helperSkeleton), which is optional. + // If a renderer for the specific rowType doesn't exist, it will fall back to a generic renderer. + // We will query the View object first for any custom rendering functions, then the methods of the subclass. + getHtmlRenderer: function(rendererName, rowType) { + var view = this.view; + var generalName; // like "cellHtml" + var specificName; // like "dayCellHtml". based on rowType + var provider; // either the View or the RowRenderer subclass, whichever provided the method + var renderer; + + generalName = rendererName + 'Html'; + if (rowType) { + specificName = rowType + capitaliseFirstLetter(rendererName) + 'Html'; + } + + if (specificName && (renderer = view[specificName])) { + provider = view; + } + else if (specificName && (renderer = this[specificName])) { + provider = this; + } + else if ((renderer = view[generalName])) { + provider = view; + } + else if ((renderer = this[generalName])) { + provider = this; + } + + if (typeof renderer === 'function') { + return function(row) { + return renderer.apply(provider, arguments) || ''; // use correct `this` and always return a string + }; + } + + // the rendered can be a plain string as well. if not specified, always an empty string. + return function() { + return renderer || ''; + }; + } + +}; + +;; + +/* An abstract class comprised of a "grid" of cells that each represent a specific datetime +----------------------------------------------------------------------------------------------------------------------*/ + +function Grid(view) { + RowRenderer.call(this, view); // call the super-constructor + this.coordMap = new GridCoordMap(this); +} + + +Grid.prototype = createObject(RowRenderer.prototype); // declare the super-class +$.extend(Grid.prototype, { + + el: null, // the containing element + coordMap: null, // a GridCoordMap that converts pixel values to datetimes + cellDuration: null, // a cell's duration. subclasses must assign this ASAP + + + // Renders the grid into the `el` element. + // Subclasses should override and call this super-method when done. + render: function() { + this.bindHandlers(); + }, + + + // Called when the grid's resources need to be cleaned up + destroy: function() { + // subclasses can implement + }, + + + /* Coordinates & Cells + ------------------------------------------------------------------------------------------------------------------*/ + + + // Populates the given empty arrays with the y and x coordinates of the cells + buildCoords: function(rows, cols) { + // subclasses must implement + }, + + + // Given a cell object, returns the date for that cell + getCellDate: function(cell) { + // subclasses must implement + }, + + + // Given a cell object, returns the element that represents the cell's whole-day + getCellDayEl: function(cell) { + // subclasses must implement + }, + + + // Converts a range with an inclusive `start` and an exclusive `end` into an array of segment objects + rangeToSegs: function(start, end) { + // subclasses must implement + }, + + + /* Handlers + ------------------------------------------------------------------------------------------------------------------*/ + + + // Attach handlers to `this.el`, using bubbling to listen to all ancestors. + // We don't need to undo any of this in a "destroy" method, because the view will simply remove `this.el` from the + // DOM and jQuery will be smart enough to garbage collect the handlers. + bindHandlers: function() { + var _this = this; + + this.el.on('mousedown', function(ev) { + if ( + !$(ev.target).is('.fc-event-container *, .fc-more') && // not an an event element, or "more.." link + !$(ev.target).closest('.fc-popover').length // not on a popover (like the "more.." events one) + ) { + _this.dayMousedown(ev); + } + }); + + this.bindSegHandlers(); // attach event-element-related handlers. in Grid.events.js + }, + + + // Process a mousedown on an element that represents a day. For day clicking and selecting. + dayMousedown: function(ev) { + var _this = this; + var view = this.view; + var isSelectable = view.opt('selectable'); + var dates = null; // the inclusive dates of the selection. will be null if no selection + var start; // the inclusive start of the selection + var end; // the *exclusive* end of the selection + var dayEl; + + // this listener tracks a mousedown on a day element, and a subsequent drag. + // if the drag ends on the same day, it is a 'dayClick'. + // if 'selectable' is enabled, this listener also detects selections. + var dragListener = new DragListener(this.coordMap, { + //distance: 5, // needs more work if we want dayClick to fire correctly + scroll: view.opt('dragScroll'), + dragStart: function() { + view.unselect(); // since we could be rendering a new selection, we want to clear any old one + }, + cellOver: function(cell, date) { + if (dragListener.origDate) { // click needs to have started on a cell + + dayEl = _this.getCellDayEl(cell); + + dates = [ date, dragListener.origDate ].sort(dateCompare); + start = dates[0]; + end = dates[1].clone().add(_this.cellDuration); + + if (isSelectable) { + _this.renderSelection(start, end); + } + } + }, + cellOut: function(cell, date) { + dates = null; + _this.destroySelection(); + }, + listenStop: function(ev) { + if (dates) { // started and ended on a cell? + if (dates[0].isSame(dates[1])) { + view.trigger('dayClick', dayEl[0], start, ev); + } + if (isSelectable) { + // the selection will already have been rendered. just report it + view.reportSelection(start, end, ev); + } + } + } + }); + + dragListener.mousedown(ev); // start listening, which will eventually initiate a dragStart + }, + + + /* Event Dragging + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of a event being dragged over the given date(s). + // `end` can be null, as well as `seg`. See View's documentation on renderDrag for more info. + // A returned value of `true` signals that a mock "helper" event has been rendered. + renderDrag: function(start, end, seg) { + // subclasses must implement + }, + + + // Unrenders a visual indication of an event being dragged + destroyDrag: function() { + // subclasses must implement + }, + + + /* Event Resizing + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of an event being resized. + // `start` and `end` are the updated dates of the event. `seg` is the original segment object involved in the drag. + renderResize: function(start, end, seg) { + // subclasses must implement + }, + + + // Unrenders a visual indication of an event being resized. + destroyResize: function() { + // subclasses must implement + }, + + + /* Event Helper + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a mock event over the given date(s). + // `end` can be null, in which case the mock event that is rendered will have a null end time. + // `sourceSeg` is the internal segment object involved in the drag. If null, something external is dragging. + renderRangeHelper: function(start, end, sourceSeg) { + var view = this.view; + var fakeEvent; + + // compute the end time if forced to do so (this is what EventManager does) + if (!end && view.opt('forceEventDuration')) { + end = view.calendar.getDefaultEventEnd(!start.hasTime(), start); + } + + fakeEvent = sourceSeg ? createObject(sourceSeg.event) : {}; // mask the original event object if possible + fakeEvent.start = start; + fakeEvent.end = end; + fakeEvent.allDay = !(start.hasTime() || (end && end.hasTime())); // freshly compute allDay + + // this extra className will be useful for differentiating real events from mock events in CSS + fakeEvent.className = (fakeEvent.className || []).concat('fc-helper'); + + // if something external is being dragged in, don't render a resizer + if (!sourceSeg) { + fakeEvent.editable = false; + } + + this.renderHelper(fakeEvent, sourceSeg); // do the actual rendering + }, + + + // Renders a mock event + renderHelper: function(event, sourceSeg) { + // subclasses must implement + }, + + + // Unrenders a mock event + destroyHelper: function() { + // subclasses must implement + }, + + + /* Selection + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of a selection. Will highlight by default but can be overridden by subclasses. + renderSelection: function(start, end) { + this.renderHighlight(start, end); + }, + + + // Unrenders any visual indications of a selection. Will unrender a highlight by default. + destroySelection: function() { + this.destroyHighlight(); + }, + + + /* Highlight + ------------------------------------------------------------------------------------------------------------------*/ + + + // Puts visual emphasis on a certain date range + renderHighlight: function(start, end) { + // subclasses should implement + }, + + + // Removes visual emphasis on a date range + destroyHighlight: function() { + // subclasses should implement + }, + + + + /* Generic rendering utilities for subclasses + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a day-of-week header row + headHtml: function() { + return '' + + '
' + + '' + + '' + + this.rowHtml('head') + // leverages RowRenderer + '' + + '
' + + '
'; + }, + + + // Used by the `headHtml` method, via RowRenderer, for rendering the HTML of a day-of-week header cell + headCellHtml: function(row, col, date) { + var view = this.view; + var calendar = view.calendar; + var colFormat = view.opt('columnFormat'); + + return '' + + '' + + htmlEscape(calendar.formatDate(date, colFormat)) + + ''; + }, + + + // Renders the HTML for a single-day background cell + bgCellHtml: function(row, col, date) { + var view = this.view; + var classes = this.getDayClasses(date); + + classes.unshift('fc-day', view.widgetContentClass); + + return ''; + }, + + + // Computes HTML classNames for a single-day cell + getDayClasses: function(date) { + var view = this.view; + var today = view.calendar.getNow().stripTime(); + var classes = [ 'fc-' + dayIDs[date.day()] ]; + + if ( + view.name === 'month' && + date.month() != view.intervalStart.month() + ) { + classes.push('fc-other-month'); + } + + if (date.isSame(today, 'day')) { + classes.push( + 'fc-today', + view.highlightStateClass + ); + } + else if (date < today) { + classes.push('fc-past'); + } + else { + classes.push('fc-future'); + } + + return classes; + } + +}); + +;; + +/* Event-rendering and event-interaction methods for the abstract Grid class +----------------------------------------------------------------------------------------------------------------------*/ + +$.extend(Grid.prototype, { + + mousedOverSeg: null, // the segment object the user's mouse is over. null if over nothing + isDraggingSeg: false, // is a segment being dragged? boolean + isResizingSeg: false, // is a segment being resized? boolean + + + // Renders the given events onto the grid + renderEvents: function(events) { + // subclasses must implement + }, + + + // Retrieves all rendered segment objects in this grid + getSegs: function() { + // subclasses must implement + }, + + + // Unrenders all events. Subclasses should implement, calling this super-method first. + destroyEvents: function() { + this.triggerSegMouseout(); // trigger an eventMouseout if user's mouse is over an event + }, + + + // Renders a `el` property for each seg, and only returns segments that successfully rendered + renderSegs: function(segs, disableResizing) { + var view = this.view; + var html = ''; + var renderedSegs = []; + var i; + + // build a large concatenation of event segment HTML + for (i = 0; i < segs.length; i++) { + html += this.renderSegHtml(segs[i], disableResizing); + } + + // Grab individual elements from the combined HTML string. Use each as the default rendering. + // Then, compute the 'el' for each segment. An el might be null if the eventRender callback returned false. + $(html).each(function(i, node) { + var seg = segs[i]; + var el = view.resolveEventEl(seg.event, $(node)); + if (el) { + el.data('fc-seg', seg); // used by handlers + seg.el = el; + renderedSegs.push(seg); + } + }); + + return renderedSegs; + }, + + + // Generates the HTML for the default rendering of a segment + renderSegHtml: function(seg, disableResizing) { + // subclasses must implement + }, + + + // Converts an array of event objects into an array of segment objects + eventsToSegs: function(events, intervalStart, intervalEnd) { + var _this = this; + + return $.map(events, function(event) { + return _this.eventToSegs(event, intervalStart, intervalEnd); // $.map flattens all returned arrays together + }); + }, + + + // Slices a single event into an array of event segments. + // When `intervalStart` and `intervalEnd` are specified, intersect the events with that interval. + // Otherwise, let the subclass decide how it wants to slice the segments over the grid. + eventToSegs: function(event, intervalStart, intervalEnd) { + var eventStart = event.start.clone().stripZone(); // normalize + var eventEnd = this.view.calendar.getEventEnd(event).stripZone(); // compute (if necessary) and normalize + var segs; + var i, seg; + + if (intervalStart && intervalEnd) { + seg = intersectionToSeg(eventStart, eventEnd, intervalStart, intervalEnd); + segs = seg ? [ seg ] : []; + } + else { + segs = this.rangeToSegs(eventStart, eventEnd); // defined by the subclass + } + + // assign extra event-related properties to the segment objects + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + seg.event = event; + seg.eventStartMS = +eventStart; + seg.eventDurationMS = eventEnd - eventStart; + } + + return segs; + }, + + + /* Handlers + ------------------------------------------------------------------------------------------------------------------*/ + + + // Attaches event-element-related handlers to the container element and leverage bubbling + bindSegHandlers: function() { + var _this = this; + var view = this.view; + + $.each( + { + mouseenter: function(seg, ev) { + _this.triggerSegMouseover(seg, ev); + }, + mouseleave: function(seg, ev) { + _this.triggerSegMouseout(seg, ev); + }, + click: function(seg, ev) { + return view.trigger('eventClick', this, seg.event, ev); // can return `false` to cancel + }, + mousedown: function(seg, ev) { + if ($(ev.target).is('.fc-resizer') && view.isEventResizable(seg.event)) { + _this.segResizeMousedown(seg, ev); + } + else if (view.isEventDraggable(seg.event)) { + _this.segDragMousedown(seg, ev); + } + } + }, + function(name, func) { + // attach the handler to the container element and only listen for real event elements via bubbling + _this.el.on(name, '.fc-event-container > *', function(ev) { + var seg = $(this).data('fc-seg'); // grab segment data. put there by View::renderEvents + + // only call the handlers if there is not a drag/resize in progress + if (seg && !_this.isDraggingSeg && !_this.isResizingSeg) { + return func.call(this, seg, ev); // `this` will be the event element + } + }); + } + ); + }, + + + // Updates internal state and triggers handlers for when an event element is moused over + triggerSegMouseover: function(seg, ev) { + if (!this.mousedOverSeg) { + this.mousedOverSeg = seg; + this.view.trigger('eventMouseover', seg.el[0], seg.event, ev); + } + }, + + + // Updates internal state and triggers handlers for when an event element is moused out. + // Can be given no arguments, in which case it will mouseout the segment that was previously moused over. + triggerSegMouseout: function(seg, ev) { + ev = ev || {}; // if given no args, make a mock mouse event + + if (this.mousedOverSeg) { + seg = seg || this.mousedOverSeg; // if given no args, use the currently moused-over segment + this.mousedOverSeg = null; + this.view.trigger('eventMouseout', seg.el[0], seg.event, ev); + } + }, + + + /* Dragging + ------------------------------------------------------------------------------------------------------------------*/ + + + // Called when the user does a mousedown on an event, which might lead to dragging. + // Generic enough to work with any type of Grid. + segDragMousedown: function(seg, ev) { + var _this = this; + var view = this.view; + var el = seg.el; + var event = seg.event; + var newStart, newEnd; + + // A clone of the original element that will move with the mouse + var mouseFollower = new MouseFollower(seg.el, { + parentEl: view.el, + opacity: view.opt('dragOpacity'), + revertDuration: view.opt('dragRevertDuration'), + zIndex: 2 // one above the .fc-view + }); + + // Tracks mouse movement over the *view's* coordinate map. Allows dragging and dropping between subcomponents + // of the view. + var dragListener = new DragListener(view.coordMap, { + distance: 5, + scroll: view.opt('dragScroll'), + listenStart: function(ev) { + mouseFollower.hide(); // don't show until we know this is a real drag + mouseFollower.start(ev); + }, + dragStart: function(ev) { + _this.triggerSegMouseout(seg, ev); // ensure a mouseout on the manipulated event has been reported + _this.isDraggingSeg = true; + view.hideEvent(event); // hide all event segments. our mouseFollower will take over + view.trigger('eventDragStart', el[0], event, ev, {}); // last argument is jqui dummy + }, + cellOver: function(cell, date) { + var origDate = seg.cellDate || dragListener.origDate; + var res = _this.computeDraggedEventDates(seg, origDate, date); + newStart = res.start; + newEnd = res.end; + + if (view.renderDrag(newStart, newEnd, seg)) { // have the view render a visual indication + mouseFollower.hide(); // if the view is already using a mock event "helper", hide our own + } + else { + mouseFollower.show(); + } + }, + cellOut: function() { // called before mouse moves to a different cell OR moved out of all cells + newStart = null; + view.destroyDrag(); // unrender whatever was done in view.renderDrag + mouseFollower.show(); // show in case we are moving out of all cells + }, + dragStop: function(ev) { + var hasChanged = newStart && !newStart.isSame(event.start); + + // do revert animation if hasn't changed. calls a callback when finished (whether animation or not) + mouseFollower.stop(!hasChanged, function() { + _this.isDraggingSeg = false; + view.destroyDrag(); + view.showEvent(event); + view.trigger('eventDragStop', el[0], event, ev, {}); // last argument is jqui dummy + + if (hasChanged) { + view.eventDrop(el[0], event, newStart, ev); // will rerender all events... + } + }); + }, + listenStop: function() { + mouseFollower.stop(); // put in listenStop in case there was a mousedown but the drag never started + } + }); + + dragListener.mousedown(ev); // start listening, which will eventually lead to a dragStart + }, + + + // Given a segment, the dates where a drag began and ended, calculates the Event Object's new start and end dates + computeDraggedEventDates: function(seg, dragStartDate, dropDate) { + var view = this.view; + var event = seg.event; + var start = event.start; + var end = view.calendar.getEventEnd(event); + var delta; + var newStart; + var newEnd; + + if (dropDate.hasTime() === dragStartDate.hasTime()) { + delta = dayishDiff(dropDate, dragStartDate); + newStart = start.clone().add(delta); + if (event.end === null) { // do we need to compute an end? + newEnd = null; + } + else { + newEnd = end.clone().add(delta); + } + } + else { + // if switching from day <-> timed, start should be reset to the dropped date, and the end cleared + newStart = dropDate; + newEnd = null; // end should be cleared + } + + return { start: newStart, end: newEnd }; + }, + + + /* Resizing + ------------------------------------------------------------------------------------------------------------------*/ + + + // Called when the user does a mousedown on an event's resizer, which might lead to resizing. + // Generic enough to work with any type of Grid. + segResizeMousedown: function(seg, ev) { + var _this = this; + var view = this.view; + var el = seg.el; + var event = seg.event; + var start = event.start; + var end = view.calendar.getEventEnd(event); + var newEnd = null; + var dragListener; + + function destroy() { // resets the rendering + _this.destroyResize(); + view.showEvent(event); + } + + // Tracks mouse movement over the *grid's* coordinate map + dragListener = new DragListener(this.coordMap, { + distance: 5, + scroll: view.opt('dragScroll'), + dragStart: function(ev) { + _this.triggerSegMouseout(seg, ev); // ensure a mouseout on the manipulated event has been reported + _this.isResizingSeg = true; + view.trigger('eventResizeStart', el[0], event, ev, {}); // last argument is jqui dummy + }, + cellOver: function(cell, date) { + // compute the new end. don't allow it to go before the event's start + if (date.isBefore(start)) { // allows comparing ambig to non-ambig + date = start; + } + newEnd = date.clone().add(_this.cellDuration); // make it an exclusive end + + if (newEnd.isSame(end)) { + newEnd = null; + destroy(); + } + else { + _this.renderResize(start, newEnd, seg); + view.hideEvent(event); + } + }, + cellOut: function() { // called before mouse moves to a different cell OR moved out of all cells + newEnd = null; + destroy(); + }, + dragStop: function(ev) { + _this.isResizingSeg = false; + destroy(); + view.trigger('eventResizeStop', el[0], event, ev, {}); // last argument is jqui dummy + + if (newEnd) { + view.eventResize(el[0], event, newEnd, ev); // will rerender all events... + } + } + }); + + dragListener.mousedown(ev); // start listening, which will eventually lead to a dragStart + }, + + + /* Rendering Utils + ------------------------------------------------------------------------------------------------------------------*/ + + + // Generic utility for generating the HTML classNames for an event segment's element + getSegClasses: function(seg, isDraggable, isResizable) { + var event = seg.event; + var classes = [ + 'fc-event', + seg.isStart ? 'fc-start' : 'fc-not-start', + seg.isEnd ? 'fc-end' : 'fc-not-end' + ].concat( + event.className, + event.source ? event.source.className : [] + ); + + if (isDraggable) { + classes.push('fc-draggable'); + } + if (isResizable) { + classes.push('fc-resizable'); + } + + return classes; + }, + + + // Utility for generating a CSS string with all the event skin-related properties + getEventSkinCss: function(event) { + var view = this.view; + var source = event.source || {}; + var eventColor = event.color; + var sourceColor = source.color; + var optionColor = view.opt('eventColor'); + var backgroundColor = + event.backgroundColor || + eventColor || + source.backgroundColor || + sourceColor || + view.opt('eventBackgroundColor') || + optionColor; + var borderColor = + event.borderColor || + eventColor || + source.borderColor || + sourceColor || + view.opt('eventBorderColor') || + optionColor; + var textColor = + event.textColor || + source.textColor || + view.opt('eventTextColor'); + var statements = []; + if (backgroundColor) { + statements.push('background-color:' + backgroundColor); + } + if (borderColor) { + statements.push('border-color:' + borderColor); + } + if (textColor) { + statements.push('color:' + textColor); + } + return statements.join(';'); + } + +}); + + +/* Event Segment Utilities +----------------------------------------------------------------------------------------------------------------------*/ + + +// A cmp function for determining which segments should take visual priority +function compareSegs(seg1, seg2) { + return seg1.eventStartMS - seg2.eventStartMS || // earlier events go first + seg2.eventDurationMS - seg1.eventDurationMS || // tie? longer events go first + seg2.event.allDay - seg1.event.allDay || // tie? put all-day events first (booleans cast to 0/1) + (seg1.event.title || '').localeCompare(seg2.event.title); // tie? alphabetically by title +} + + +;; + +/* A component that renders a grid of whole-days that runs horizontally. There can be multiple rows, one per week. +----------------------------------------------------------------------------------------------------------------------*/ + +function DayGrid(view) { + Grid.call(this, view); // call the super-constructor +} + + +DayGrid.prototype = createObject(Grid.prototype); // declare the super-class +$.extend(DayGrid.prototype, { + + numbersVisible: false, // should render a row for day/week numbers? manually set by the view + cellDuration: moment.duration({ days: 1 }), // required for Grid.event.js. Each cell is always a single day + bottomCoordPadding: 0, // hack for extending the hit area for the last row of the coordinate grid + + rowEls: null, // set of fake row elements + dayEls: null, // set of whole-day elements comprising the row's background + helperEls: null, // set of cell skeleton elements for rendering the mock event "helper" + highlightEls: null, // set of cell skeleton elements for rendering the highlight + + + // Renders the rows and columns into the component's `this.el`, which should already be assigned. + // isRigid determins whether the individual rows should ignore the contents and be a constant height. + // Relies on the view's colCnt and rowCnt. In the future, this component should probably be self-sufficient. + render: function(isRigid) { + var view = this.view; + var html = ''; + var row; + + for (row = 0; row < view.rowCnt; row++) { + html += this.dayRowHtml(row, isRigid); + } + this.el.html(html); + + this.rowEls = this.el.find('.fc-row'); + this.dayEls = this.el.find('.fc-day'); + + // run all the day cells through the dayRender callback + this.dayEls.each(function(i, node) { + var date = view.cellToDate(Math.floor(i / view.colCnt), i % view.colCnt); + view.trigger('dayRender', null, date, $(node)); + }); + + Grid.prototype.render.call(this); // call the super-method + }, + + + destroy: function() { + this.destroySegPopover(); + }, + + + // Generates the HTML for a single row. `row` is the row number. + dayRowHtml: function(row, isRigid) { + var view = this.view; + var classes = [ 'fc-row', 'fc-week', view.widgetContentClass ]; + + if (isRigid) { + classes.push('fc-rigid'); + } + + return '' + + '
' + + '
' + + '' + + this.rowHtml('day', row) + // leverages RowRenderer. calls dayCellHtml() + '
' + + '
' + + '
' + + '' + + (this.numbersVisible ? + '' + + this.rowHtml('number', row) + // leverages RowRenderer. View will define render method + '' : + '' + ) + + '
' + + '
' + + '
'; + }, + + + // Renders the HTML for a whole-day cell. Will eventually end up in the day-row's background. + // We go through a 'day' row type instead of just doing a 'bg' row type so that the View can do custom rendering + // specifically for whole-day rows, whereas a 'bg' might also be used for other purposes (TimeGrid bg for example). + dayCellHtml: function(row, col, date) { + return this.bgCellHtml(row, col, date); + }, + + + /* Coordinates & Cells + ------------------------------------------------------------------------------------------------------------------*/ + + + // Populates the empty `rows` and `cols` arrays with coordinates of the cells. For CoordGrid. + buildCoords: function(rows, cols) { + var colCnt = this.view.colCnt; + var e, n, p; + + this.dayEls.slice(0, colCnt).each(function(i, _e) { // iterate the first row of day elements + e = $(_e); + n = e.offset().left; + if (i) { + p[1] = n; + } + p = [ n ]; + cols[i] = p; + }); + p[1] = n + e.outerWidth(); + + this.rowEls.each(function(i, _e) { + e = $(_e); + n = e.offset().top; + if (i) { + p[1] = n; + } + p = [ n ]; + rows[i] = p; + }); + p[1] = n + e.outerHeight() + this.bottomCoordPadding; // hack to extend hit area of last row + }, + + + // Converts a cell to a date + getCellDate: function(cell) { + return this.view.cellToDate(cell); // leverages the View's cell system + }, + + + // Gets the whole-day element associated with the cell + getCellDayEl: function(cell) { + return this.dayEls.eq(cell.row * this.view.colCnt + cell.col); + }, + + + // Converts a range with an inclusive `start` and an exclusive `end` into an array of segment objects + rangeToSegs: function(start, end) { + return this.view.rangeToSegments(start, end); // leverages the View's cell system + }, + + + /* Event Drag Visualization + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of an event hovering over the given date(s). + // `end` can be null, as well as `seg`. See View's documentation on renderDrag for more info. + // A returned value of `true` signals that a mock "helper" event has been rendered. + renderDrag: function(start, end, seg) { + var opacity; + + // always render a highlight underneath + this.renderHighlight( + start, + end || this.view.calendar.getDefaultEventEnd(true, start) + ); + + // if a segment from the same calendar but another component is being dragged, render a helper event + if (seg && !seg.el.closest(this.el).length) { + + this.renderRangeHelper(start, end, seg); + + opacity = this.view.opt('dragOpacity'); + if (opacity !== undefined) { + this.helperEls.css('opacity', opacity); + } + + return true; // a helper has been rendered + } + }, + + + // Unrenders any visual indication of a hovering event + destroyDrag: function() { + this.destroyHighlight(); + this.destroyHelper(); + }, + + + /* Event Resize Visualization + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of an event being resized + renderResize: function(start, end, seg) { + this.renderHighlight(start, end); + this.renderRangeHelper(start, end, seg); + }, + + + // Unrenders a visual indication of an event being resized + destroyResize: function() { + this.destroyHighlight(); + this.destroyHelper(); + }, + + + /* Event Helper + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a mock "helper" event. `sourceSeg` is the associated internal segment object. It can be null. + renderHelper: function(event, sourceSeg) { + var helperNodes = []; + var rowStructs = this.renderEventRows([ event ]); + + // inject each new event skeleton into each associated row + this.rowEls.each(function(row, rowNode) { + var rowEl = $(rowNode); // the .fc-row + var skeletonEl = $('
'); // will be absolutely positioned + var skeletonTop; + + // If there is an original segment, match the top position. Otherwise, put it at the row's top level + if (sourceSeg && sourceSeg.row === row) { + skeletonTop = sourceSeg.el.position().top; + } + else { + skeletonTop = rowEl.find('.fc-content-skeleton tbody').position().top; + } + + skeletonEl.css('top', skeletonTop) + .find('table') + .append(rowStructs[row].tbodyEl); + + rowEl.append(skeletonEl); + helperNodes.push(skeletonEl[0]); + }); + + this.helperEls = $(helperNodes); // array -> jQuery set + }, + + + // Unrenders any visual indication of a mock helper event + destroyHelper: function() { + if (this.helperEls) { + this.helperEls.remove(); + this.helperEls = null; + } + }, + + + /* Highlighting + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders an emphasis on the given date range. `start` is an inclusive, `end` is exclusive. + renderHighlight: function(start, end) { + var segs = this.rangeToSegs(start, end); + var highlightNodes = []; + var i, seg; + var el; + + // build an event skeleton for each row that needs it + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + el = $( + this.highlightSkeletonHtml(seg.leftCol, seg.rightCol + 1) // make end exclusive + ); + el.appendTo(this.rowEls[seg.row]); + highlightNodes.push(el[0]); + } + + this.highlightEls = $(highlightNodes); // array -> jQuery set + }, + + + // Unrenders any visual emphasis on a date range + destroyHighlight: function() { + if (this.highlightEls) { + this.highlightEls.remove(); + this.highlightEls = null; + } + }, + + + // Generates the HTML used to build a single-row "highlight skeleton", a table that frames highlight cells + highlightSkeletonHtml: function(startCol, endCol) { + var colCnt = this.view.colCnt; + var cellHtml = ''; + + if (startCol > 0) { + cellHtml += ' elements that should be appended to each row's content skeleton. + // Returns an array of rowStruct objects (see the bottom of `renderEventRow`). + renderEventRows: function(events) { + var segs = this.eventsToSegs(events); + var rowStructs = []; + var segRows; + var row; + + segs = this.renderSegs(segs); // returns a new array with only visible segments + segRows = this.groupSegRows(segs); // group into nested arrays + + // iterate each row of segment groupings + for (row = 0; row < segRows.length; row++) { + rowStructs.push( + this.renderEventRow(row, segRows[row]) + ); + } + + return rowStructs; + }, + + + // Builds the HTML to be used for the default element for an individual segment + renderSegHtml: function(seg, disableResizing) { + var view = this.view; + var isRTL = view.opt('isRTL'); + var event = seg.event; + var isDraggable = view.isEventDraggable(event); + var isResizable = !disableResizing && event.allDay && seg.isEnd && view.isEventResizable(event); + var classes = this.getSegClasses(seg, isDraggable, isResizable); + var skinCss = this.getEventSkinCss(event); + var timeHtml = ''; + var titleHtml; + + classes.unshift('fc-day-grid-event'); + + // Only display a timed events time if it is the starting segment + if (!event.allDay && seg.isStart) { + timeHtml = '' + htmlEscape(view.getEventTimeText(event)) + ''; + } + + titleHtml = + '' + + (htmlEscape(event.title || '') || ' ') + // we always want one line of height + ''; + + return '' + + '
' + + (isRTL ? + titleHtml + ' ' + timeHtml : // put a natural space in between + timeHtml + ' ' + titleHtml // + ) + + '
' + + (isResizable ? + '
' : + '' + ) + + ''; + }, + + + // Given a row # and an array of segments all in the same row, render a
element, a skeleton that contains + // the segments. Returns object with a bunch of internal data about how the render was calculated. + renderEventRow: function(row, rowSegs) { + var view = this.view; + var colCnt = view.colCnt; + var segLevels = this.buildSegLevels(rowSegs); // group into sub-arrays of levels + var levelCnt = Math.max(1, segLevels.length); // ensure at least one level + var tbody = $(''); + var segMatrix = []; // lookup for which segments are rendered into which level+col cells + var cellMatrix = []; // lookup for all '); + + segMatrix.push([]); + cellMatrix.push([]); + loneCellMatrix.push([]); + + // levelCnt might be 1 even though there are no actual levels. protect against this. + // this single empty row is useful for styling. + if (levelSegs) { + for (j = 0; j < levelSegs.length; j++) { // iterate through segments in level + seg = levelSegs[j]; + + emptyCellsUntil(seg.leftCol); + + // create a container that occupies or more columns. append the event element. + td = $(' at a time and stop when we find one out of bounds + for (i = 0; i < trEls.length; i++) { + trEl = trEls.eq(i).removeClass('fc-limited'); // get and reveal + if (trEl.position().top + trEl.outerHeight() > rowHeight) { + return i; + } + } + + return false; // should not limit at all + }, + + + // Limits the given grid row to the maximum number of levels and injects "more" links if necessary. + // `row` is the row number. + // `levelLimit` is a number for the maximum (inclusive) number of levels allowed. + limitRow: function(row, levelLimit) { + var _this = this; + var view = this.view; + var rowStruct = this.rowStructs[row]; + var moreNodes = []; // array of "more" links and and segment elements past the limit + .addClass('fc-limited').get(); // hide elements and get a simple DOM-nodes array + + // iterate though segments in the last allowable level + for (i = 0; i < levelSegs.length; i++) { + seg = levelSegs[i]; + emptyCellsUntil(seg.leftCol); // process empty cells before the segment + + // determine *all* segments below `seg` that occupy the same columns + colSegsBelow = []; + totalSegsBelow = 0; + while (col <= seg.rightCol) { + cell = { row: row, col: col }; + segsBelow = this.getCellSegs(cell, levelLimit); + colSegsBelow.push(segsBelow); + totalSegsBelow += segsBelow.length; + col++; + } + + if (totalSegsBelow) { // do we need to replace this segment with one or many "more" links? + td = cellMatrix[levelLimit - 1][seg.leftCol]; // the segment's parent cell + rowspan = td.attr('rowspan') || 1; + segMoreNodes = []; + + // make a replacement '; + + html += + '' + + (!isRTL ? axisHtml : '') + + '"; + + slotTime.add(this.slotDuration); + } + + return html; + }, + + + // Parses various options into properties of this object + processOptions: function() { + var view = this.view; + var slotDuration = view.opt('slotDuration'); + var snapDuration = view.opt('snapDuration'); + + slotDuration = moment.duration(slotDuration); + snapDuration = snapDuration ? moment.duration(snapDuration) : slotDuration; + + this.slotDuration = slotDuration; + this.snapDuration = snapDuration; + this.cellDuration = snapDuration; // important to assign this for Grid.events.js + + this.minTime = moment.duration(view.opt('minTime')); + this.maxTime = moment.duration(view.opt('maxTime')); + }, + + + // Slices up a date range into a segment for each column + rangeToSegs: function(rangeStart, rangeEnd) { + var view = this.view; + var segs = []; + var seg; + var col; + var cellDate; + var colStart, colEnd; + + // normalize + rangeStart = rangeStart.clone().stripZone(); + rangeEnd = rangeEnd.clone().stripZone(); + + for (col = 0; col < view.colCnt; col++) { + cellDate = view.cellToDate(0, col); // use the View's cell system for this + colStart = cellDate.clone().time(this.minTime); + colEnd = cellDate.clone().time(this.maxTime); + seg = intersectionToSeg(rangeStart, rangeEnd, colStart, colEnd); + if (seg) { + seg.col = col; + segs.push(seg); + } + } + + return segs; + }, + + + /* Coordinates + ------------------------------------------------------------------------------------------------------------------*/ + + + // Called when there is a window resize/zoom and we need to recalculate coordinates for the grid + resize: function() { + this.computeSlatTops(); + this.updateSegVerticals(); + }, + + + // Populates the given empty `rows` and `cols` arrays with offset positions of the "snap" cells. + // "Snap" cells are different the slots because they might have finer granularity. + buildCoords: function(rows, cols) { + var colCnt = this.view.colCnt; + var originTop = this.el.offset().top; + var snapTime = moment.duration(+this.minTime); + var p = null; + var e, n; + + this.dayEls.slice(0, colCnt).each(function(i, _e) { + e = $(_e); + n = e.offset().left; + if (p) { + p[1] = n; + } + p = [ n ]; + cols[i] = p; + }); + p[1] = n + e.outerWidth(); + + p = null; + while (snapTime < this.maxTime) { + n = originTop + this.computeTimeTop(snapTime); + if (p) { + p[1] = n; + } + p = [ n ]; + rows.push(p); + snapTime.add(this.snapDuration); + } + p[1] = originTop + this.computeTimeTop(snapTime); // the position of the exclusive end + }, + + + // Gets the datetime for the given slot cell + getCellDate: function(cell) { + var view = this.view; + var calendar = view.calendar; + + return calendar.rezoneDate( // since we are adding a time, it needs to be in the calendar's timezone + view.cellToDate(0, cell.col) // View's coord system only accounts for start-of-day for column + .time(this.minTime + this.snapDuration * cell.row) + ); + }, + + + // Gets the element that represents the whole-day the cell resides on + getCellDayEl: function(cell) { + return this.dayEls.eq(cell.col); + }, + + + // Computes the top coordinate, relative to the bounds of the grid, of the given date. + // A `startOfDayDate` must be given for avoiding ambiguity over how to treat midnight. + computeDateTop: function(date, startOfDayDate) { + return this.computeTimeTop( + moment.duration( + date.clone().stripZone() - startOfDayDate.clone().stripTime() + ) + ); + }, + + + // Computes the top coordinate, relative to the bounds of the grid, of the given time (a Duration). + computeTimeTop: function(time) { + var slatCoverage = (time - this.minTime) / this.slotDuration; // floating-point value of # of slots covered + var slatIndex; + var slatRemainder; + var slatTop; + var slatBottom; + + // constrain. because minTime/maxTime might be customized + slatCoverage = Math.max(0, slatCoverage); + slatCoverage = Math.min(this.slatEls.length, slatCoverage); + + slatIndex = Math.floor(slatCoverage); // an integer index of the furthest whole slot + slatRemainder = slatCoverage - slatIndex; + slatTop = this.slatTops[slatIndex]; // the top position of the furthest whole slot + + if (slatRemainder) { // time spans part-way into the slot + slatBottom = this.slatTops[slatIndex + 1]; + return slatTop + (slatBottom - slatTop) * slatRemainder; // part-way between slots + } + else { + return slatTop; + } + }, + + + // Queries each `slatEl` for its position relative to the grid's container and stores it in `slatTops`. + // Includes the the bottom of the last slat as the last item in the array. + computeSlatTops: function() { + var tops = []; + var top; + + this.slatEls.each(function(i, node) { + top = $(node).position().top; + tops.push(top); + }); + + tops.push(top + this.slatEls.last().outerHeight()); // bottom of the last slat + + this.slatTops = tops; + }, + + + /* Event Drag Visualization + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of an event being dragged over the specified date(s). + // `end` and `seg` can be null. See View's documentation on renderDrag for more info. + renderDrag: function(start, end, seg) { + var opacity; + + if (seg) { // if there is event information for this drag, render a helper event + this.renderRangeHelper(start, end, seg); + + opacity = this.view.opt('dragOpacity'); + if (opacity !== undefined) { + this.helperEl.css('opacity', opacity); + } + + return true; // signal that a helper has been rendered + } + else { + // otherwise, just render a highlight + this.renderHighlight( + start, + end || this.view.calendar.getDefaultEventEnd(false, start) + ); + } + }, + + + // Unrenders any visual indication of an event being dragged + destroyDrag: function() { + this.destroyHelper(); + this.destroyHighlight(); + }, + + + /* Event Resize Visualization + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of an event being resized + renderResize: function(start, end, seg) { + this.renderRangeHelper(start, end, seg); + }, + + + // Unrenders any visual indication of an event being resized + destroyResize: function() { + this.destroyHelper(); + }, + + + /* Event Helper + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a mock "helper" event. `sourceSeg` is the original segment object and might be null (an external drag) + renderHelper: function(event, sourceSeg) { + var res = this.renderEventTable([ event ]); + var tableEl = res.tableEl; + var segs = res.segs; + var i, seg; + var sourceEl; + + // Try to make the segment that is in the same row as sourceSeg look the same + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + if (sourceSeg && sourceSeg.col === seg.col) { + sourceEl = sourceSeg.el; + seg.el.css({ + left: sourceEl.css('left'), + right: sourceEl.css('right'), + 'margin-left': sourceEl.css('margin-left'), + 'margin-right': sourceEl.css('margin-right') + }); + } + } + + this.helperEl = $('
') + .append(tableEl) + .appendTo(this.el); + }, + + + // Unrenders any mock helper event + destroyHelper: function() { + if (this.helperEl) { + this.helperEl.remove(); + this.helperEl = null; + } + }, + + + /* Selection + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of a selection. Overrides the default, which was to simply render a highlight. + renderSelection: function(start, end) { + if (this.view.opt('selectHelper')) { // this setting signals that a mock helper event should be rendered + this.renderRangeHelper(start, end); + } + else { + this.renderHighlight(start, end); + } + }, + + + // Unrenders any visual indication of a selection + destroySelection: function() { + this.destroyHelper(); + this.destroyHighlight(); + }, + + + /* Highlight + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders an emphasis on the given date range. `start` is inclusive. `end` is exclusive. + renderHighlight: function(start, end) { + this.highlightEl = $( + this.highlightSkeletonHtml(start, end) + ).appendTo(this.el); + }, + + + // Unrenders the emphasis on a date range + destroyHighlight: function() { + if (this.highlightEl) { + this.highlightEl.remove(); + this.highlightEl = null; + } + }, + + + // Generates HTML for a table element with containers in each column, responsible for absolutely positioning the + // highlight elements to cover the highlighted slots. + highlightSkeletonHtml: function(start, end) { + var view = this.view; + var segs = this.rangeToSegs(start, end); + var cellHtml = ''; + var col = 0; + var i, seg; + var dayDate; + var top, bottom; + + for (i = 0; i < segs.length; i++) { // loop through the segments. one per column + seg = segs[i]; + + // need empty cells beforehand? + if (col < seg.col) { + cellHtml += '
'; + + col++; + } + + // need empty cells after the last segment? + if (col < view.colCnt) { + cellHtml += ''; + } + }, + + + // Generates the HTML that will go before content-skeleton cells that display the day/week numbers. + // Queried by the DayGrid subcomponent. Ordering depends on isRTL. + numberIntroHtml: function(row) { + if (this.weekNumbersVisible) { + return '' + + ''; + } + }, + + + // Generates the HTML that goes before the day bg cells for each day-row. + // Queried by the DayGrid subcomponent. Ordering depends on isRTL. + dayIntroHtml: function() { + if (this.weekNumbersVisible) { + return ''; + } + }, + + + // Generates the HTML that goes before every other type of row generated by DayGrid. Ordering depends on isRTL. + // Affects helper-skeleton and highlight-skeleton rows. + introHtml: function() { + if (this.weekNumbersVisible) { + return ''; + } + }, + + + // Generates the HTML for the '; + }, + + + // Generates an HTML attribute string for setting the width of the week number column, if it is known + weekNumberStyleAttr: function() { + if (this.weekNumberWidth !== null) { + return 'style="width:' + this.weekNumberWidth + 'px"'; + } + return ''; + }, + + + // Determines whether each row should have a constant height + hasRigidRows: function() { + var eventLimit = this.opt('eventLimit'); + return eventLimit && typeof eventLimit !== 'number'; + }, + + + /* Dimensions + ------------------------------------------------------------------------------------------------------------------*/ + + + // Refreshes the horizontal dimensions of the view + updateWidth: function() { + if (this.weekNumbersVisible) { + // Make sure all week number cells running down the side have the same width. + // Record the width for cells created later. + this.weekNumberWidth = matchCellWidths( + this.el.find('.fc-week-number') + ); + } + }, + + + // Adjusts the vertical dimensions of the view to the specified values + setHeight: function(totalHeight, isAuto) { + var eventLimit = this.opt('eventLimit'); + var scrollerHeight; + + // reset all heights to be natural + unsetScroller(this.scrollerEl); + uncompensateScroll(this.headRowEl); + + this.dayGrid.destroySegPopover(); // kill the "more" popover if displayed + + // is the event limit a constant level number? + if (eventLimit && typeof eventLimit === 'number') { + this.dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after + } + + scrollerHeight = this.computeScrollerHeight(totalHeight); + this.setGridHeight(scrollerHeight, isAuto); + + // is the event limit dynamically calculated? + if (eventLimit && typeof eventLimit !== 'number') { + this.dayGrid.limitRows(eventLimit); // limit the levels after the grid's row heights have been set + } + + if (!isAuto && setPotentialScroller(this.scrollerEl, scrollerHeight)) { // using scrollbars? + + compensateScroll(this.headRowEl, getScrollbarWidths(this.scrollerEl)); + + // doing the scrollbar compensation might have created text overflow which created more height. redo + scrollerHeight = this.computeScrollerHeight(totalHeight); + this.scrollerEl.height(scrollerHeight); + + this.restoreScroll(); + } + }, + + + // Sets the height of just the DayGrid component in this view + setGridHeight: function(height, isAuto) { + if (isAuto) { + undistributeHeight(this.dayGrid.rowEls); // let the rows be their natural height with no expanding + } + else { + distributeHeight(this.dayGrid.rowEls, height, true); // true = compensate for height-hogging rows + } + }, + + + /* Events + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders the given events onto the view and populates the segments array + renderEvents: function(events) { + this.dayGrid.renderEvents(events); + + this.updateHeight(); // must compensate for events that overflow the row + + View.prototype.renderEvents.call(this, events); // call the super-method + }, + + + // Retrieves all segment objects that are rendered in the view + getSegs: function() { + return this.dayGrid.getSegs(); + }, + + + // Unrenders all event elements and clears internal segment data + destroyEvents: function() { + View.prototype.destroyEvents.call(this); // do this before dayGrid's segs have been cleared + + this.recordScroll(); // removing events will reduce height and mess with the scroll, so record beforehand + this.dayGrid.destroyEvents(); + + // we DON'T need to call updateHeight() because: + // A) a renderEvents() call always happens after this, which will eventually call updateHeight() + // B) in IE8, this causes a flash whenever events are rerendered + }, + + + /* Event Dragging + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of an event being dragged over the view. + // A returned value of `true` signals that a mock "helper" event has been rendered. + renderDrag: function(start, end, seg) { + return this.dayGrid.renderDrag(start, end, seg); + }, + + + // Unrenders the visual indication of an event being dragged over the view + destroyDrag: function() { + this.dayGrid.destroyDrag(); + }, + + + /* Selection + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of a selection + renderSelection: function(start, end) { + this.dayGrid.renderSelection(start, end); + }, + + + // Unrenders a visual indications of a selection + destroySelection: function() { + this.dayGrid.destroySelection(); + } + +}); + +;; + +/* A month view with day cells running in rows (one-per-week) and columns +----------------------------------------------------------------------------------------------------------------------*/ + +setDefaults({ + fixedWeekCount: true +}); + +fcViews.month = MonthView; // register the view + +function MonthView(calendar) { + BasicView.call(this, calendar); // call the super-constructor +} + + +MonthView.prototype = createObject(BasicView.prototype); // define the super-class +$.extend(MonthView.prototype, { + + name: 'month', + + + incrementDate: function(date, delta) { + return date.clone().stripTime().add(delta, 'months').startOf('month'); + }, + + + render: function(date) { + var rowCnt; + + this.intervalStart = date.clone().stripTime().startOf('month'); + this.intervalEnd = this.intervalStart.clone().add(1, 'months'); + + this.start = this.intervalStart.clone(); + this.start = this.skipHiddenDays(this.start); // move past the first week if no visible days + this.start.startOf('week'); + this.start = this.skipHiddenDays(this.start); // move past the first invisible days of the week + + this.end = this.intervalEnd.clone(); + this.end = this.skipHiddenDays(this.end, -1, true); // move in from the last week if no visible days + this.end.add((7 - this.end.weekday()) % 7, 'days'); // move to end of week if not already + this.end = this.skipHiddenDays(this.end, -1, true); // move in from the last invisible days of the week + + rowCnt = Math.ceil( // need to ceil in case there are hidden days + this.end.diff(this.start, 'weeks', true) // returnfloat=true + ); + if (this.isFixedWeeks()) { + this.end.add(6 - rowCnt, 'weeks'); + rowCnt = 6; + } + + this.title = this.calendar.formatDate(this.intervalStart, this.opt('titleFormat')); + + BasicView.prototype.render.call(this, rowCnt, this.getCellsPerWeek(), true); // call the super-method + }, + + + // Overrides the default BasicView behavior to have special multi-week auto-height logic + setGridHeight: function(height, isAuto) { + + isAuto = isAuto || this.opt('weekMode') === 'variable'; // LEGACY: weekMode is deprecated + + // if auto, make the height of each row the height that it would be if there were 6 weeks + if (isAuto) { + height *= this.rowCnt / 6; + } + + distributeHeight(this.dayGrid.rowEls, height, !isAuto); // if auto, don't compensate for height-hogging rows + }, + + + isFixedWeeks: function() { + var weekMode = this.opt('weekMode'); // LEGACY: weekMode is deprecated + if (weekMode) { + return weekMode === 'fixed'; // if any other type of weekMode, assume NOT fixed + } + + return this.opt('fixedWeekCount'); + } + +}); + +;; + +/* A week view with simple day cells running horizontally +----------------------------------------------------------------------------------------------------------------------*/ +// TODO: a WeekView mixin for calculating dates and titles + +fcViews.basicWeek = BasicWeekView; // register this view + +function BasicWeekView(calendar) { + BasicView.call(this, calendar); // call the super-constructor +} + + +BasicWeekView.prototype = createObject(BasicView.prototype); // define the super-class +$.extend(BasicWeekView.prototype, { + + name: 'basicWeek', + + + incrementDate: function(date, delta) { + return date.clone().stripTime().add(delta, 'weeks').startOf('week'); + }, + + + render: function(date) { + + this.intervalStart = date.clone().stripTime().startOf('week'); + this.intervalEnd = this.intervalStart.clone().add(1, 'weeks'); + + this.start = this.skipHiddenDays(this.intervalStart); + this.end = this.skipHiddenDays(this.intervalEnd, -1, true); + + this.title = this.calendar.formatRange( + this.start, + this.end.clone().subtract(1), // make inclusive by subtracting 1 ms + this.opt('titleFormat'), + ' \u2014 ' // emphasized dash + ); + + BasicView.prototype.render.call(this, 1, this.getCellsPerWeek(), false); // call the super-method + } + +}); +;; + +/* A view with a single simple day cell +----------------------------------------------------------------------------------------------------------------------*/ + +fcViews.basicDay = BasicDayView; // register this view + +function BasicDayView(calendar) { + BasicView.call(this, calendar); // call the super-constructor +} + + +BasicDayView.prototype = createObject(BasicView.prototype); // define the super-class +$.extend(BasicDayView.prototype, { + + name: 'basicDay', + + + incrementDate: function(date, delta) { + var out = date.clone().stripTime().add(delta, 'days'); + out = this.skipHiddenDays(out, delta < 0 ? -1 : 1); + return out; + }, + + + render: function(date) { + + this.start = this.intervalStart = date.clone().stripTime(); + this.end = this.intervalEnd = this.start.clone().add(1, 'days'); + + this.title = this.calendar.formatDate(this.start, this.opt('titleFormat')); + + BasicView.prototype.render.call(this, 1, 1, false); // call the super-method + } + +}); +;; + +/* An abstract class for all agenda-related views. Displays one more columns with time slots running vertically. +----------------------------------------------------------------------------------------------------------------------*/ +// Is a manager for the TimeGrid subcomponent and possibly the DayGrid subcomponent (if allDaySlot is on). +// Responsible for managing width/height. + +setDefaults({ + allDaySlot: true, + allDayText: 'all-day', + + scrollTime: '06:00:00', + + slotDuration: '00:30:00', + + axisFormat: generateAgendaAxisFormat, + timeFormat: { + agenda: generateAgendaTimeFormat + }, + + minTime: '00:00:00', + maxTime: '24:00:00', + slotEventOverlap: true +}); + +var AGENDA_ALL_DAY_EVENT_LIMIT = 5; + + +function generateAgendaAxisFormat(options, langData) { + return langData.longDateFormat('LT') + .replace(':mm', '(:mm)') + .replace(/(\Wmm)$/, '($1)') // like above, but for foreign langs + .replace(/\s*a$/i, 'a'); // convert AM/PM/am/pm to lowercase. remove any spaces beforehand +} + + +function generateAgendaTimeFormat(options, langData) { + return langData.longDateFormat('LT') + .replace(/\s*a$/i, ''); // remove trailing AM/PM +} + + +function AgendaView(calendar) { + View.call(this, calendar); // call the super-constructor + + this.timeGrid = new TimeGrid(this); + + if (this.opt('allDaySlot')) { // should we display the "all-day" area? + this.dayGrid = new DayGrid(this); // the all-day subcomponent of this view + + // the coordinate grid will be a combination of both subcomponents' grids + this.coordMap = new ComboCoordMap([ + this.dayGrid.coordMap, + this.timeGrid.coordMap + ]); + } + else { + this.coordMap = this.timeGrid.coordMap; + } +} + + +AgendaView.prototype = createObject(View.prototype); // define the super-class +$.extend(AgendaView.prototype, { + + timeGrid: null, // the main time-grid subcomponent of this view + dayGrid: null, // the "all-day" subcomponent. if all-day is turned off, this will be null + + axisWidth: null, // the width of the time axis running down the side + + noScrollRowEls: null, // set of fake row elements that must compensate when scrollerEl has scrollbars + + // when the time-grid isn't tall enough to occupy the given height, we render an
underneath + bottomRuleEl: null, + bottomRuleHeight: null, + + + /* Rendering + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders the view into `this.el`, which has already been assigned. + // `colCnt` has been calculated by a subclass and passed here. + render: function(colCnt) { + + // needed for cell-to-date and date-to-cell calculations in View + this.rowCnt = 1; + this.colCnt = colCnt; + + this.el.addClass('fc-agenda-view').html(this.renderHtml()); + + // the element that wraps the time-grid that will probably scroll + this.scrollerEl = this.el.find('.fc-time-grid-container'); + this.timeGrid.coordMap.containerEl = this.scrollerEl; // don't accept clicks/etc outside of this + + this.timeGrid.el = this.el.find('.fc-time-grid'); + this.timeGrid.render(); + + // the
that sometimes displays under the time-grid + this.bottomRuleEl = $('
') + .appendTo(this.timeGrid.el); // inject it into the time-grid + + if (this.dayGrid) { + this.dayGrid.el = this.el.find('.fc-day-grid'); + this.dayGrid.render(); + + // have the day-grid extend it's coordinate area over the
dividing the two grids + this.dayGrid.bottomCoordPadding = this.dayGrid.el.next('hr').outerHeight(); + } + + this.noScrollRowEls = this.el.find('.fc-row:not(.fc-scroller *)'); // fake rows not within the scroller + + View.prototype.render.call(this); // call the super-method + + this.resetScroll(); // do this after sizes have been set + }, + + + // Make subcomponents ready for cleanup + destroy: function() { + this.timeGrid.destroy(); + if (this.dayGrid) { + this.dayGrid.destroy(); + } + View.prototype.destroy.call(this); // call the super-method + }, + + + // Builds the HTML skeleton for the view. + // The day-grid and time-grid components will render inside containers defined by this HTML. + renderHtml: function() { + return '' + + '
'; + } + if (endCol > startCol) { + cellHtml += ''; + } + if (colCnt > endCol) { + cellHtml += ''; + } + + cellHtml = this.bookendCells(cellHtml, 'highlight'); + + return '' + + '
' + + '' + + '' + + cellHtml + + '' + + '
' + + '
'; + } + +}); + +;; + +/* Event-rendering methods for the DayGrid class +----------------------------------------------------------------------------------------------------------------------*/ + +$.extend(DayGrid.prototype, { + + segs: null, + rowStructs: null, // an array of objects, each holding information about a row's event-rendering + + + // Render the given events onto the Grid and return the rendered segments + renderEvents: function(events) { + var rowStructs = this.rowStructs = this.renderEventRows(events); + var segs = []; + + // append to each row's content skeleton + this.rowEls.each(function(i, rowNode) { + $(rowNode).find('.fc-content-skeleton > table').append( + rowStructs[i].tbodyEl + ); + segs.push.apply(segs, rowStructs[i].segs); + }); + + this.segs = segs; + }, + + + // Retrieves all segment objects that have been rendered + getSegs: function() { + return (this.segs || []).concat( + this.popoverSegs || [] // segs rendered in the "more" events popover + ); + }, + + + // Removes all rendered event elements + destroyEvents: function() { + var rowStructs; + var rowStruct; + + Grid.prototype.destroyEvents.call(this); // call the super-method + + rowStructs = this.rowStructs || []; + while ((rowStruct = rowStructs.pop())) { + rowStruct.tbodyEl.remove(); + } + + this.segs = null; + this.destroySegPopover(); // removes the "more.." events popover + }, + + + // Uses the given events array to generate
elements of the level+col matrix + var loneCellMatrix = []; // lookup for elements that only take up a single column + var i, levelSegs; + var col; + var tr; + var j, seg; + var td; + + // populates empty cells from the current column (`col`) to `endCol` + function emptyCellsUntil(endCol) { + while (col < endCol) { + // try to grab a cell from the level above and extend its rowspan. otherwise, create a fresh cell + td = (loneCellMatrix[i - 1] || [])[col]; + if (td) { + td.attr( + 'rowspan', + parseInt(td.attr('rowspan') || 1, 10) + 1 + ); + } + else { + td = $(''); + tr.append(td); + } + cellMatrix[i][col] = td; + loneCellMatrix[i][col] = td; + col++; + } + } + + for (i = 0; i < levelCnt; i++) { // iterate through all levels + levelSegs = segLevels[i]; + col = 0; + tr = $('
').append(seg.el); + if (seg.leftCol != seg.rightCol) { + td.attr('colspan', seg.rightCol - seg.leftCol + 1); + } + else { // a single-column segment + loneCellMatrix[i][col] = td; + } + + while (col <= seg.rightCol) { + cellMatrix[i][col] = td; + segMatrix[i][col] = seg; + col++; + } + + tr.append(td); + } + } + + emptyCellsUntil(colCnt); // finish off the row + this.bookendCells(tr, 'eventSkeleton'); + tbody.append(tr); + } + + return { // a "rowStruct" + row: row, // the row number + tbodyEl: tbody, + cellMatrix: cellMatrix, + segMatrix: segMatrix, + segLevels: segLevels, + segs: rowSegs + }; + }, + + + // Stacks a flat array of segments, which are all assumed to be in the same row, into subarrays of vertical levels. + buildSegLevels: function(segs) { + var levels = []; + var i, seg; + var j; + + // Give preference to elements with certain criteria, so they have + // a chance to be closer to the top. + segs.sort(compareSegs); + + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + + // loop through levels, starting with the topmost, until the segment doesn't collide with other segments + for (j = 0; j < levels.length; j++) { + if (!isDaySegCollision(seg, levels[j])) { + break; + } + } + // `j` now holds the desired subrow index + seg.level = j; + + // create new level array if needed and append segment + (levels[j] || (levels[j] = [])).push(seg); + } + + // order segments left-to-right. very important if calendar is RTL + for (j = 0; j < levels.length; j++) { + levels[j].sort(compareDaySegCols); + } + + return levels; + }, + + + // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's row + groupSegRows: function(segs) { + var view = this.view; + var segRows = []; + var i; + + for (i = 0; i < view.rowCnt; i++) { + segRows.push([]); + } + + for (i = 0; i < segs.length; i++) { + segRows[segs[i].row].push(segs[i]); + } + + return segRows; + } + +}); + + +// Computes whether two segments' columns collide. They are assumed to be in the same row. +function isDaySegCollision(seg, otherSegs) { + var i, otherSeg; + + for (i = 0; i < otherSegs.length; i++) { + otherSeg = otherSegs[i]; + + if ( + otherSeg.leftCol <= seg.rightCol && + otherSeg.rightCol >= seg.leftCol + ) { + return true; + } + } + + return false; +} + + +// A cmp function for determining the leftmost event +function compareDaySegCols(a, b) { + return a.leftCol - b.leftCol; +} + +;; + +/* Methods relate to limiting the number events for a given day on a DayGrid +----------------------------------------------------------------------------------------------------------------------*/ + +$.extend(DayGrid.prototype, { + + + segPopover: null, // the Popover that holds events that can't fit in a cell. null when not visible + popoverSegs: null, // an array of segment objects that the segPopover holds. null when not visible + + + destroySegPopover: function() { + if (this.segPopover) { + this.segPopover.hide(); // will trigger destruction of `segPopover` and `popoverSegs` + } + }, + + + // Limits the number of "levels" (vertically stacking layers of events) for each row of the grid. + // `levelLimit` can be false (don't limit), a number, or true (should be computed). + limitRows: function(levelLimit) { + var rowStructs = this.rowStructs || []; + var row; // row # + var rowLevelLimit; + + for (row = 0; row < rowStructs.length; row++) { + this.unlimitRow(row); + + if (!levelLimit) { + rowLevelLimit = false; + } + else if (typeof levelLimit === 'number') { + rowLevelLimit = levelLimit; + } + else { + rowLevelLimit = this.computeRowLevelLimit(row); + } + + if (rowLevelLimit !== false) { + this.limitRow(row, rowLevelLimit); + } + } + }, + + + // Computes the number of levels a row will accomodate without going outside its bounds. + // Assumes the row is "rigid" (maintains a constant height regardless of what is inside). + // `row` is the row number. + computeRowLevelLimit: function(row) { + var rowEl = this.rowEls.eq(row); // the containing "fake" row div + var rowHeight = rowEl.height(); // TODO: cache somehow? + var trEls = this.rowStructs[row].tbodyEl.children(); + var i, trEl; + + // Reveal one level
DOM nodes + var col = 0; // col # + var cell; + var levelSegs; // array of segment objects in the last allowable level, ordered left-to-right + var cellMatrix; // a matrix (by level, then column) of all jQuery elements in the row + var limitedNodes; // array of temporarily hidden level
DOM nodes + var i, seg; + var segsBelow; // array of segment objects below `seg` in the current `col` + var totalSegsBelow; // total number of segments below `seg` in any of the columns `seg` occupies + var colSegsBelow; // array of segment arrays, below seg, one for each column (offset from segs's first column) + var td, rowspan; + var segMoreNodes; // array of "more" cells that will stand-in for the current seg's cell + var j; + var moreTd, moreWrap, moreLink; + + // Iterates through empty level cells and places "more" links inside if need be + function emptyCellsUntil(endCol) { // goes from current `col` to `endCol` + while (col < endCol) { + cell = { row: row, col: col }; + segsBelow = _this.getCellSegs(cell, levelLimit); + if (segsBelow.length) { + td = cellMatrix[levelLimit - 1][col]; + moreLink = _this.renderMoreLink(cell, segsBelow); + moreWrap = $('
').append(moreLink); + td.append(moreWrap); + moreNodes.push(moreWrap[0]); + } + col++; + } + } + + if (levelLimit && levelLimit < rowStruct.segLevels.length) { // is it actually over the limit? + levelSegs = rowStruct.segLevels[levelLimit - 1]; + cellMatrix = rowStruct.cellMatrix; + + limitedNodes = rowStruct.tbodyEl.children().slice(levelLimit) // get level
for each column the segment occupies. will be one for each colspan + for (j = 0; j < colSegsBelow.length; j++) { + moreTd = $('').attr('rowspan', rowspan); + segsBelow = colSegsBelow[j]; + cell = { row: row, col: seg.leftCol + j }; + moreLink = this.renderMoreLink(cell, [ seg ].concat(segsBelow)); // count seg as hidden too + moreWrap = $('
').append(moreLink); + moreTd.append(moreWrap); + segMoreNodes.push(moreTd[0]); + moreNodes.push(moreTd[0]); + } + + td.addClass('fc-limited').after($(segMoreNodes)); // hide original
and inject replacements + limitedNodes.push(td[0]); + } + } + + emptyCellsUntil(view.colCnt); // finish off the level + rowStruct.moreEls = $(moreNodes); // for easy undoing later + rowStruct.limitedEls = $(limitedNodes); // for easy undoing later + } + }, + + + // Reveals all levels and removes all "more"-related elements for a grid's row. + // `row` is a row number. + unlimitRow: function(row) { + var rowStruct = this.rowStructs[row]; + + if (rowStruct.moreEls) { + rowStruct.moreEls.remove(); + rowStruct.moreEls = null; + } + + if (rowStruct.limitedEls) { + rowStruct.limitedEls.removeClass('fc-limited'); + rowStruct.limitedEls = null; + } + }, + + + // Renders an element that represents hidden event element for a cell. + // Responsible for attaching click handler as well. + renderMoreLink: function(cell, hiddenSegs) { + var _this = this; + var view = this.view; + + return $('') + .text( + this.getMoreLinkText(hiddenSegs.length) + ) + .on('click', function(ev) { + var clickOption = view.opt('eventLimitClick'); + var date = view.cellToDate(cell); + var moreEl = $(this); + var dayEl = _this.getCellDayEl(cell); + var allSegs = _this.getCellSegs(cell); + + // rescope the segments to be within the cell's date + var reslicedAllSegs = _this.resliceDaySegs(allSegs, date); + var reslicedHiddenSegs = _this.resliceDaySegs(hiddenSegs, date); + + if (typeof clickOption === 'function') { + // the returned value can be an atomic option + clickOption = view.trigger('eventLimitClick', null, { + date: date, + dayEl: dayEl, + moreEl: moreEl, + segs: reslicedAllSegs, + hiddenSegs: reslicedHiddenSegs + }, ev); + } + + if (clickOption === 'popover') { + _this.showSegPopover(date, cell, moreEl, reslicedAllSegs); + } + else if (typeof clickOption === 'string') { // a view name + view.calendar.zoomTo(date, clickOption); + } + }); + }, + + + // Reveals the popover that displays all events within a cell + showSegPopover: function(date, cell, moreLink, segs) { + var _this = this; + var view = this.view; + var moreWrap = moreLink.parent(); // the
wrapper around the + var topEl; // the element we want to match the top coordinate of + var options; + + if (view.rowCnt == 1) { + topEl = this.view.el; // will cause the popover to cover any sort of header + } + else { + topEl = this.rowEls.eq(cell.row); // will align with top of row + } + + options = { + className: 'fc-more-popover', + content: this.renderSegPopoverContent(date, segs), + parentEl: this.el, + top: topEl.offset().top, + autoHide: true, // when the user clicks elsewhere, hide the popover + viewportConstrain: view.opt('popoverViewportConstrain'), + hide: function() { + // destroy everything when the popover is hidden + _this.segPopover.destroy(); + _this.segPopover = null; + _this.popoverSegs = null; + } + }; + + // Determine horizontal coordinate. + // We use the moreWrap instead of the
to avoid border confusion. + if (view.opt('isRTL')) { + options.right = moreWrap.offset().left + moreWrap.outerWidth() + 1; // +1 to be over cell border + } + else { + options.left = moreWrap.offset().left - 1; // -1 to be over cell border + } + + this.segPopover = new Popover(options); + this.segPopover.show(); + }, + + + // Builds the inner DOM contents of the segment popover + renderSegPopoverContent: function(date, segs) { + var view = this.view; + var isTheme = view.opt('theme'); + var title = date.format(view.opt('dayPopoverFormat')); + var content = $( + '
' + + '' + + '' + + htmlEscape(title) + + '' + + '
' + + '
' + + '
' + + '
' + + '
' + ); + var segContainer = content.find('.fc-event-container'); + var i; + + // render each seg's `el` and only return the visible segs + segs = this.renderSegs(segs, true); // disableResizing=true + this.popoverSegs = segs; + + for (i = 0; i < segs.length; i++) { + + // because segments in the popover are not part of a grid coordinate system, provide a hint to any + // grids that want to do drag-n-drop about which cell it came from + segs[i].cellDate = date; + + segContainer.append(segs[i].el); + } + + return content; + }, + + + // Given the events within an array of segment objects, reslice them to be in a single day + resliceDaySegs: function(segs, dayDate) { + var events = $.map(segs, function(seg) { + return seg.event; + }); + var dayStart = dayDate.clone().stripTime(); + var dayEnd = dayStart.clone().add(1, 'days'); + + return this.eventsToSegs(events, dayStart, dayEnd); + }, + + + // Generates the text that should be inside a "more" link, given the number of events it represents + getMoreLinkText: function(num) { + var view = this.view; + var opt = view.opt('eventLimitText'); + + if (typeof opt === 'function') { + return opt(num); + } + else { + return '+' + num + ' ' + opt; + } + }, + + + // Returns segments within a given cell. + // If `startLevel` is specified, returns only events including and below that level. Otherwise returns all segs. + getCellSegs: function(cell, startLevel) { + var segMatrix = this.rowStructs[cell.row].segMatrix; + var level = startLevel || 0; + var segs = []; + var seg; + + while (level < segMatrix.length) { + seg = segMatrix[level][cell.col]; + if (seg) { + segs.push(seg); + } + level++; + } + + return segs; + } + +}); + +;; + +/* A component that renders one or more columns of vertical time slots +----------------------------------------------------------------------------------------------------------------------*/ + +function TimeGrid(view) { + Grid.call(this, view); // call the super-constructor +} + + +TimeGrid.prototype = createObject(Grid.prototype); // define the super-class +$.extend(TimeGrid.prototype, { + + slotDuration: null, // duration of a "slot", a distinct time segment on given day, visualized by lines + snapDuration: null, // granularity of time for dragging and selecting + + minTime: null, // Duration object that denotes the first visible time of any given day + maxTime: null, // Duration object that denotes the exclusive visible end time of any given day + + dayEls: null, // cells elements in the day-row background + slatEls: null, // elements running horizontally across all columns + + slatTops: null, // an array of top positions, relative to the container. last item holds bottom of last slot + + highlightEl: null, // cell skeleton element for rendering the highlight + helperEl: null, // cell skeleton element for rendering the mock event "helper" + + + // Renders the time grid into `this.el`, which should already be assigned. + // Relies on the view's colCnt. In the future, this component should probably be self-sufficient. + render: function() { + this.processOptions(); + + this.el.html(this.renderHtml()); + + this.dayEls = this.el.find('.fc-day'); + this.slatEls = this.el.find('.fc-slats tr'); + + this.computeSlatTops(); + + Grid.prototype.render.call(this); // call the super-method + }, + + + // Renders the basic HTML skeleton for the grid + renderHtml: function() { + return '' + + '
' + + '' + + this.rowHtml('slotBg') + // leverages RowRenderer, which will call slotBgCellHtml + '
' + + '
' + + '
' + + '' + + this.slatRowHtml() + + '
' + + '
'; + }, + + + // Renders the HTML for a vertical background cell behind the slots. + // This method is distinct from 'bg' because we wanted a new `rowType` so the View could customize the rendering. + slotBgCellHtml: function(row, col, date) { + return this.bgCellHtml(row, col, date); + }, + + + // Generates the HTML for the horizontal "slats" that run width-wise. Has a time axis on a side. Depends on RTL. + slatRowHtml: function() { + var view = this.view; + var calendar = view.calendar; + var isRTL = view.opt('isRTL'); + var html = ''; + var slotNormal = this.slotDuration.asMinutes() % 15 === 0; + var slotTime = moment.duration(+this.minTime); // wish there was .clone() for durations + var slotDate; // will be on the view's first day, but we only care about its time + var minutes; + var axisHtml; + + // Calculate the time for each slot + while (slotTime < this.maxTime) { + slotDate = view.start.clone().time(slotTime); // will be in UTC but that's good. to avoid DST issues + minutes = slotDate.minutes(); + + axisHtml = + '
' + + ((!slotNormal || !minutes) ? // if irregular slot duration, or on the hour, then display the time + '' + // for matchCellWidths + htmlEscape(calendar.formatDate(slotDate, view.opt('axisFormat'))) + + '' : + '' + ) + + '
' + + (isRTL ? axisHtml : '') + + "
'; + col = seg.col; + } + + // compute vertical position + dayDate = view.cellToDate(0, col); + top = this.computeDateTop(seg.start, dayDate); + bottom = this.computeDateTop(seg.end, dayDate); // the y position of the bottom edge + + // generate the cell HTML. bottom becomes negative because it needs to be a CSS value relative to the + // bottom edge of the zero-height container. + cellHtml += + '' + + '
' + + '
' + + '
' + + '
'; + } + + cellHtml = this.bookendCells(cellHtml, 'highlight'); + + return '' + + '
' + + '' + + '' + + cellHtml + + '' + + '
' + + '
'; + } + +}); + +;; + +/* Event-rendering methods for the TimeGrid class +----------------------------------------------------------------------------------------------------------------------*/ + +$.extend(TimeGrid.prototype, { + + segs: null, // segment objects rendered in the component. null of events haven't been rendered yet + eventSkeletonEl: null, // has cells with event-containers, which contain absolutely positioned event elements + + + // Renders the events onto the grid and returns an array of segments that have been rendered + renderEvents: function(events) { + var res = this.renderEventTable(events); + + this.eventSkeletonEl = $('
').append(res.tableEl); + this.el.append(this.eventSkeletonEl); + + this.segs = res.segs; + }, + + + // Retrieves rendered segment objects + getSegs: function() { + return this.segs || []; + }, + + + // Removes all event segment elements from the view + destroyEvents: function() { + Grid.prototype.destroyEvents.call(this); // call the super-method + + if (this.eventSkeletonEl) { + this.eventSkeletonEl.remove(); + this.eventSkeletonEl = null; + } + + this.segs = null; + }, + + + // Renders and returns the portion of the event-skeleton. + // Returns an object with properties 'tbodyEl' and 'segs'. + renderEventTable: function(events) { + var tableEl = $('
'); + var trEl = tableEl.find('tr'); + var segs = this.eventsToSegs(events); + var segCols; + var i, seg; + var col, colSegs; + var containerEl; + + segs = this.renderSegs(segs); // returns only the visible segs + segCols = this.groupSegCols(segs); // group into sub-arrays, and assigns 'col' to each seg + + this.computeSegVerticals(segs); // compute and assign top/bottom + + for (col = 0; col < segCols.length; col++) { // iterate each column grouping + colSegs = segCols[col]; + placeSlotSegs(colSegs); // compute horizontal coordinates, z-index's, and reorder the array + + containerEl = $('
'); + + // assign positioning CSS and insert into container + for (i = 0; i < colSegs.length; i++) { + seg = colSegs[i]; + seg.el.css(this.generateSegPositionCss(seg)); + + // if the height is short, add a className for alternate styling + if (seg.bottom - seg.top < 30) { + seg.el.addClass('fc-short'); + } + + containerEl.append(seg.el); + } + + trEl.append($('
').append(containerEl)); + } + + this.bookendCells(trEl, 'eventSkeleton'); + + return { + tableEl: tableEl, + segs: segs + }; + }, + + + // Refreshes the CSS top/bottom coordinates for each segment element. Probably after a window resize/zoom. + updateSegVerticals: function() { + var segs = this.segs; + var i; + + if (segs) { + this.computeSegVerticals(segs); + + for (i = 0; i < segs.length; i++) { + segs[i].el.css( + this.generateSegVerticalCss(segs[i]) + ); + } + } + }, + + + // For each segment in an array, computes and assigns its top and bottom properties + computeSegVerticals: function(segs) { + var i, seg; + + for (i = 0; i < segs.length; i++) { + seg = segs[i]; + seg.top = this.computeDateTop(seg.start, seg.start); + seg.bottom = this.computeDateTop(seg.end, seg.start); + } + }, + + + // Renders the HTML for a single event segment's default rendering + renderSegHtml: function(seg, disableResizing) { + var view = this.view; + var event = seg.event; + var isDraggable = view.isEventDraggable(event); + var isResizable = !disableResizing && seg.isEnd && view.isEventResizable(event); + var classes = this.getSegClasses(seg, isDraggable, isResizable); + var skinCss = this.getEventSkinCss(event); + var timeText; + var fullTimeText; // more verbose time text. for the print stylesheet + var startTimeText; // just the start time text + + classes.unshift('fc-time-grid-event'); + + if (view.isMultiDayEvent(event)) { // if the event appears to span more than one day... + // Don't display time text on segments that run entirely through a day. + // That would appear as midnight-midnight and would look dumb. + // Otherwise, display the time text for the *segment's* times (like 6pm-midnight or midnight-10am) + if (seg.isStart || seg.isEnd) { + timeText = view.getEventTimeText(seg.start, seg.end); + fullTimeText = view.getEventTimeText(seg.start, seg.end, 'LT'); + startTimeText = view.getEventTimeText(seg.start, null); + } + } else { + // Display the normal time text for the *event's* times + timeText = view.getEventTimeText(event); + fullTimeText = view.getEventTimeText(event, 'LT'); + startTimeText = view.getEventTimeText(event.start, null); + } + + return '' + + '
' + + (timeText ? + '
' + + '' + htmlEscape(timeText) + '' + + '
' : + '' + ) + + (event.title ? + '
' + + htmlEscape(event.title) + + '
' : + '' + ) + + '
' + + '
' + + (isResizable ? + '
' : + '' + ) + + ''; + }, + + + // Generates an object with CSS properties/values that should be applied to an event segment element. + // Contains important positioning-related properties that should be applied to any event element, customized or not. + generateSegPositionCss: function(seg) { + var view = this.view; + var isRTL = view.opt('isRTL'); + var shouldOverlap = view.opt('slotEventOverlap'); + var backwardCoord = seg.backwardCoord; // the left side if LTR. the right side if RTL. floating-point + var forwardCoord = seg.forwardCoord; // the right side if LTR. the left side if RTL. floating-point + var props = this.generateSegVerticalCss(seg); // get top/bottom first + var left; // amount of space from left edge, a fraction of the total width + var right; // amount of space from right edge, a fraction of the total width + + if (shouldOverlap) { + // double the width, but don't go beyond the maximum forward coordinate (1.0) + forwardCoord = Math.min(1, backwardCoord + (forwardCoord - backwardCoord) * 2); + } + + if (isRTL) { + left = 1 - forwardCoord; + right = backwardCoord; + } + else { + left = backwardCoord; + right = 1 - forwardCoord; + } + + props.zIndex = seg.level + 1; // convert from 0-base to 1-based + props.left = left * 100 + '%'; + props.right = right * 100 + '%'; + + if (shouldOverlap && seg.forwardPressure) { + // add padding to the edge so that forward stacked events don't cover the resizer's icon + props[isRTL ? 'marginLeft' : 'marginRight'] = 10 * 2; // 10 is a guesstimate of the icon's width + } + + return props; + }, + + + // Generates an object with CSS properties for the top/bottom coordinates of a segment element + generateSegVerticalCss: function(seg) { + return { + top: seg.top, + bottom: -seg.bottom // flipped because needs to be space beyond bottom edge of event container + }; + }, + + + // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's col + groupSegCols: function(segs) { + var view = this.view; + var segCols = []; + var i; + + for (i = 0; i < view.colCnt; i++) { + segCols.push([]); + } + + for (i = 0; i < segs.length; i++) { + segCols[segs[i].col].push(segs[i]); + } + + return segCols; + } + +}); + + +// Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each. +// Also reorders the given array by date! +function placeSlotSegs(segs) { + var levels; + var level0; + var i; + + segs.sort(compareSegs); // order by date + levels = buildSlotSegLevels(segs); + computeForwardSlotSegs(levels); + + if ((level0 = levels[0])) { + + for (i = 0; i < level0.length; i++) { + computeSlotSegPressures(level0[i]); + } + + for (i = 0; i < level0.length; i++) { + computeSlotSegCoords(level0[i], 0, 0); + } + } +} + + +// Builds an array of segments "levels". The first level will be the leftmost tier of segments if the calendar is +// left-to-right, or the rightmost if the calendar is right-to-left. Assumes the segments are already ordered by date. +function buildSlotSegLevels(segs) { + var levels = []; + var i, seg; + var j; + + for (i=0; i seg2.top && seg1.top < seg2.bottom; +} + + +// A cmp function for determining which forward segment to rely on more when computing coordinates. +function compareForwardSlotSegs(seg1, seg2) { + // put higher-pressure first + return seg2.forwardPressure - seg1.forwardPressure || + // put segments that are closer to initial edge first (and favor ones with no coords yet) + (seg1.backwardCoord || 0) - (seg2.backwardCoord || 0) || + // do normal sorting... + compareSegs(seg1, seg2); +} + +;; + +/* An abstract class from which other views inherit from +----------------------------------------------------------------------------------------------------------------------*/ +// Newer methods should be written as prototype methods, not in the monster `View` function at the bottom. + +View.prototype = { + + calendar: null, // owner Calendar object + coordMap: null, // a CoordMap object for converting pixel regions to dates + el: null, // the view's containing element. set by Calendar + + // important Moments + start: null, // the date of the very first cell + end: null, // the date after the very last cell + intervalStart: null, // the start of the interval of time the view represents (1st of month for month view) + intervalEnd: null, // the exclusive end of the interval of time the view represents + + // used for cell-to-date and date-to-cell calculations + rowCnt: null, // # of weeks + colCnt: null, // # of days displayed in a week + + isSelected: false, // boolean whether cells are user-selected or not + + // subclasses can optionally use a scroll container + scrollerEl: null, // the element that will most likely scroll when content is too tall + scrollTop: null, // cached vertical scroll value + + // classNames styled by jqui themes + widgetHeaderClass: null, + widgetContentClass: null, + highlightStateClass: null, + + // document handlers, bound to `this` object + documentMousedownProxy: null, + documentDragStartProxy: null, + + + // Serves as a "constructor" to suppliment the monster `View` constructor below + init: function() { + var tm = this.opt('theme') ? 'ui' : 'fc'; + + this.widgetHeaderClass = tm + '-widget-header'; + this.widgetContentClass = tm + '-widget-content'; + this.highlightStateClass = tm + '-state-highlight'; + + // save references to `this`-bound handlers + this.documentMousedownProxy = $.proxy(this, 'documentMousedown'); + this.documentDragStartProxy = $.proxy(this, 'documentDragStart'); + }, + + + // Renders the view inside an already-defined `this.el`. + // Subclasses should override this and then call the super method afterwards. + render: function() { + this.updateSize(); + this.trigger('viewRender', this, this, this.el); + + // attach handlers to document. do it here to allow for destroy/rerender + $(document) + .on('mousedown', this.documentMousedownProxy) + .on('dragstart', this.documentDragStartProxy); // jqui drag + }, + + + // Clears all view rendering, event elements, and unregisters handlers + destroy: function() { + this.unselect(); + this.trigger('viewDestroy', this, this, this.el); + this.destroyEvents(); + this.el.empty(); // removes inner contents but leaves the element intact + + $(document) + .off('mousedown', this.documentMousedownProxy) + .off('dragstart', this.documentDragStartProxy); + }, + + + // Used to determine what happens when the users clicks next/prev. Given -1 for prev, 1 for next. + // Should apply the delta to `date` (a Moment) and return it. + incrementDate: function(date, delta) { + // subclasses should implement + }, + + + /* Dimensions + ------------------------------------------------------------------------------------------------------------------*/ + + + // Refreshes anything dependant upon sizing of the container element of the grid + updateSize: function(isResize) { + if (isResize) { + this.recordScroll(); + } + this.updateHeight(); + this.updateWidth(); + }, + + + // Refreshes the horizontal dimensions of the calendar + updateWidth: function() { + // subclasses should implement + }, + + + // Refreshes the vertical dimensions of the calendar + updateHeight: function() { + var calendar = this.calendar; // we poll the calendar for height information + + this.setHeight( + calendar.getSuggestedViewHeight(), + calendar.isHeightAuto() + ); + }, + + + // Updates the vertical dimensions of the calendar to the specified height. + // if `isAuto` is set to true, height becomes merely a suggestion and the view should use its "natural" height. + setHeight: function(height, isAuto) { + // subclasses should implement + }, + + + // Given the total height of the view, return the number of pixels that should be used for the scroller. + // Utility for subclasses. + computeScrollerHeight: function(totalHeight) { + var both = this.el.add(this.scrollerEl); + var otherHeight; // cumulative height of everything that is not the scrollerEl in the view (header+borders) + + // fuckin IE8/9/10/11 sometimes returns 0 for dimensions. this weird hack was the only thing that worked + both.css({ + position: 'relative', // cause a reflow, which will force fresh dimension recalculation + left: -1 // ensure reflow in case the el was already relative. negative is less likely to cause new scroll + }); + otherHeight = this.el.outerHeight() - this.scrollerEl.height(); // grab the dimensions + both.css({ position: '', left: '' }); // undo hack + + return totalHeight - otherHeight; + }, + + + // Called for remembering the current scroll value of the scroller. + // Should be called before there is a destructive operation (like removing DOM elements) that might inadvertently + // change the scroll of the container. + recordScroll: function() { + if (this.scrollerEl) { + this.scrollTop = this.scrollerEl.scrollTop(); + } + }, + + + // Set the scroll value of the scroller to the previously recorded value. + // Should be called after we know the view's dimensions have been restored following some type of destructive + // operation (like temporarily removing DOM elements). + restoreScroll: function() { + if (this.scrollTop !== null) { + this.scrollerEl.scrollTop(this.scrollTop); + } + }, + + + /* Events + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders the events onto the view. + // Should be overriden by subclasses. Subclasses should call the super-method afterwards. + renderEvents: function(events) { + this.segEach(function(seg) { + this.trigger('eventAfterRender', seg.event, seg.event, seg.el); + }); + this.trigger('eventAfterAllRender'); + }, + + + // Removes event elements from the view. + // Should be overridden by subclasses. Should call this super-method FIRST, then subclass DOM destruction. + destroyEvents: function() { + this.segEach(function(seg) { + this.trigger('eventDestroy', seg.event, seg.event, seg.el); + }); + }, + + + // Given an event and the default element used for rendering, returns the element that should actually be used. + // Basically runs events and elements through the eventRender hook. + resolveEventEl: function(event, el) { + var custom = this.trigger('eventRender', event, event, el); + + if (custom === false) { // means don't render at all + el = null; + } + else if (custom && custom !== true) { + el = $(custom); + } + + return el; + }, + + + // Hides all rendered event segments linked to the given event + showEvent: function(event) { + this.segEach(function(seg) { + seg.el.css('visibility', ''); + }, event); + }, + + + // Shows all rendered event segments linked to the given event + hideEvent: function(event) { + this.segEach(function(seg) { + seg.el.css('visibility', 'hidden'); + }, event); + }, + + + // Iterates through event segments. Goes through all by default. + // If the optional `event` argument is specified, only iterates through segments linked to that event. + // The `this` value of the callback function will be the view. + segEach: function(func, event) { + var segs = this.getSegs(); + var i; + + for (i = 0; i < segs.length; i++) { + if (!event || segs[i].event._id === event._id) { + func.call(this, segs[i]); + } + } + }, + + + // Retrieves all the rendered segment objects for the view + getSegs: function() { + // subclasses must implement + }, + + + /* Event Drag Visualization + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of an event hovering over the specified date. + // `end` is a Moment and might be null. + // `seg` might be null. if specified, it is the segment object of the event being dragged. + // otherwise, an external event from outside the calendar is being dragged. + renderDrag: function(start, end, seg) { + // subclasses should implement + }, + + + // Unrenders a visual indication of event hovering + destroyDrag: function() { + // subclasses should implement + }, + + + // Handler for accepting externally dragged events being dropped in the view. + // Gets called when jqui's 'dragstart' is fired. + documentDragStart: function(ev, ui) { + var _this = this; + var dropDate = null; + var dragListener; + + if (this.opt('droppable')) { // only listen if this setting is on + + // listener that tracks mouse movement over date-associated pixel regions + dragListener = new DragListener(this.coordMap, { + cellOver: function(cell, date) { + dropDate = date; + _this.renderDrag(date); + }, + cellOut: function() { + dropDate = null; + _this.destroyDrag(); + } + }); + + // gets called, only once, when jqui drag is finished + $(document).one('dragstop', function(ev, ui) { + _this.destroyDrag(); + if (dropDate) { + _this.trigger('drop', ev.target, dropDate, ev, ui); + } + }); + + dragListener.startDrag(ev); // start listening immediately + } + }, + + + /* Selection + ------------------------------------------------------------------------------------------------------------------*/ + + + // Selects a date range on the view. `start` and `end` are both Moments. + // `ev` is the native mouse event that begin the interaction. + select: function(start, end, ev) { + this.unselect(ev); + this.renderSelection(start, end); + this.reportSelection(start, end, ev); + }, + + + // Renders a visual indication of the selection + renderSelection: function(start, end) { + // subclasses should implement + }, + + + // Called when a new selection is made. Updates internal state and triggers handlers. + reportSelection: function(start, end, ev) { + this.isSelected = true; + this.trigger('select', null, start, end, ev); + }, + + + // Undoes a selection. updates in the internal state and triggers handlers. + // `ev` is the native mouse event that began the interaction. + unselect: function(ev) { + if (this.isSelected) { + this.isSelected = false; + this.destroySelection(); + this.trigger('unselect', null, ev); + } + }, + + + // Unrenders a visual indication of selection + destroySelection: function() { + // subclasses should implement + }, + + + // Handler for unselecting when the user clicks something and the 'unselectAuto' setting is on + documentMousedown: function(ev) { + var ignore; + + // is there a selection, and has the user made a proper left click? + if (this.isSelected && this.opt('unselectAuto') && isPrimaryMouseButton(ev)) { + + // only unselect if the clicked element is not identical to or inside of an 'unselectCancel' element + ignore = this.opt('unselectCancel'); + if (!ignore || !$(ev.target).closest(ignore).length) { + this.unselect(ev); + } + } + } + +}; + + +// We are mixing JavaScript OOP design patterns here by putting methods and member variables in the closed scope of the +// constructor. Going forward, methods should be part of the prototype. +function View(calendar) { + var t = this; + + // exports + t.calendar = calendar; + t.opt = opt; + t.trigger = trigger; + t.isEventDraggable = isEventDraggable; + t.isEventResizable = isEventResizable; + t.eventDrop = eventDrop; + t.eventResize = eventResize; + + // imports + var reportEventChange = calendar.reportEventChange; + + // locals + var options = calendar.options; + var nextDayThreshold = moment.duration(options.nextDayThreshold); + + + t.init(); // the "constructor" that concerns the prototype methods + + + function opt(name) { + var v = options[name]; + if ($.isPlainObject(v) && !isForcedAtomicOption(name)) { + return smartProperty(v, t.name); + } + return v; + } + + + function trigger(name, thisObj) { + return calendar.trigger.apply( + calendar, + [name, thisObj || t].concat(Array.prototype.slice.call(arguments, 2), [t]) + ); + } + + + + /* Event Editable Boolean Calculations + ------------------------------------------------------------------------------*/ + + + function isEventDraggable(event) { + var source = event.source || {}; + + return firstDefined( + event.startEditable, + source.startEditable, + opt('eventStartEditable'), + event.editable, + source.editable, + opt('editable') + ); + } + + + function isEventResizable(event) { + var source = event.source || {}; + + return firstDefined( + event.durationEditable, + source.durationEditable, + opt('eventDurationEditable'), + event.editable, + source.editable, + opt('editable') + ); + } + + + + /* Event Elements + ------------------------------------------------------------------------------*/ + + + // Compute the text that should be displayed on an event's element. + // Based off the settings of the view. Possible signatures: + // .getEventTimeText(event, formatStr) + // .getEventTimeText(startMoment, endMoment, formatStr) + // .getEventTimeText(startMoment, null, formatStr) + // `timeFormat` is used but the `formatStr` argument can be used to override. + t.getEventTimeText = function(event, formatStr) { + var start; + var end; + + if (typeof event === 'object' && typeof formatStr === 'object') { + // first two arguments are actually moments (or null). shift arguments. + start = event; + end = formatStr; + formatStr = arguments[2]; + } + else { + // otherwise, an event object was the first argument + start = event.start; + end = event.end; + } + + formatStr = formatStr || opt('timeFormat'); + + if (end && opt('displayEventEnd')) { + return calendar.formatRange(start, end, formatStr); + } + else { + return calendar.formatDate(start, formatStr); + } + }; + + + + /* Event Modification Reporting + ---------------------------------------------------------------------------------*/ + + + function eventDrop(el, event, newStart, ev) { + var mutateResult = calendar.mutateEvent(event, newStart, null); + + trigger( + 'eventDrop', + el, + event, + mutateResult.dateDelta, + function() { + mutateResult.undo(); + reportEventChange(); + }, + ev, + {} // jqui dummy + ); + + reportEventChange(); + } + + + function eventResize(el, event, newEnd, ev) { + var mutateResult = calendar.mutateEvent(event, null, newEnd); + + trigger( + 'eventResize', + el, + event, + mutateResult.durationDelta, + function() { + mutateResult.undo(); + reportEventChange(); + }, + ev, + {} // jqui dummy + ); + + reportEventChange(); + } + + + // ==================================================================================================== + // Utilities for day "cells" + // ==================================================================================================== + // The "basic" views are completely made up of day cells. + // The "agenda" views have day cells at the top "all day" slot. + // This was the obvious common place to put these utilities, but they should be abstracted out into + // a more meaningful class (like DayEventRenderer). + // ==================================================================================================== + + + // For determining how a given "cell" translates into a "date": + // + // 1. Convert the "cell" (row and column) into a "cell offset" (the # of the cell, cronologically from the first). + // Keep in mind that column indices are inverted with isRTL. This is taken into account. + // + // 2. Convert the "cell offset" to a "day offset" (the # of days since the first visible day in the view). + // + // 3. Convert the "day offset" into a "date" (a Moment). + // + // The reverse transformation happens when transforming a date into a cell. + + + // exports + t.isHiddenDay = isHiddenDay; + t.skipHiddenDays = skipHiddenDays; + t.getCellsPerWeek = getCellsPerWeek; + t.dateToCell = dateToCell; + t.dateToDayOffset = dateToDayOffset; + t.dayOffsetToCellOffset = dayOffsetToCellOffset; + t.cellOffsetToCell = cellOffsetToCell; + t.cellToDate = cellToDate; + t.cellToCellOffset = cellToCellOffset; + t.cellOffsetToDayOffset = cellOffsetToDayOffset; + t.dayOffsetToDate = dayOffsetToDate; + t.rangeToSegments = rangeToSegments; + t.isMultiDayEvent = isMultiDayEvent; + + + // internals + var hiddenDays = opt('hiddenDays') || []; // array of day-of-week indices that are hidden + var isHiddenDayHash = []; // is the day-of-week hidden? (hash with day-of-week-index -> bool) + var cellsPerWeek; + var dayToCellMap = []; // hash from dayIndex -> cellIndex, for one week + var cellToDayMap = []; // hash from cellIndex -> dayIndex, for one week + var isRTL = opt('isRTL'); + + + // initialize important internal variables + (function() { + + if (opt('weekends') === false) { + hiddenDays.push(0, 6); // 0=sunday, 6=saturday + } + + // Loop through a hypothetical week and determine which + // days-of-week are hidden. Record in both hashes (one is the reverse of the other). + for (var dayIndex=0, cellIndex=0; dayIndex<7; dayIndex++) { + dayToCellMap[dayIndex] = cellIndex; + isHiddenDayHash[dayIndex] = $.inArray(dayIndex, hiddenDays) != -1; + if (!isHiddenDayHash[dayIndex]) { + cellToDayMap[cellIndex] = dayIndex; + cellIndex++; + } + } + + cellsPerWeek = cellIndex; + if (!cellsPerWeek) { + throw 'invalid hiddenDays'; // all days were hidden? bad. + } + + })(); + + + // Is the current day hidden? + // `day` is a day-of-week index (0-6), or a Moment + function isHiddenDay(day) { + if (moment.isMoment(day)) { + day = day.day(); + } + return isHiddenDayHash[day]; + } + + + function getCellsPerWeek() { + return cellsPerWeek; + } + + + // Incrementing the current day until it is no longer a hidden day, returning a copy. + // If the initial value of `date` is not a hidden day, don't do anything. + // Pass `isExclusive` as `true` if you are dealing with an end date. + // `inc` defaults to `1` (increment one day forward each time) + function skipHiddenDays(date, inc, isExclusive) { + var out = date.clone(); + inc = inc || 1; + while ( + isHiddenDayHash[(out.day() + (isExclusive ? inc : 0) + 7) % 7] + ) { + out.add(inc, 'days'); + } + return out; + } + + + // + // TRANSFORMATIONS: cell -> cell offset -> day offset -> date + // + + // cell -> date (combines all transformations) + // Possible arguments: + // - row, col + // - { row:#, col: # } + function cellToDate() { + var cellOffset = cellToCellOffset.apply(null, arguments); + var dayOffset = cellOffsetToDayOffset(cellOffset); + var date = dayOffsetToDate(dayOffset); + return date; + } + + // cell -> cell offset + // Possible arguments: + // - row, col + // - { row:#, col:# } + function cellToCellOffset(row, col) { + var colCnt = t.colCnt; + + // rtl variables. wish we could pre-populate these. but where? + var dis = isRTL ? -1 : 1; + var dit = isRTL ? colCnt - 1 : 0; + + if (typeof row == 'object') { + col = row.col; + row = row.row; + } + var cellOffset = row * colCnt + (col * dis + dit); // column, adjusted for RTL (dis & dit) + + return cellOffset; + } + + // cell offset -> day offset + function cellOffsetToDayOffset(cellOffset) { + var day0 = t.start.day(); // first date's day of week + cellOffset += dayToCellMap[day0]; // normlize cellOffset to beginning-of-week + return Math.floor(cellOffset / cellsPerWeek) * 7 + // # of days from full weeks + cellToDayMap[ // # of days from partial last week + (cellOffset % cellsPerWeek + cellsPerWeek) % cellsPerWeek // crazy math to handle negative cellOffsets + ] - + day0; // adjustment for beginning-of-week normalization + } + + // day offset -> date + function dayOffsetToDate(dayOffset) { + return t.start.clone().add(dayOffset, 'days'); + } + + + // + // TRANSFORMATIONS: date -> day offset -> cell offset -> cell + // + + // date -> cell (combines all transformations) + function dateToCell(date) { + var dayOffset = dateToDayOffset(date); + var cellOffset = dayOffsetToCellOffset(dayOffset); + var cell = cellOffsetToCell(cellOffset); + return cell; + } + + // date -> day offset + function dateToDayOffset(date) { + return date.clone().stripTime().diff(t.start, 'days'); + } + + // day offset -> cell offset + function dayOffsetToCellOffset(dayOffset) { + var day0 = t.start.day(); // first date's day of week + dayOffset += day0; // normalize dayOffset to beginning-of-week + return Math.floor(dayOffset / 7) * cellsPerWeek + // # of cells from full weeks + dayToCellMap[ // # of cells from partial last week + (dayOffset % 7 + 7) % 7 // crazy math to handle negative dayOffsets + ] - + dayToCellMap[day0]; // adjustment for beginning-of-week normalization + } + + // cell offset -> cell (object with row & col keys) + function cellOffsetToCell(cellOffset) { + var colCnt = t.colCnt; + + // rtl variables. wish we could pre-populate these. but where? + var dis = isRTL ? -1 : 1; + var dit = isRTL ? colCnt - 1 : 0; + + var row = Math.floor(cellOffset / colCnt); + var col = ((cellOffset % colCnt + colCnt) % colCnt) * dis + dit; // column, adjusted for RTL (dis & dit) + return { + row: row, + col: col + }; + } + + + // + // Converts a date range into an array of segment objects. + // "Segments" are horizontal stretches of time, sliced up by row. + // A segment object has the following properties: + // - row + // - cols + // - isStart + // - isEnd + // + function rangeToSegments(start, end) { + + var rowCnt = t.rowCnt; + var colCnt = t.colCnt; + var segments = []; // array of segments to return + + // day offset for given date range + var dayRange = computeDayRange(start, end); // convert to a whole-day range + var rangeDayOffsetStart = dateToDayOffset(dayRange.start); + var rangeDayOffsetEnd = dateToDayOffset(dayRange.end); // an exclusive value + + // first and last cell offset for the given date range + // "last" implies inclusivity + var rangeCellOffsetFirst = dayOffsetToCellOffset(rangeDayOffsetStart); + var rangeCellOffsetLast = dayOffsetToCellOffset(rangeDayOffsetEnd) - 1; + + // loop through all the rows in the view + for (var row=0; row
' + + '' + // needed for matchCellWidths + htmlEscape(this.opt('weekNumberTitle')) + + '' + + '' + + '' + // needed for matchCellWidths + this.calendar.calculateWeekNumber(this.cellToDate(row, 0)) + + '' + + 's of the "number" row in the DayGrid's content skeleton. + // The number row will only exist if either day numbers or week numbers are turned on. + numberCellHtml: function(row, col, date) { + var classes; + + if (!this.dayNumbersVisible) { // if there are week numbers but not day numbers + return ''; // will create an empty space above events :( + } + + classes = this.dayGrid.getDayClasses(date); + classes.unshift('fc-day-number'); + + return '' + + '' + + date.date() + + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
' + + this.timeGrid.headHtml() + // render the day-of-week headers + '
' + + (this.dayGrid ? + '
' + + '
' : + '' + ) + + '
' + + '
' + + '
' + + '
'; + }, + + + // Generates the HTML that will go before the day-of week header cells. + // Queried by the TimeGrid subcomponent when generating rows. Ordering depends on isRTL. + headIntroHtml: function() { + var date; + var weekNumber; + var weekTitle; + var weekText; + + if (this.opt('weekNumbers')) { + date = this.cellToDate(0, 0); + weekNumber = this.calendar.calculateWeekNumber(date); + weekTitle = this.opt('weekNumberTitle'); + + if (this.opt('isRTL')) { + weekText = weekNumber + weekTitle; + } + else { + weekText = weekTitle + weekNumber; + } + + return '' + + '' + + '' + // needed for matchCellWidths + htmlEscape(weekText) + + '' + + ''; + } + else { + return ''; + } + }, + + + // Generates the HTML that goes before the all-day cells. + // Queried by the DayGrid subcomponent when generating rows. Ordering depends on isRTL. + dayIntroHtml: function() { + return '' + + '' + + '' + // needed for matchCellWidths + (this.opt('allDayHtml') || htmlEscape(this.opt('allDayText'))) + + '' + + ''; + }, + + + // Generates the HTML that goes before the bg of the TimeGrid slot area. Long vertical column. + slotBgIntroHtml: function() { + return ''; + }, + + + // Generates the HTML that goes before all other types of cells. + // Affects content-skeleton, helper-skeleton, highlight-skeleton for both the time-grid and day-grid. + // Queried by the TimeGrid and DayGrid subcomponents when generating rows. Ordering depends on isRTL. + introHtml: function() { + return ''; + }, + + + // Generates an HTML attribute string for setting the width of the axis, if it is known + axisStyleAttr: function() { + if (this.axisWidth !== null) { + return 'style="width:' + this.axisWidth + 'px"'; + } + return ''; + }, + + + /* Dimensions + ------------------------------------------------------------------------------------------------------------------*/ + + updateSize: function(isResize) { + if (isResize) { + this.timeGrid.resize(); + } + View.prototype.updateSize.call(this, isResize); + }, + + + // Refreshes the horizontal dimensions of the view + updateWidth: function() { + // make all axis cells line up, and record the width so newly created axis cells will have it + this.axisWidth = matchCellWidths(this.el.find('.fc-axis')); + }, + + + // Adjusts the vertical dimensions of the view to the specified values + setHeight: function(totalHeight, isAuto) { + var eventLimit; + var scrollerHeight; + + if (this.bottomRuleHeight === null) { + // calculate the height of the rule the very first time + this.bottomRuleHeight = this.bottomRuleEl.outerHeight(); + } + this.bottomRuleEl.hide(); // .show() will be called later if this
is necessary + + // reset all dimensions back to the original state + this.scrollerEl.css('overflow', ''); + unsetScroller(this.scrollerEl); + uncompensateScroll(this.noScrollRowEls); + + // limit number of events in the all-day area + if (this.dayGrid) { + this.dayGrid.destroySegPopover(); // kill the "more" popover if displayed + + eventLimit = this.opt('eventLimit'); + if (eventLimit && typeof eventLimit !== 'number') { + eventLimit = AGENDA_ALL_DAY_EVENT_LIMIT; // make sure "auto" goes to a real number + } + if (eventLimit) { + this.dayGrid.limitRows(eventLimit); + } + } + + if (!isAuto) { // should we force dimensions of the scroll container, or let the contents be natural height? + + scrollerHeight = this.computeScrollerHeight(totalHeight); + if (setPotentialScroller(this.scrollerEl, scrollerHeight)) { // using scrollbars? + + // make the all-day and header rows lines up + compensateScroll(this.noScrollRowEls, getScrollbarWidths(this.scrollerEl)); + + // the scrollbar compensation might have changed text flow, which might affect height, so recalculate + // and reapply the desired height to the scroller. + scrollerHeight = this.computeScrollerHeight(totalHeight); + this.scrollerEl.height(scrollerHeight); + + this.restoreScroll(); + } + else { // no scrollbars + // still, force a height and display the bottom rule (marks the end of day) + this.scrollerEl.height(scrollerHeight).css('overflow', 'hidden'); // in case
goes outside + this.bottomRuleEl.show(); + } + } + }, + + + // Sets the scroll value of the scroller to the intial pre-configured state prior to allowing the user to change it. + resetScroll: function() { + var _this = this; + var scrollTime = moment.duration(this.opt('scrollTime')); + var top = this.timeGrid.computeTimeTop(scrollTime); + + // zoom can give weird floating-point values. rather scroll a little bit further + top = Math.ceil(top); + + if (top) { + top++; // to overcome top border that slots beyond the first have. looks better + } + + function scroll() { + _this.scrollerEl.scrollTop(top); + } + + scroll(); + setTimeout(scroll, 0); // overrides any previous scroll state made by the browser + }, + + + /* Events + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders events onto the view and populates the View's segment array + renderEvents: function(events) { + var dayEvents = []; + var timedEvents = []; + var daySegs = []; + var timedSegs; + var i; + + // separate the events into all-day and timed + for (i = 0; i < events.length; i++) { + if (events[i].allDay) { + dayEvents.push(events[i]); + } + else { + timedEvents.push(events[i]); + } + } + + // render the events in the subcomponents + timedSegs = this.timeGrid.renderEvents(timedEvents); + if (this.dayGrid) { + daySegs = this.dayGrid.renderEvents(dayEvents); + } + + // the all-day area is flexible and might have a lot of events, so shift the height + this.updateHeight(); + + View.prototype.renderEvents.call(this, events); // call the super-method + }, + + + // Retrieves all segment objects that are rendered in the view + getSegs: function() { + return this.timeGrid.getSegs().concat( + this.dayGrid ? this.dayGrid.getSegs() : [] + ); + }, + + + // Unrenders all event elements and clears internal segment data + destroyEvents: function() { + View.prototype.destroyEvents.call(this); // do this before the grids' segs have been cleared + + // if destroyEvents is being called as part of an event rerender, renderEvents will be called shortly + // after, so remember what the scroll value was so we can restore it. + this.recordScroll(); + + // destroy the events in the subcomponents + this.timeGrid.destroyEvents(); + if (this.dayGrid) { + this.dayGrid.destroyEvents(); + } + + // we DON'T need to call updateHeight() because: + // A) a renderEvents() call always happens after this, which will eventually call updateHeight() + // B) in IE8, this causes a flash whenever events are rerendered + }, + + + /* Event Dragging + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of an event being dragged over the view. + // A returned value of `true` signals that a mock "helper" event has been rendered. + renderDrag: function(start, end, seg) { + if (start.hasTime()) { + return this.timeGrid.renderDrag(start, end, seg); + } + else if (this.dayGrid) { + return this.dayGrid.renderDrag(start, end, seg); + } + }, + + + // Unrenders a visual indications of an event being dragged over the view + destroyDrag: function() { + this.timeGrid.destroyDrag(); + if (this.dayGrid) { + this.dayGrid.destroyDrag(); + } + }, + + + /* Selection + ------------------------------------------------------------------------------------------------------------------*/ + + + // Renders a visual indication of a selection + renderSelection: function(start, end) { + if (start.hasTime() || end.hasTime()) { + this.timeGrid.renderSelection(start, end); + } + else if (this.dayGrid) { + this.dayGrid.renderSelection(start, end); + } + }, + + + // Unrenders a visual indications of a selection + destroySelection: function() { + this.timeGrid.destroySelection(); + if (this.dayGrid) { + this.dayGrid.destroySelection(); + } + } + +}); + +;; + +/* A week view with an all-day cell area at the top, and a time grid below +----------------------------------------------------------------------------------------------------------------------*/ +// TODO: a WeekView mixin for calculating dates and titles + +fcViews.agendaWeek = AgendaWeekView; // register the view + +function AgendaWeekView(calendar) { + AgendaView.call(this, calendar); // call the super-constructor +} + + +AgendaWeekView.prototype = createObject(AgendaView.prototype); // define the super-class +$.extend(AgendaWeekView.prototype, { + + name: 'agendaWeek', + + + incrementDate: function(date, delta) { + return date.clone().stripTime().add(delta, 'weeks').startOf('week'); + }, + + + render: function(date) { + + this.intervalStart = date.clone().stripTime().startOf('week'); + this.intervalEnd = this.intervalStart.clone().add(1, 'weeks'); + + this.start = this.skipHiddenDays(this.intervalStart); + this.end = this.skipHiddenDays(this.intervalEnd, -1, true); + + this.title = this.calendar.formatRange( + this.start, + this.end.clone().subtract(1), // make inclusive by subtracting 1 ms + this.opt('titleFormat'), + ' \u2014 ' // emphasized dash + ); + + AgendaView.prototype.render.call(this, this.getCellsPerWeek()); // call the super-method + } + +}); + +;; + +/* A day view with an all-day cell area at the top, and a time grid below +----------------------------------------------------------------------------------------------------------------------*/ + +fcViews.agendaDay = AgendaDayView; // register the view + +function AgendaDayView(calendar) { + AgendaView.call(this, calendar); // call the super-constructor +} + + +AgendaDayView.prototype = createObject(AgendaView.prototype); // define the super-class +$.extend(AgendaDayView.prototype, { + + name: 'agendaDay', + + + incrementDate: function(date, delta) { + var out = date.clone().stripTime().add(delta, 'days'); + out = this.skipHiddenDays(out, delta < 0 ? -1 : 1); + return out; + }, + + + render: function(date) { + + this.start = this.intervalStart = date.clone().stripTime(); + this.end = this.intervalEnd = this.start.clone().add(1, 'days'); + + this.title = this.calendar.formatDate(this.start, this.opt('titleFormat')); + + AgendaView.prototype.render.call(this, 1); // call the super-method + } + +}); + +;; + +}); \ No newline at end of file diff --git a/dist/fullcalendar.min.css b/dist/fullcalendar.min.css new file mode 100644 index 0000000..31aaeb4 --- /dev/null +++ b/dist/fullcalendar.min.css @@ -0,0 +1,5 @@ +/*! + * FullCalendar v2.1.1 Stylesheet + * Docs & License: http://arshaw.com/fullcalendar/ + * (c) 2013 Adam Shaw + */.fc{direction:ltr;text-align:left}.fc-rtl{text-align:right}body .fc{font-size:1em}.fc-unthemed .fc-popover,.fc-unthemed .fc-row,.fc-unthemed hr,.fc-unthemed tbody,.fc-unthemed td,.fc-unthemed th,.fc-unthemed thead{border-color:#ddd}.fc-unthemed .fc-popover{background-color:#fff}.fc-unthemed .fc-popover .fc-header,.fc-unthemed hr{background:#eee}.fc-unthemed .fc-popover .fc-header .fc-close{color:#666}.fc-unthemed .fc-today{background:#fcf8e3}.fc-highlight{background:#bce8f1;opacity:.3;filter:alpha(opacity=30)}.fc-icon{display:inline-block;font-size:2em;line-height:.5em;height:.5em;font-family:"Courier New",Courier,monospace}.fc-icon-left-single-arrow:after{content:"\02039";font-weight:700}.fc-icon-right-single-arrow:after{content:"\0203A";font-weight:700}.fc-icon-left-double-arrow:after{content:"\000AB"}.fc-icon-right-double-arrow:after{content:"\000BB"}.fc-icon-x:after{content:"\000D7"}.fc button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;height:2.1em;padding:0 .6em;font-size:1em;white-space:nowrap;cursor:pointer}.fc button::-moz-focus-inner{margin:0;padding:0}.fc-state-default{border:1px solid}.fc-state-default.fc-corner-left{border-top-left-radius:4px;border-bottom-left-radius:4px}.fc-state-default.fc-corner-right{border-top-right-radius:4px;border-bottom-right-radius:4px}.fc button .fc-icon{position:relative;top:.05em;margin:0 .1em}.fc-state-default{background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.fc-state-active,.fc-state-disabled,.fc-state-down,.fc-state-hover{color:#333;background-color:#e6e6e6}.fc-state-hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.fc-state-active,.fc-state-down{background-color:#ccc;background-image:none;box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.fc-state-disabled{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);box-shadow:none}.fc-button-group{display:inline-block}.fc .fc-button-group>*{float:left;margin:0 0 0 -1px}.fc .fc-button-group>:first-child{margin-left:0}.fc-popover{position:absolute;box-shadow:0 2px 6px rgba(0,0,0,.15)}.fc-popover .fc-header{padding:2px 4px}.fc-popover .fc-header .fc-title{margin:0 2px}.fc-popover .fc-header .fc-close{cursor:pointer}.fc-ltr .fc-popover .fc-header .fc-title,.fc-rtl .fc-popover .fc-header .fc-close{float:left}.fc-ltr .fc-popover .fc-header .fc-close,.fc-rtl .fc-popover .fc-header .fc-title{float:right}.fc-unthemed .fc-popover{border-width:1px;border-style:solid}.fc-unthemed .fc-popover .fc-header .fc-close{font-size:25px;margin-top:4px}.fc-popover>.ui-widget-header+.ui-widget-content{border-top:0}.fc hr{height:0;margin:0;padding:0 0 2px;border-style:solid;border-width:1px 0}.fc-clear{clear:both}.fc-bg,.fc-helper-skeleton,.fc-highlight-skeleton{position:absolute;top:0;left:0;right:0}.fc-bg{bottom:0}.fc-bg table{height:100%}.fc table{width:100%;table-layout:fixed;border-collapse:collapse;border-spacing:0;font-size:1em}.fc th{text-align:center}.fc td,.fc th{border-style:solid;border-width:1px;padding:0;vertical-align:top}.fc td.fc-today{border-style:double}.fc .fc-row{border-style:solid;border-width:0}.fc-row table{border-left:0 hidden transparent;border-right:0 hidden transparent;border-bottom:0 hidden transparent}.fc-row:first-child table{border-top:0 hidden transparent}.fc-row{position:relative}.fc-row .fc-bg{z-index:1}.fc-row .fc-highlight-skeleton{z-index:2;bottom:0}.fc-row .fc-highlight-skeleton table{height:100%}.fc-row .fc-highlight-skeleton td{border-color:transparent}.fc-row .fc-content-skeleton{position:relative;z-index:3;padding-bottom:2px}.fc-row .fc-helper-skeleton{z-index:4}.fc-row .fc-content-skeleton td,.fc-row .fc-helper-skeleton td{background:0 0;border-color:transparent;border-bottom:0}.fc-row .fc-content-skeleton tbody td,.fc-row .fc-helper-skeleton tbody td{border-top:0}.fc-scroller{overflow-y:scroll;overflow-x:hidden}.fc-scroller>*{position:relative;width:100%;overflow:hidden}.fc-event{position:relative;display:block;font-size:.85em;line-height:1.3;border-radius:3px;border:1px solid #3a87ad;background-color:#3a87ad;font-weight:400}.fc-event,.fc-event:hover,.ui-widget .fc-event{color:#fff;text-decoration:none}.fc-event.fc-draggable,.fc-event[href]{cursor:pointer}.fc-day-grid-event{margin:1px 2px 0;padding:0 1px}.fc-ltr .fc-day-grid-event.fc-not-start,.fc-rtl .fc-day-grid-event.fc-not-end{margin-left:0;border-left-width:0;padding-left:1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-ltr .fc-day-grid-event.fc-not-end,.fc-rtl .fc-day-grid-event.fc-not-start{margin-right:0;border-right-width:0;padding-right:1px;border-top-right-radius:0;border-bottom-right-radius:0}.fc-day-grid-event>.fc-content{white-space:nowrap;overflow:hidden}.fc-day-grid-event .fc-time{font-weight:700}.fc-day-grid-event .fc-resizer{position:absolute;top:0;bottom:0;width:7px}.fc-ltr .fc-day-grid-event .fc-resizer{right:-3px;cursor:e-resize}.fc-rtl .fc-day-grid-event .fc-resizer{left:-3px;cursor:w-resize}a.fc-more{margin:1px 3px;font-size:.85em;cursor:pointer;text-decoration:none}a.fc-more:hover{text-decoration:underline}.fc-limited{display:none}.fc-day-grid .fc-row{z-index:1}.fc-more-popover{z-index:2;width:220px}.fc-more-popover .fc-event-container{padding:10px}.fc-toolbar{text-align:center;margin-bottom:1em}.fc-toolbar .fc-left{float:left}.fc-toolbar .fc-right{float:right}.fc-toolbar .fc-center{display:inline-block}.fc .fc-toolbar>*>*{float:left;margin-left:.75em}.fc .fc-toolbar>*>:first-child{margin-left:0}.fc-toolbar h2{margin:0}.fc-toolbar button{position:relative}.fc-toolbar .fc-state-hover,.fc-toolbar .ui-state-hover{z-index:2}.fc-toolbar .fc-state-down{z-index:3}.fc-toolbar .fc-state-active,.fc-toolbar .ui-state-active{z-index:4}.fc-toolbar button:focus{z-index:5}.fc-view-container *,.fc-view-container :after,.fc-view-container :before{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fc-view,.fc-view>table{position:relative;z-index:1}.fc-basicDay-view .fc-content-skeleton,.fc-basicWeek-view .fc-content-skeleton{padding-top:1px;padding-bottom:1em}.fc-basic-view tbody .fc-row{min-height:4em}.fc-row.fc-rigid{overflow:hidden}.fc-row.fc-rigid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-basic-view .fc-day-number,.fc-basic-view .fc-week-number{padding:0 2px}.fc-basic-view td.fc-day-number,.fc-basic-view td.fc-week-number span{padding-top:2px;padding-bottom:2px}.fc-basic-view .fc-week-number{text-align:center}.fc-basic-view .fc-week-number span{display:inline-block;min-width:1.25em}.fc-ltr .fc-basic-view .fc-day-number{text-align:right}.fc-rtl .fc-basic-view .fc-day-number{text-align:left}.fc-day-number.fc-other-month{opacity:.3;filter:alpha(opacity=30)}.fc-agenda-view .fc-day-grid{position:relative;z-index:2}.fc-agenda-view .fc-day-grid .fc-row{min-height:3em}.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton{padding-top:1px;padding-bottom:1em}.fc .fc-axis{vertical-align:middle;padding:0 4px;white-space:nowrap}.fc-ltr .fc-axis{text-align:right}.fc-rtl .fc-axis{text-align:left}.ui-widget td.fc-axis{font-weight:400}.fc-time-grid,.fc-time-grid-container{position:relative;z-index:1}.fc-time-grid{min-height:100%}.fc-time-grid table{border:0 hidden transparent}.fc-time-grid>.fc-bg{z-index:1}.fc-time-grid .fc-slats,.fc-time-grid>hr{position:relative;z-index:2}.fc-time-grid .fc-highlight-skeleton{z-index:3}.fc-time-grid .fc-content-skeleton{position:absolute;z-index:4;top:0;left:0;right:0}.fc-time-grid>.fc-helper-skeleton{z-index:5}.fc-slats td{height:1.5em;border-bottom:0}.fc-slats .fc-minor td{border-top-style:dotted}.fc-slats .ui-widget-content{background:0 0}.fc-time-grid .fc-highlight-container{position:relative}.fc-time-grid .fc-highlight{position:absolute;left:0;right:0}.fc-time-grid .fc-event-container{position:relative}.fc-ltr .fc-time-grid .fc-event-container{margin:0 2.5% 0 2px}.fc-rtl .fc-time-grid .fc-event-container{margin:0 2px 0 2.5%}.fc-time-grid .fc-event{position:absolute;z-index:1}.fc-time-grid-event.fc-not-start{border-top-width:0;padding-top:1px;border-top-left-radius:0;border-top-right-radius:0}.fc-time-grid-event.fc-not-end{border-bottom-width:0;padding-bottom:1px;border-bottom-left-radius:0;border-bottom-right-radius:0}.fc-time-grid-event{overflow:hidden}.fc-time-grid-event>.fc-content{position:relative;z-index:2}.fc-time-grid-event .fc-time,.fc-time-grid-event .fc-title{padding:0 1px}.fc-time-grid-event .fc-time{font-size:.85em;white-space:nowrap}.fc-time-grid-event .fc-bg{z-index:1;background:#fff;opacity:.25;filter:alpha(opacity=25)}.fc-time-grid-event.fc-short .fc-content{white-space:nowrap}.fc-time-grid-event.fc-short .fc-time,.fc-time-grid-event.fc-short .fc-title{display:inline-block;vertical-align:top}.fc-time-grid-event.fc-short .fc-time span{display:none}.fc-time-grid-event.fc-short .fc-time:before{content:attr(data-start)}.fc-time-grid-event.fc-short .fc-time:after{content:"\000A0-\000A0"}.fc-time-grid-event.fc-short .fc-title{font-size:.85em;padding:0}.fc-time-grid-event .fc-resizer{position:absolute;z-index:3;left:0;right:0;bottom:0;height:8px;overflow:hidden;line-height:8px;font-size:11px;font-family:monospace;text-align:center;cursor:s-resize}.fc-time-grid-event .fc-resizer:after{content:"="} \ No newline at end of file diff --git a/dist/fullcalendar.min.js b/dist/fullcalendar.min.js new file mode 100644 index 0000000..87be86d --- /dev/null +++ b/dist/fullcalendar.min.js @@ -0,0 +1,8 @@ +/*! + * FullCalendar v2.1.1 + * Docs & License: http://arshaw.com/fullcalendar/ + * (c) 2013 Adam Shaw + */ +(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){function i(t,e){return e.longDateFormat("LT").replace(":mm","(:mm)").replace(/(\Wmm)$/,"($1)").replace(/\s*a$/i,"t")}function n(t,e){var i=e.longDateFormat("L");return i=i.replace(/^Y+[^\w\s]*|[^\w\s]*Y+$/g,""),t.isRTL?i+=" ddd":i="ddd "+i,i}function r(t){o(De,t)}function o(e){function i(i,n){t.isPlainObject(n)&&t.isPlainObject(e[i])&&!s(i)?e[i]=o({},e[i],n):void 0!==n&&(e[i]=n)}for(var n=1;arguments.length>n;n++)t.each(arguments[n],i);return e}function s(t){return/(Time|Duration)$/.test(t)}function l(i,n){function r(t){var i=e.localeData||e.langData;return i.call(e,t)||i.call(e,"en")}function s(t){ie?h()&&(p(),f(t)):l()}function l(){ne=K.theme?"ui":"fc",i.addClass("fc"),K.isRTL?i.addClass("fc-rtl"):i.addClass("fc-ltr"),K.theme?i.addClass("ui-widget"):i.addClass("fc-unthemed"),ie=t("
").prependTo(i),te=new a(q,K),ee=te.render(),ee&&i.prepend(ee),u(K.defaultView),K.handleWindowResize&&(se=L(v,K.windowResizeDelay),t(window).resize(se))}function d(){re&&re.destroy(),te.destroy(),ie.remove(),i.removeClass("fc fc-ltr fc-rtl fc-unthemed ui-widget"),t(window).unbind("resize",se)}function h(){return i.is(":visible")}function u(t){f(0,t)}function f(e,i){he++,re&&i&&re.name!==i&&(te.deactivateButton(re.name),I(),re.start&&re.destroy(),re.el.remove(),re=null),!re&&i&&(re=new xe[i](q),re.el=t("
").appendTo(ie),te.activateButton(i)),re&&(e&&(le=re.incrementDate(le,e)),re.start&&!e&&le.isWithin(re.intervalStart,re.intervalEnd)||h()&&(I(),re.start&&re.destroy(),re.render(le),Z(),C(),x(),b())),Z(),he--}function g(t){return h()?(t&&m(),he++,re.updateSize(!0),he--,!0):void 0}function p(){h()&&m()}function m(){oe="number"==typeof K.contentHeight?K.contentHeight:"number"==typeof K.height?K.height-(ee?ee.outerHeight(!0):0):Math.round(ie.width()/Math.max(K.aspectRatio,.5))}function v(t){!he&&t.target===window&&re.start&&g(!0)&&re.trigger("windowResize",de)}function y(){E(),S()}function w(){h()&&(I(),re.destroyEvents(),re.renderEvents(ue),Z())}function E(){I(),re.destroyEvents(),Z()}function b(){!K.lazyFetching||ae(re.start,re.end)?S():w()}function S(){ce(re.start,re.end)}function D(t){ue=t,w()}function T(){w()}function C(){te.updateTitle(re.title)}function x(){var t=q.getNow();t.isWithin(re.intervalStart,re.intervalEnd)?te.disableButton("today"):te.enableButton("today")}function k(t,e){t=q.moment(t),e=e?q.moment(e):t.hasTime()?t.clone().add(q.defaultTimedEventDuration):t.clone().add(q.defaultAllDayEventDuration),re.select(t,e)}function M(){re&&re.unselect()}function R(){f(-1)}function P(){f(1)}function G(){le.add(-1,"years"),f()}function N(){le.add(1,"years"),f()}function Y(){le=q.getNow(),f()}function A(t){le=q.moment(t),f()}function _(t){le.add(e.duration(t)),f()}function O(t,e){var i,n;e&&void 0!==xe[e]||(e=e||"day",i=te.getViewsWithButtons().join(" "),n=i.match(RegExp("\\w+"+z(e))),n||(n=i.match(/\w+Day/)),e=n?n[0]:"agendaDay"),le=t,u(e)}function F(){return le.clone()}function I(){ie.css({width:"100%",height:ie.height(),overflow:"hidden"})}function Z(){ie.css({width:"",height:"",overflow:""})}function B(){return q}function j(){return re}function X(t,e){return void 0===e?K[t]:(("height"==t||"contentHeight"==t||"aspectRatio"==t)&&(K[t]=e,g(!0)),void 0)}function $(t,e){return K[t]?K[t].apply(e||de,Array.prototype.slice.call(arguments,2)):void 0}var q=this;n=n||{};var U,K=o({},De,n);U=K.lang in Te?Te[K.lang]:Te[De.lang],U&&(K=o({},De,U,n)),K.isRTL&&(K=o({},De,Ce,U||{},n)),q.options=K,q.render=s,q.destroy=d,q.refetchEvents=y,q.reportEvents=D,q.reportEventChange=T,q.rerenderEvents=w,q.changeView=u,q.select=k,q.unselect=M,q.prev=R,q.next=P,q.prevYear=G,q.nextYear=N,q.today=Y,q.gotoDate=A,q.incrementDate=_,q.zoomTo=O,q.getDate=F,q.getCalendar=B,q.getView=j,q.option=X,q.trigger=$;var Q=H(r(K.lang));if(K.monthNames&&(Q._months=K.monthNames),K.monthNamesShort&&(Q._monthsShort=K.monthNamesShort),K.dayNames&&(Q._weekdays=K.dayNames),K.dayNamesShort&&(Q._weekdaysShort=K.dayNamesShort),null!=K.firstDay){var J=H(Q._week);J.dow=K.firstDay,Q._week=J}q.defaultAllDayEventDuration=e.duration(K.defaultAllDayEventDuration),q.defaultTimedEventDuration=e.duration(K.defaultTimedEventDuration),q.moment=function(){var t;return"local"===K.timezone?(t=He.moment.apply(null,arguments),t.hasTime()&&t.local()):t="UTC"===K.timezone?He.moment.utc.apply(null,arguments):He.moment.parseZone.apply(null,arguments),"_locale"in t?t._locale=Q:t._lang=Q,t},q.getIsAmbigTimezone=function(){return"local"!==K.timezone&&"UTC"!==K.timezone},q.rezoneDate=function(t){return q.moment(t.toArray())},q.getNow=function(){var t=K.now;return"function"==typeof t&&(t=t()),q.moment(t)},q.calculateWeekNumber=function(t){var e=K.weekNumberCalculation;return"function"==typeof e?e(t):"local"===e?t.week():"ISO"===e.toUpperCase()?t.isoWeek():void 0},q.getEventEnd=function(t){return t.end?t.end.clone():q.getDefaultEventEnd(t.allDay,t.start)},q.getDefaultEventEnd=function(t,e){var i=e.clone();return t?i.stripTime().add(q.defaultAllDayEventDuration):i.add(q.defaultTimedEventDuration),q.getIsAmbigTimezone()&&i.stripZone(),i},q.formatRange=function(t,e,i){return"function"==typeof i&&(i=i.call(q,K,Q)),W(t,e,i,null,K.isRTL)},q.formatDate=function(t,e){return"function"==typeof e&&(e=e.call(q,K,Q)),V(t,e)},c.call(q,K);var te,ee,ie,ne,re,oe,se,le,ae=q.isFetchNeeded,ce=q.fetchEvents,de=i[0],he=0,ue=[];le=null!=K.defaultDate?q.moment(K.defaultDate):q.getNow(),q.getSuggestedViewHeight=function(){return void 0===oe&&p(),oe},q.isHeightAuto=function(){return"auto"===K.contentHeight||"auto"===K.height}}function a(e,i){function n(){var e=i.header;return f=i.theme?"ui":"fc",e?g=t("
").append(o("left")).append(o("right")).append(o("center")).append('
'):void 0}function r(){g.remove()}function o(n){var r=t('
'),o=i.header[n];return o&&t.each(o.split(" "),function(){var n,o=t(),s=!0;t.each(this.split(","),function(n,r){var l,a,c,d,h,u,g,m;"title"==r?(o=o.add(t("

 

")),s=!1):(e[r]?l=function(){e[r]()}:xe[r]&&(l=function(){e.changeView(r)},p.push(r)),l&&(a=S(i.themeButtonIcons,r),c=S(i.buttonIcons,r),d=S(i.defaultButtonText,r),h=S(i.buttonText,r),u=h?R(h):a&&i.theme?"":c&&!i.theme?"":R(d||r),g=["fc-"+r+"-button",f+"-button",f+"-state-default"],m=t('").click(function(){m.hasClass(f+"-state-disabled")||(l(),(m.hasClass(f+"-state-active")||m.hasClass(f+"-state-disabled"))&&m.removeClass(f+"-state-hover"))}).mousedown(function(){m.not("."+f+"-state-active").not("."+f+"-state-disabled").addClass(f+"-state-down")}).mouseup(function(){m.removeClass(f+"-state-down")}).hover(function(){m.not("."+f+"-state-active").not("."+f+"-state-disabled").addClass(f+"-state-hover")},function(){m.removeClass(f+"-state-hover").removeClass(f+"-state-down")}),o=o.add(m)))}),s&&o.first().addClass(f+"-corner-left").end().last().addClass(f+"-corner-right").end(),o.length>1?(n=t("
"),s&&n.addClass("fc-button-group"),n.append(o),r.append(n)):r.append(o)}),r}function s(t){g.find("h2").text(t)}function l(t){g.find(".fc-"+t+"-button").addClass(f+"-state-active")}function a(t){g.find(".fc-"+t+"-button").removeClass(f+"-state-active")}function c(t){g.find(".fc-"+t+"-button").attr("disabled","disabled").addClass(f+"-state-disabled")}function d(t){g.find(".fc-"+t+"-button").removeAttr("disabled").removeClass(f+"-state-disabled")}function h(){return p}var u=this;u.render=n,u.destroy=r,u.updateTitle=s,u.activateButton=l,u.deactivateButton=a,u.disableButton=c,u.enableButton=d,u.getViewsWithButtons=h;var f,g=t(),p=[]}function c(e){function i(t,e){return!T||t.clone().stripZone()C.clone().stripZone()}function n(t,e){T=t,C=e,A=[];var i=++G,n=L.length;N=n;for(var o=0;n>o;o++)r(L[o],i)}function r(e,i){o(e,function(n){var r,o,s=t.isArray(e.events);if(i==G){if(n)for(r=0;n.length>r;r++)o=n[r],s||(o=w(o,e)),o&&A.push(o);N--,N||R(A)}})}function o(i,n){var r,s,l=He.sourceFetchers;for(r=0;l.length>r;r++){if(s=l[r].call(S,i,T.clone(),C.clone(),e.timezone,n),s===!0)return;if("object"==typeof s)return o(s,n),void 0}var a=i.events;if(a)t.isFunction(a)?(v(),a.call(S,T.clone(),C.clone(),e.timezone,function(t){n(t),y()})):t.isArray(a)?n(a):n();else{var c=i.url;if(c){var d,h=i.success,u=i.error,f=i.complete;d=t.isFunction(i.data)?i.data():i.data;var g=t.extend({},d||{}),p=M(i.startParam,e.startParam),m=M(i.endParam,e.endParam),w=M(i.timezoneParam,e.timezoneParam);p&&(g[p]=T.format()),m&&(g[m]=C.format()),e.timezone&&"local"!=e.timezone&&(g[w]=e.timezone),v(),t.ajax(t.extend({},ke,i,{data:g,success:function(e){e=e||[];var i=k(h,this,arguments);t.isArray(i)&&(e=i),n(e)},error:function(){k(u,this,arguments),n()},complete:function(){k(f,this,arguments),y()}}))}else n()}}function s(t){var e=l(t);e&&(L.push(e),N++,r(e,G))}function l(e){var i,n,r=He.sourceNormalizers;if(t.isFunction(e)||t.isArray(e)?i={events:e}:"string"==typeof e?i={url:e}:"object"==typeof e&&(i=t.extend({},e)),i){for(i.className?"string"==typeof i.className&&(i.className=i.className.split(/\s+/)):i.className=[],t.isArray(i.events)&&(i.origArray=i.events,i.events=t.map(i.events,function(t){return w(t,i)})),n=0;r.length>n;n++)r[n].call(S,i);return i}}function a(e){L=t.grep(L,function(t){return!c(t,e)}),A=t.grep(A,function(t){return!c(t.source,e)}),R(A)}function c(t,e){return t&&e&&h(t)==h(e)}function h(t){return("object"==typeof t?t.origArray||t.url||t.events:null)||t}function u(t){t.start=S.moment(t.start),t.end&&(t.end=S.moment(t.end)),E(t),f(t),R(A)}function f(t){var e,i,n,r;for(e=0;A.length>e;e++)if(i=A[e],i._id==t._id&&i!==t)for(n=0;V.length>n;n++)r=V[n],void 0!==t[r]&&(i[r]=t[r])}function g(t,e){var i=w(t);i&&(i.source||(e&&(z.events.push(i),i.source=z),A.push(i)),R(A))}function p(e){var i,n;for(null==e?e=function(){return!0}:t.isFunction(e)||(i=e+"",e=function(t){return t._id==i}),A=t.grep(A,e,!0),n=0;L.length>n;n++)t.isArray(L[n].events)&&(L[n].events=t.grep(L[n].events,e,!0));R(A)}function m(e){return t.isFunction(e)?t.grep(A,e):null!=e?(e+="",t.grep(A,function(t){return t._id==e})):A}function v(){Y++||H("loading",null,!0,x())}function y(){--Y||H("loading",null,!1,x())}function w(i,n){var r,o,s,l,a={};return e.eventDataTransform&&(i=e.eventDataTransform(i)),n&&n.eventDataTransform&&(i=n.eventDataTransform(i)),r=S.moment(i.start||i.date),r.isValid()&&(o=null,!i.end||(o=S.moment(i.end),o.isValid()))?(s=i.allDay,void 0===s&&(l=M(n?n.allDayDefault:void 0,e.allDayDefault),s=void 0!==l?l:!(r.hasTime()||o&&o.hasTime())),s?(r.hasTime()&&r.stripTime(),o&&o.hasTime()&&o.stripTime()):(r.hasTime()||(r=S.rezoneDate(r)),o&&!o.hasTime()&&(o=S.rezoneDate(o))),t.extend(a,i),n&&(a.source=n),a._id=i._id||(void 0===i.id?"_fc"+Me++:i.id+""),a.className=i.className?"string"==typeof i.className?i.className.split(/\s+/):i.className:[],a.allDay=s,a.start=r,a.end=o,e.forceEventDuration&&!a.end&&(a.end=P(a)),d(a),a):void 0}function E(t,e,i){var n,r,o,s,l=t._allDay,a=t._start,c=t._end,d=!1;return e||i||(e=t.start,i=t.end),n=t.allDay!=l?t.allDay:!(e||i).hasTime(),n&&(e&&(e=e.clone().stripTime()),i&&(i=i.clone().stripTime())),e&&(r=n?D(e,a.clone().stripTime()):D(e,a)),n!=l?d=!0:i&&(o=D(i||S.getDefaultEventEnd(n,e||a),e||a).subtract(D(c||S.getDefaultEventEnd(l,a),a))),s=b(m(t._id),d,n,r,o),{dateDelta:r,durationDelta:o,undo:s}}function b(i,n,r,o,s){var l=S.getIsAmbigTimezone(),a=[];return t.each(i,function(t,i){var c=i._allDay,h=i._start,u=i._end,f=null!=r?r:c,g=h.clone(),p=!n&&u?u.clone():null;f?(g.stripTime(),p&&p.stripTime()):(g.hasTime()||(g=S.rezoneDate(g)),p&&!p.hasTime()&&(p=S.rezoneDate(p))),p||!e.forceEventDuration&&!+s||(p=S.getDefaultEventEnd(f,g)),g.add(o),p&&p.add(o).add(s),l&&(+o||+s)&&(g.stripZone(),p&&p.stripZone()),i.allDay=f,i.start=g,i.end=p,d(i),a.push(function(){i.allDay=c,i.start=h,i.end=u,d(i)})}),function(){for(var t=0;a.length>t;t++)a[t]()}}var S=this;S.isFetchNeeded=i,S.fetchEvents=n,S.addEventSource=s,S.removeEventSource=a,S.updateEvent=u,S.renderEvent=g,S.removeEvents=p,S.clientEvents=m,S.mutateEvent=E;var T,C,H=S.trigger,x=S.getView,R=S.reportEvents,P=S.getEventEnd,z={events:[]},L=[z],G=0,N=0,Y=0,A=[];t.each((e.events?[e.events]:[]).concat(e.eventSources||[]),function(t,e){var i=l(e);i&&L.push(i)});var V=["title","url","allDay","className","editable","color","backgroundColor","borderColor","textColor"]}function d(t){t._allDay=t.allDay,t._start=t.start.clone(),t._end=t.end?t.end.clone():null}function h(t,e){e.left&&t.css({"border-left-width":1,"margin-left":e.left-1}),e.right&&t.css({"border-right-width":1,"margin-right":e.right-1})}function u(t){t.css({"margin-left":"","margin-right":"","border-left-width":"","border-right-width":""})}function f(e,i,n){var r=Math.floor(i/e.length),o=Math.floor(i-r*(e.length-1)),s=[],l=[],a=[],c=0;g(e),e.each(function(i,n){var d=i===e.length-1?o:r,h=t(n).outerHeight(!0);d>h?(s.push(n),l.push(h),a.push(t(n).height())):c+=h}),n&&(i-=c,r=Math.floor(i/s.length),o=Math.floor(i-r*(s.length-1))),t(s).each(function(e,i){var n=e===s.length-1?o:r,c=l[e],d=a[e],h=n-(c-d);n>c&&t(i).height(h)})}function g(t){t.height("")}function p(e){var i=0;return e.find("> *").each(function(e,n){var r=t(n).outerWidth();r>i&&(i=r)}),i++,e.width(i),i}function m(t,e){return t.height(e).addClass("fc-scroller"),t[0].scrollHeight-1>t[0].clientHeight?!0:(v(t),!1)}function v(t){t.height("").removeClass("fc-scroller")}function y(e){var i=e.css("position"),n=e.parents().filter(function(){var e=t(this);return/(auto|scroll)/.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&n.length?n:t(e[0].ownerDocument||document)}function w(t){var e=t.offset().left,i=e+t.width(),n=t.children(),r=n.offset().left,o=r+n.outerWidth();return{left:r-e,right:i-o}}function E(t){return 1==t.which&&!t.ctrlKey}function b(t,e,i,n){var r,o,s,l;return e>i&&n>t?(t>=i?(r=t.clone(),s=!0):(r=i.clone(),s=!1),n>=e?(o=e.clone(),l=!0):(o=n.clone(),l=!1),{start:r,end:o,isStart:s,isEnd:l}):void 0}function S(t,e){if(t=t||{},void 0!==t[e])return t[e];for(var i,n=e.split(/(?=[A-Z])/),r=n.length-1;r>=0;r--)if(i=t[n[r].toLowerCase()],void 0!==i)return i;return t["default"]}function D(t,i){return e.duration({days:t.clone().stripTime().diff(i.clone().stripTime(),"days"),ms:t.time()-i.time()})}function T(t){return"[object Date]"===Object.prototype.toString.call(t)||t instanceof Date}function C(t,e){return t-e}function H(t){var e=function(){};return e.prototype=t,new e}function x(t,e){for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i])}function k(e,i,n){if(t.isFunction(e)&&(e=[e]),e){var r,o;for(r=0;e.length>r;r++)o=e[r].apply(i,n)||o;return o}}function M(){for(var t=0;arguments.length>t;t++)if(void 0!==arguments[t])return arguments[t]}function R(t){return(t+"").replace(/&/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""").replace(/\n/g,"
")}function P(t){return t.replace(/&.*?;/g,"")}function z(t){return t.charAt(0).toUpperCase()+t.slice(1)}function L(t,e){var i,n,r,o,s=function(){var l=+new Date-o;e>l&&l>0?i=setTimeout(s,e-l):(i=null,t.apply(r,n),i||(r=n=null))};return function(){r=this,n=arguments,o=+new Date,i||(i=setTimeout(s,e))}}function G(i,n,r){var o,s,l,a,c=i[0],d=1==i.length&&"string"==typeof c;return e.isMoment(c)?(a=e.apply(null,i),c._ambigTime&&(a._ambigTime=!0),c._ambigZone&&(a._ambigZone=!0)):T(c)||void 0===c?a=e.apply(null,i):(o=!1,s=!1,d?Pe.test(c)?(c+="-01",i=[c],o=!0,s=!0):(l=ze.exec(c))&&(o=!l[5],s=!0):t.isArray(c)&&(s=!0),a=n?e.utc.apply(e,i):e.apply(null,i),o?(a._ambigTime=!0,a._ambigZone=!0):r&&(s?a._ambigZone=!0:d&&a.zone(c))),new N(a)}function N(t){x(this,t)}function Y(t,e){var i,n=[],r=!1,o=!1;for(i=0;t.length>i;i++)n.push(He.moment.parseZone(t[i])),r=r||n[i]._ambigTime,o=o||n[i]._ambigZone;for(i=0;n.length>i;i++)r&&!e?n[i].stripTime():o&&n[i].stripZone();return n}function A(t,i){return e.fn.format.call(t,i)}function V(t,e){return _(t,Z(e))}function _(t,e){var i,n="";for(i=0;e.length>i;i++)n+=O(t,e[i]);return n}function O(t,e){var i,n;return"string"==typeof e?e:(i=e.token)?Le[i]?Le[i](t):A(t,i):e.maybe&&(n=_(t,e.maybe),n.match(/[1-9]/))?n:""}function W(t,e,i,n,r){var o;return t=He.moment.parseZone(t),e=He.moment.parseZone(e),o=(t.localeData||t.lang).call(t),i=o.longDateFormat(i)||i,n=n||" - ",F(t,e,Z(i),n,r)}function F(t,e,i,n,r){var o,s,l,a,c="",d="",h="",u="",f="";for(s=0;i.length>s&&(o=I(t,e,i[s]),o!==!1);s++)c+=o;for(l=i.length-1;l>s&&(o=I(t,e,i[l]),o!==!1);l--)d=o+d;for(a=s;l>=a;a++)h+=O(t,i[a]),u+=O(e,i[a]);return(h||u)&&(f=r?u+n+h:h+n+u),c+f+d}function I(t,e,i){var n,r;return"string"==typeof i?i:(n=i.token)&&(r=Ge[n.charAt(0)],r&&t.isSame(e,r))?A(t,n):!1}function Z(t){return t in Ne?Ne[t]:Ne[t]=B(t)}function B(t){for(var e,i=[],n=/\[([^\]]*)\]|\(([^\)]*)\)|(LT|(\w)\4*o?)|([^\w\[\(]+)/g;e=n.exec(t);)e[1]?i.push(e[1]):e[2]?i.push({maybe:B(e[2])}):e[3]?i.push({token:e[3]}):e[5]&&i.push(e[5]);return i}function j(t){this.options=t||{}}function X(t){this.grid=t}function $(t){this.coordMaps=t}function q(t,e){this.coordMap=t,this.options=e||{}}function U(t,e){return t||e?t&&e?t.grid===e.grid&&t.row===e.row&&t.col===e.col:!1:!0}function K(e,i){this.options=i=i||{},this.sourceEl=e,this.parentEl=i.parentEl?t(i.parentEl):e.parent()}function Q(t){this.view=t}function J(t){Q.call(this,t),this.coordMap=new X(this)}function te(t,e){return t.eventStartMS-e.eventStartMS||e.eventDurationMS-t.eventDurationMS||e.event.allDay-t.event.allDay||(t.event.title||"").localeCompare(e.event.title)}function ee(t){J.call(this,t)}function ie(t,e){var i,n;for(i=0;e.length>i;i++)if(n=e[i],n.leftCol<=t.rightCol&&n.rightCol>=t.leftCol)return!0;return!1}function ne(t,e){return t.leftCol-e.leftCol}function re(t){J.call(this,t)}function oe(t){var e,i,n;if(t.sort(te),e=se(t),le(e),i=e[0]){for(n=0;i.length>n;n++)ae(i[n]);for(n=0;i.length>n;n++)ce(i[n],0,0)}}function se(t){var e,i,n,r=[];for(e=0;t.length>e;e++){for(i=t[e],n=0;r.length>n&&de(i,r[n]).length;n++);i.level=n,(r[n]||(r[n]=[])).push(i)}return r}function le(t){var e,i,n,r,o;for(e=0;t.length>e;e++)for(i=t[e],n=0;i.length>n;n++)for(r=i[n],r.forwardSegs=[],o=e+1;t.length>o;o++)de(r,t[o],r.forwardSegs)}function ae(t){var e,i,n=t.forwardSegs,r=0;if(void 0===t.forwardPressure){for(e=0;n.length>e;e++)i=n[e],ae(i),r=Math.max(r,1+i.forwardPressure);t.forwardPressure=r}}function ce(t,e,i){var n,r=t.forwardSegs;if(void 0===t.forwardCoord)for(r.length?(r.sort(ue),ce(r[0],e+1,i),t.forwardCoord=r[0].backwardCoord):t.forwardCoord=1,t.backwardCoord=t.forwardCoord-(t.forwardCoord-i)/(e+1),n=0;r.length>n;n++)ce(r[n],0,t.forwardCoord)}function de(t,e,i){i=i||[];for(var n=0;e.length>n;n++)he(t,e[n])&&i.push(e[n]);return i}function he(t,e){return t.bottom>e.top&&t.topd;d++){var h=d*n,u=h+n-1,f=Math.max(a,h),g=Math.min(c,u);if(g>=f){var m=E(f),v=E(g),b=[m.col,v.col].sort(),S=p(f)==s,T=p(g)+1==l;r.push({row:d,leftCol:b[0],rightCol:b[1],isStart:S,isEnd:T})}}return r}function D(t,e){var i,n,r=t.clone().stripTime();return e&&(i=e.clone().stripTime(),n=+e.time(),n&&n>=k&&i.add(1,"days")),(!e||r>=i)&&(i=r.clone().add(1,"days")),{start:r,end:i}}function T(t){var e=D(t.start,t.end);return e.end.diff(e.start,"days")>1}var C=this;C.calendar=i,C.opt=n,C.trigger=r,C.isEventDraggable=o,C.isEventResizable=l,C.eventDrop=a,C.eventResize=c;var H=i.reportEventChange,x=i.options,k=e.duration(x.nextDayThreshold);C.init(),C.getEventTimeText=function(t,e){var r,o;return"object"==typeof t&&"object"==typeof e?(r=t,o=e,e=arguments[2]):(r=t.start,o=t.end),e=e||n("timeFormat"),o&&n("displayEventEnd")?i.formatRange(r,o,e):i.formatDate(r,e)},C.isHiddenDay=d,C.skipHiddenDays=u,C.getCellsPerWeek=h,C.dateToCell=v,C.dateToDayOffset=y,C.dayOffsetToCellOffset=w,C.cellOffsetToCell=E,C.cellToDate=f,C.cellToCellOffset=g,C.cellOffsetToDayOffset=p,C.dayOffsetToDate=m,C.rangeToSegments=b,C.isMultiDayEvent=T;var R,P=n("hiddenDays")||[],z=[],L=[],G=[],N=n("isRTL");(function(){n("weekends")===!1&&P.push(0,6);for(var e=0,i=0;7>e;e++)L[e]=i,z[e]=-1!=t.inArray(e,P),z[e]||(G[i]=e,i++);if(R=i,!R)throw"invalid hiddenDays"})()}function ge(t){fe.call(this,t),this.dayGrid=new ee(this),this.coordMap=this.dayGrid.coordMap}function pe(t){ge.call(this,t)}function me(t){ge.call(this,t)}function ve(t){ge.call(this,t)}function ye(t,e){return e.longDateFormat("LT").replace(":mm","(:mm)").replace(/(\Wmm)$/,"($1)").replace(/\s*a$/i,"a")}function we(t,e){return e.longDateFormat("LT").replace(/\s*a$/i,"")}function Ee(t){fe.call(this,t),this.timeGrid=new re(this),this.opt("allDaySlot")?(this.dayGrid=new ee(this),this.coordMap=new $([this.dayGrid.coordMap,this.timeGrid.coordMap])):this.coordMap=this.timeGrid.coordMap}function be(t){Ee.call(this,t)}function Se(t){Ee.call(this,t)}var De={lang:"en",defaultTimedEventDuration:"02:00:00",defaultAllDayEventDuration:{days:1},forceEventDuration:!1,nextDayThreshold:"09:00:00",defaultView:"month",aspectRatio:1.35,header:{left:"title",center:"",right:"today prev,next"},weekends:!0,weekNumbers:!1,weekNumberTitle:"W",weekNumberCalculation:"local",lazyFetching:!0,startParam:"start",endParam:"end",timezoneParam:"timezone",timezone:!1,titleFormat:{month:"MMMM YYYY",week:"ll",day:"LL"},columnFormat:{month:"ddd",week:n,day:"dddd"},timeFormat:{"default":i},displayEventEnd:{month:!1,basicWeek:!1,"default":!0},isRTL:!1,defaultButtonText:{prev:"prev",next:"next",prevYear:"prev year",nextYear:"next year",today:"today",month:"month",week:"week",day:"day"},buttonIcons:{prev:"left-single-arrow",next:"right-single-arrow",prevYear:"left-double-arrow",nextYear:"right-double-arrow"},theme:!1,themeButtonIcons:{prev:"circle-triangle-w",next:"circle-triangle-e",prevYear:"seek-prev",nextYear:"seek-next"},dragOpacity:.75,dragRevertDuration:500,dragScroll:!0,unselectAuto:!0,dropAccept:"*",eventLimit:!1,eventLimitText:"more",eventLimitClick:"popover",dayPopoverFormat:"LL",handleWindowResize:!0,windowResizeDelay:200},Te={en:{columnFormat:{week:"ddd M/D"},dayPopoverFormat:"dddd, MMMM D"}},Ce={header:{left:"next,prev today",center:"",right:"title"},buttonIcons:{prev:"right-single-arrow",next:"left-single-arrow",prevYear:"right-double-arrow",nextYear:"left-double-arrow"},themeButtonIcons:{prev:"circle-triangle-e",next:"circle-triangle-w",nextYear:"seek-prev",prevYear:"seek-next"}},He=t.fullCalendar={version:"2.1.1"},xe=He.views={};t.fn.fullCalendar=function(e){var i=Array.prototype.slice.call(arguments,1),n=this;return this.each(function(r,o){var s,a=t(o),c=a.data("fullCalendar");"string"==typeof e?c&&t.isFunction(c[e])&&(s=c[e].apply(c,i),r||(n=s),"destroy"===e&&a.removeData("fullCalendar")):c||(c=new l(a,e),a.data("fullCalendar",c),c.render())}),n},He.langs=Te,He.datepickerLang=function(e,i,n){var r=Te[e];r||(r=Te[e]={}),o(r,{isRTL:n.isRTL,weekNumberTitle:n.weekHeader,titleFormat:{month:n.showMonthAfterYear?"YYYY["+n.yearSuffix+"] MMMM":"MMMM YYYY["+n.yearSuffix+"]"},defaultButtonText:{prev:P(n.prevText),next:P(n.nextText),today:P(n.currentText)}}),t.datepicker&&(t.datepicker.regional[i]=t.datepicker.regional[e]=n,t.datepicker.regional.en=t.datepicker.regional[""],t.datepicker.setDefaults(n))},He.lang=function(t,e){var i;e&&(i=Te[t],i||(i=Te[t]={}),o(i,e||{})),De.lang=t},He.sourceNormalizers=[],He.sourceFetchers=[];var ke={dataType:"json",cache:!1},Me=1,Re=["sun","mon","tue","wed","thu","fri","sat"];He.applyAll=k;var Pe=/^\s*\d{4}-\d\d$/,ze=/^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?)?$/;He.moment=function(){return G(arguments)},He.moment.utc=function(){var t=G(arguments,!0);return t.hasTime()&&t.utc(),t},He.moment.parseZone=function(){return G(arguments,!0,!0)},N.prototype=H(e.fn),N.prototype.clone=function(){return G([this])},N.prototype.time=function(t){if(null==t)return e.duration({hours:this.hours(),minutes:this.minutes(),seconds:this.seconds(),milliseconds:this.milliseconds()});delete this._ambigTime,e.isDuration(t)||e.isMoment(t)||(t=e.duration(t));var i=0;return e.isDuration(t)&&(i=24*Math.floor(t.asDays())),this.hours(i+t.hours()).minutes(t.minutes()).seconds(t.seconds()).milliseconds(t.milliseconds())},N.prototype.stripTime=function(){var t=this.toArray();return e.fn.utc.call(this),this.year(t[0]).month(t[1]).date(t[2]).hours(0).minutes(0).seconds(0).milliseconds(0),this._ambigTime=!0,this._ambigZone=!0,this},N.prototype.hasTime=function(){return!this._ambigTime},N.prototype.stripZone=function(){var t=this.toArray(),i=this._ambigTime;return e.fn.utc.call(this),this.year(t[0]).month(t[1]).date(t[2]).hours(t[3]).minutes(t[4]).seconds(t[5]).milliseconds(t[6]),i&&(this._ambigTime=!0),this._ambigZone=!0,this},N.prototype.hasZone=function(){return!this._ambigZone},N.prototype.zone=function(t){return null!=t&&(delete this._ambigTime,delete this._ambigZone),e.fn.zone.apply(this,arguments)},N.prototype.local=function(){var t=this.toArray(),i=this._ambigZone;return delete this._ambigTime,delete this._ambigZone,e.fn.local.apply(this,arguments),i&&this.year(t[0]).month(t[1]).date(t[2]).hours(t[3]).minutes(t[4]).seconds(t[5]).milliseconds(t[6]),this},N.prototype.utc=function(){return delete this._ambigTime,delete this._ambigZone,e.fn.utc.apply(this,arguments)},N.prototype.format=function(){return arguments[0]?V(this,arguments[0]):this._ambigTime?A(this,"YYYY-MM-DD"):this._ambigZone?A(this,"YYYY-MM-DD[T]HH:mm:ss"):A(this)},N.prototype.toISOString=function(){return this._ambigTime?A(this,"YYYY-MM-DD"):this._ambigZone?A(this,"YYYY-MM-DD[T]HH:mm:ss"):e.fn.toISOString.apply(this,arguments)},N.prototype.isWithin=function(t,e){var i=Y([this,t,e]);return i[0]>=i[1]&&i[0]').addClass(i.className||"").css({top:0,left:0}).append(i.content).appendTo(i.parentEl),this.el.on("click",".fc-close",function(){e.hide()}),i.autoHide&&t(document).on("mousedown",this.documentMousedownProxy=t.proxy(this,"documentMousedown"))},documentMousedown:function(e){this.el&&!t(e.target).closest(this.el).length&&this.hide()},destroy:function(){this.hide(),this.el&&(this.el.remove(),this.el=null),t(document).off("mousedown",this.documentMousedownProxy)},position:function(){var e,i,n,r,o,s=this.options,l=this.el.offsetParent().offset(),a=this.el.outerWidth(),c=this.el.outerHeight(),d=t(window),h=y(this.el);r=s.top||0,o=void 0!==s.left?s.left:void 0!==s.right?s.right-a:0,h.is(window)||h.is(document)?(h=d,e=0,i=0):(n=h.offset(),e=n.top,i=n.left),e+=d.scrollTop(),i+=d.scrollLeft(),s.viewportConstrain!==!1&&(r=Math.min(r,e+h.outerHeight()-c-this.margin),r=Math.max(r,e+this.margin),o=Math.min(o,i+h.outerWidth()-a-this.margin),o=Math.max(o,i+this.margin)),this.el.css({top:r-l.top,left:o-l.left})},trigger:function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1))}},X.prototype={grid:null,rows:null,cols:null,containerEl:null,minX:null,maxX:null,minY:null,maxY:null,build:function(){this.grid.buildCoords(this.rows=[],this.cols=[]),this.computeBounds()},getCell:function(t,e){var i,n=null,r=this.rows,o=this.cols,s=-1,l=-1;if(this.inBounds(t,e)){for(i=0;r.length>i;i++)if(e>=r[i][0]&&r[i][1]>e){s=i;break}for(i=0;o.length>i;i++)if(t>=o[i][0]&&o[i][1]>t){l=i;break}s>=0&&l>=0&&(n={row:s,col:l},n.grid=this.grid,n.date=this.grid.getCellDate(n))}return n},computeBounds:function(){var t;this.containerEl&&(t=this.containerEl.offset(),this.minX=t.left,this.maxX=t.left+this.containerEl.outerWidth(),this.minY=t.top,this.maxY=t.top+this.containerEl.outerHeight())},inBounds:function(t,e){return this.containerEl?t>=this.minX&&this.maxX>t&&e>=this.minY&&this.maxY>e:!0}},$.prototype={coordMaps:null,build:function(){var t,e=this.coordMaps;for(t=0;e.length>t;t++)e[t].build()},getCell:function(t,e){var i,n=this.coordMaps,r=null;for(i=0;n.length>i&&!r;i++)r=n[i].getCell(t,e);return r}},q.prototype={coordMap:null,options:null,isListening:!1,isDragging:!1,origCell:null,origDate:null,cell:null,date:null,mouseX0:null,mouseY0:null,mousemoveProxy:null,mouseupProxy:null,scrollEl:null,scrollBounds:null,scrollTopVel:null,scrollLeftVel:null,scrollIntervalId:null,scrollHandlerProxy:null,scrollSensitivity:30,scrollSpeed:200,scrollIntervalMs:50,mousedown:function(t){E(t)&&(t.preventDefault(),this.startListening(t),this.options.distance||this.startDrag(t))},startListening:function(e){var i,n;this.isListening||(e&&this.options.scroll&&(i=y(t(e.target)),i.is(window)||i.is(document)||(this.scrollEl=i,this.scrollHandlerProxy=L(t.proxy(this,"scrollHandler"),100),this.scrollEl.on("scroll",this.scrollHandlerProxy))),this.computeCoords(),e&&(n=this.getCell(e),this.origCell=n,this.origDate=n?n.date:null,this.mouseX0=e.pageX,this.mouseY0=e.pageY),t(document).on("mousemove",this.mousemoveProxy=t.proxy(this,"mousemove")).on("mouseup",this.mouseupProxy=t.proxy(this,"mouseup")).on("selectstart",this.preventDefault),this.isListening=!0,this.trigger("listenStart",e))},computeCoords:function(){this.coordMap.build(),this.computeScrollBounds()},mousemove:function(t){var e,i;this.isDragging||(e=this.options.distance||1,i=Math.pow(t.pageX-this.mouseX0,2)+Math.pow(t.pageY-this.mouseY0,2),i>=e*e&&this.startDrag(t)),this.isDragging&&this.drag(t)},startDrag:function(t){var e;this.isListening||this.startListening(),this.isDragging||(this.isDragging=!0,this.trigger("dragStart",t),e=this.getCell(t),e&&this.cellOver(e,!0)) +},drag:function(t){var e;this.isDragging&&(e=this.getCell(t),U(e,this.cell)||(this.cell&&this.cellOut(),e&&this.cellOver(e)),this.dragScroll(t))},cellOver:function(t){this.cell=t,this.date=t.date,this.trigger("cellOver",t,t.date)},cellOut:function(){this.cell&&(this.trigger("cellOut",this.cell),this.cell=null,this.date=null)},mouseup:function(t){this.stopDrag(t),this.stopListening(t)},stopDrag:function(t){this.isDragging&&(this.stopScrolling(),this.trigger("dragStop",t),this.isDragging=!1)},stopListening:function(e){this.isListening&&(this.scrollEl&&(this.scrollEl.off("scroll",this.scrollHandlerProxy),this.scrollHandlerProxy=null),t(document).off("mousemove",this.mousemoveProxy).off("mouseup",this.mouseupProxy).off("selectstart",this.preventDefault),this.mousemoveProxy=null,this.mouseupProxy=null,this.isListening=!1,this.trigger("listenStop",e),this.origCell=this.cell=null,this.origDate=this.date=null)},getCell:function(t){return this.coordMap.getCell(t.pageX,t.pageY)},trigger:function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1))},preventDefault:function(t){t.preventDefault()},computeScrollBounds:function(){var t,e=this.scrollEl;e&&(t=e.offset(),this.scrollBounds={top:t.top,left:t.left,bottom:t.top+e.outerHeight(),right:t.left+e.outerWidth()})},dragScroll:function(t){var e,i,n,r,o=this.scrollSensitivity,s=this.scrollBounds,l=0,a=0;s&&(e=(o-(t.pageY-s.top))/o,i=(o-(s.bottom-t.pageY))/o,n=(o-(t.pageX-s.left))/o,r=(o-(s.right-t.pageX))/o,e>=0&&1>=e?l=-1*e*this.scrollSpeed:i>=0&&1>=i&&(l=i*this.scrollSpeed),n>=0&&1>=n?a=-1*n*this.scrollSpeed:r>=0&&1>=r&&(a=r*this.scrollSpeed)),this.setScrollVel(l,a)},setScrollVel:function(e,i){this.scrollTopVel=e,this.scrollLeftVel=i,this.constrainScrollVel(),!this.scrollTopVel&&!this.scrollLeftVel||this.scrollIntervalId||(this.scrollIntervalId=setInterval(t.proxy(this,"scrollIntervalFunc"),this.scrollIntervalMs))},constrainScrollVel:function(){var t=this.scrollEl;0>this.scrollTopVel?0>=t.scrollTop()&&(this.scrollTopVel=0):this.scrollTopVel>0&&t.scrollTop()+t[0].clientHeight>=t[0].scrollHeight&&(this.scrollTopVel=0),0>this.scrollLeftVel?0>=t.scrollLeft()&&(this.scrollLeftVel=0):this.scrollLeftVel>0&&t.scrollLeft()+t[0].clientWidth>=t[0].scrollWidth&&(this.scrollLeftVel=0)},scrollIntervalFunc:function(){var t=this.scrollEl,e=this.scrollIntervalMs/1e3;this.scrollTopVel&&t.scrollTop(t.scrollTop()+this.scrollTopVel*e),this.scrollLeftVel&&t.scrollLeft(t.scrollLeft()+this.scrollLeftVel*e),this.constrainScrollVel(),this.scrollTopVel||this.scrollLeftVel||this.stopScrolling()},stopScrolling:function(){this.scrollIntervalId&&(clearInterval(this.scrollIntervalId),this.scrollIntervalId=null,this.computeCoords())},scrollHandler:function(){this.scrollIntervalId||this.computeCoords()}},K.prototype={options:null,sourceEl:null,el:null,parentEl:null,top0:null,left0:null,mouseY0:null,mouseX0:null,topDelta:null,leftDelta:null,mousemoveProxy:null,isFollowing:!1,isHidden:!1,isAnimating:!1,start:function(e){this.isFollowing||(this.isFollowing=!0,this.mouseY0=e.pageY,this.mouseX0=e.pageX,this.topDelta=0,this.leftDelta=0,this.isHidden||this.updatePosition(),t(document).on("mousemove",this.mousemoveProxy=t.proxy(this,"mousemove")))},stop:function(e,i){function n(){this.isAnimating=!1,r.destroyEl(),this.top0=this.left0=null,i&&i()}var r=this,o=this.options.revertDuration;this.isFollowing&&!this.isAnimating&&(this.isFollowing=!1,t(document).off("mousemove",this.mousemoveProxy),e&&o&&!this.isHidden?(this.isAnimating=!0,this.el.animate({top:this.top0,left:this.left0},{duration:o,complete:n})):n())},getEl:function(){var t=this.el;return t||(this.sourceEl.width(),t=this.el=this.sourceEl.clone().css({position:"absolute",visibility:"",display:this.isHidden?"none":"",margin:0,right:"auto",bottom:"auto",width:this.sourceEl.width(),height:this.sourceEl.height(),opacity:this.options.opacity||"",zIndex:this.options.zIndex}).appendTo(this.parentEl)),t},destroyEl:function(){this.el&&(this.el.remove(),this.el=null)},updatePosition:function(){var t,e;this.getEl(),null===this.top0&&(this.sourceEl.width(),t=this.sourceEl.offset(),e=this.el.offsetParent().offset(),this.top0=t.top-e.top,this.left0=t.left-e.left),this.el.css({top:this.top0+this.topDelta,left:this.left0+this.leftDelta})},mousemove:function(t){this.topDelta=t.pageY-this.mouseY0,this.leftDelta=t.pageX-this.mouseX0,this.isHidden||this.updatePosition()},hide:function(){this.isHidden||(this.isHidden=!0,this.el&&this.el.hide())},show:function(){this.isHidden&&(this.isHidden=!1,this.updatePosition(),this.getEl().show())}},Q.prototype={view:null,cellHtml:"",rowHtml:function(t,e){var i,n,r=this.view,o=this.getHtmlRenderer("cell",t),s="";for(e=e||0,i=0;r.colCnt>i;i++)n=r.cellToDate(e,i),s+=o(e,i,n);return s=this.bookendCells(s,t,e),""+s+""},bookendCells:function(t,e,i){var n=this.view,r=this.getHtmlRenderer("intro",e)(i||0),o=this.getHtmlRenderer("outro",e)(i||0),s=n.opt("isRTL"),l=s?o:r,a=s?r:o;return"string"==typeof t?l+t+a:t.prepend(l).append(a)},getHtmlRenderer:function(t,e){var i,n,r,o,s=this.view;return i=t+"Html",e&&(n=e+z(t)+"Html"),n&&(o=s[n])?r=s:n&&(o=this[n])?r=this:(o=s[i])?r=s:(o=this[i])&&(r=this),"function"==typeof o?function(){return o.apply(r,arguments)||""}:function(){return o||""}}},J.prototype=H(Q.prototype),t.extend(J.prototype,{el:null,coordMap:null,cellDuration:null,render:function(){this.bindHandlers()},destroy:function(){},buildCoords:function(){},getCellDate:function(){},getCellDayEl:function(){},rangeToSegs:function(){},bindHandlers:function(){var e=this;this.el.on("mousedown",function(i){t(i.target).is(".fc-event-container *, .fc-more")||t(i.target).closest(".fc-popover").length||e.dayMousedown(i)}),this.bindSegHandlers()},dayMousedown:function(t){var e,i,n,r=this,o=this.view,s=o.opt("selectable"),l=null,a=new q(this.coordMap,{scroll:o.opt("dragScroll"),dragStart:function(){o.unselect()},cellOver:function(t,o){a.origDate&&(n=r.getCellDayEl(t),l=[o,a.origDate].sort(C),e=l[0],i=l[1].clone().add(r.cellDuration),s&&r.renderSelection(e,i))},cellOut:function(){l=null,r.destroySelection()},listenStop:function(t){l&&(l[0].isSame(l[1])&&o.trigger("dayClick",n[0],e,t),s&&o.reportSelection(e,i,t))}});a.mousedown(t)},renderDrag:function(){},destroyDrag:function(){},renderResize:function(){},destroyResize:function(){},renderRangeHelper:function(t,e,i){var n,r=this.view;!e&&r.opt("forceEventDuration")&&(e=r.calendar.getDefaultEventEnd(!t.hasTime(),t)),n=i?H(i.event):{},n.start=t,n.end=e,n.allDay=!(t.hasTime()||e&&e.hasTime()),n.className=(n.className||[]).concat("fc-helper"),i||(n.editable=!1),this.renderHelper(n,i)},renderHelper:function(){},destroyHelper:function(){},renderSelection:function(t,e){this.renderHighlight(t,e)},destroySelection:function(){this.destroyHighlight()},renderHighlight:function(){},destroyHighlight:function(){},headHtml:function(){return'
'+""+""+this.rowHtml("head")+""+"
"+"
"},headCellHtml:function(t,e,i){var n=this.view,r=n.calendar,o=n.opt("columnFormat");return''+R(r.formatDate(i,o))+""},bgCellHtml:function(t,e,i){var n=this.view,r=this.getDayClasses(i);return r.unshift("fc-day",n.widgetContentClass),''},getDayClasses:function(t){var e=this.view,i=e.calendar.getNow().stripTime(),n=["fc-"+Re[t.day()]];return"month"===e.name&&t.month()!=e.intervalStart.month()&&n.push("fc-other-month"),t.isSame(i,"day")?n.push("fc-today",e.highlightStateClass):i>t?n.push("fc-past"):n.push("fc-future"),n}}),t.extend(J.prototype,{mousedOverSeg:null,isDraggingSeg:!1,isResizingSeg:!1,renderEvents:function(){},getSegs:function(){},destroyEvents:function(){this.triggerSegMouseout()},renderSegs:function(e,i){var n,r=this.view,o="",s=[];for(n=0;e.length>n;n++)o+=this.renderSegHtml(e[n],i);return t(o).each(function(i,n){var o=e[i],l=r.resolveEventEl(o.event,t(n));l&&(l.data("fc-seg",o),o.el=l,s.push(o))}),s},renderSegHtml:function(){},eventsToSegs:function(e,i,n){var r=this;return t.map(e,function(t){return r.eventToSegs(t,i,n)})},eventToSegs:function(t,e,i){var n,r,o,s=t.start.clone().stripZone(),l=this.view.calendar.getEventEnd(t).stripZone();for(e&&i?(o=b(s,l,e,i),n=o?[o]:[]):n=this.rangeToSegs(s,l),r=0;n.length>r;r++)o=n[r],o.event=t,o.eventStartMS=+s,o.eventDurationMS=l-s;return n},bindSegHandlers:function(){var e=this,i=this.view;t.each({mouseenter:function(t,i){e.triggerSegMouseover(t,i)},mouseleave:function(t,i){e.triggerSegMouseout(t,i)},click:function(t,e){return i.trigger("eventClick",this,t.event,e)},mousedown:function(n,r){t(r.target).is(".fc-resizer")&&i.isEventResizable(n.event)?e.segResizeMousedown(n,r):i.isEventDraggable(n.event)&&e.segDragMousedown(n,r)}},function(i,n){e.el.on(i,".fc-event-container > *",function(i){var r=t(this).data("fc-seg");return!r||e.isDraggingSeg||e.isResizingSeg?void 0:n.call(this,r,i)})})},triggerSegMouseover:function(t,e){this.mousedOverSeg||(this.mousedOverSeg=t,this.view.trigger("eventMouseover",t.el[0],t.event,e))},triggerSegMouseout:function(t,e){e=e||{},this.mousedOverSeg&&(t=t||this.mousedOverSeg,this.mousedOverSeg=null,this.view.trigger("eventMouseout",t.el[0],t.event,e))},segDragMousedown:function(t,e){var i,n,r=this,o=this.view,s=t.el,l=t.event,a=new K(t.el,{parentEl:o.el,opacity:o.opt("dragOpacity"),revertDuration:o.opt("dragRevertDuration"),zIndex:2}),c=new q(o.coordMap,{distance:5,scroll:o.opt("dragScroll"),listenStart:function(t){a.hide(),a.start(t)},dragStart:function(e){r.triggerSegMouseout(t,e),r.isDraggingSeg=!0,o.hideEvent(l),o.trigger("eventDragStart",s[0],l,e,{})},cellOver:function(e,s){var l=t.cellDate||c.origDate,d=r.computeDraggedEventDates(t,l,s);i=d.start,n=d.end,o.renderDrag(i,n,t)?a.hide():a.show()},cellOut:function(){i=null,o.destroyDrag(),a.show()},dragStop:function(t){var e=i&&!i.isSame(l.start);a.stop(!e,function(){r.isDraggingSeg=!1,o.destroyDrag(),o.showEvent(l),o.trigger("eventDragStop",s[0],l,t,{}),e&&o.eventDrop(s[0],l,i,t)})},listenStop:function(){a.stop()}});c.mousedown(e)},computeDraggedEventDates:function(t,e,i){var n,r,o,s=this.view,l=t.event,a=l.start,c=s.calendar.getEventEnd(l);return i.hasTime()===e.hasTime()?(n=D(i,e),r=a.clone().add(n),o=null===l.end?null:c.clone().add(n)):(r=i,o=null),{start:r,end:o}},segResizeMousedown:function(t,e){function i(){r.destroyResize(),o.showEvent(l)}var n,r=this,o=this.view,s=t.el,l=t.event,a=l.start,c=o.calendar.getEventEnd(l),d=null;n=new q(this.coordMap,{distance:5,scroll:o.opt("dragScroll"),dragStart:function(e){r.triggerSegMouseout(t,e),r.isResizingSeg=!0,o.trigger("eventResizeStart",s[0],l,e,{})},cellOver:function(e,n){n.isBefore(a)&&(n=a),d=n.clone().add(r.cellDuration),d.isSame(c)?(d=null,i()):(r.renderResize(a,d,t),o.hideEvent(l))},cellOut:function(){d=null,i()},dragStop:function(t){r.isResizingSeg=!1,i(),o.trigger("eventResizeStop",s[0],l,t,{}),d&&o.eventResize(s[0],l,d,t)}}),n.mousedown(e)},getSegClasses:function(t,e,i){var n=t.event,r=["fc-event",t.isStart?"fc-start":"fc-not-start",t.isEnd?"fc-end":"fc-not-end"].concat(n.className,n.source?n.source.className:[]);return e&&r.push("fc-draggable"),i&&r.push("fc-resizable"),r},getEventSkinCss:function(t){var e=this.view,i=t.source||{},n=t.color,r=i.color,o=e.opt("eventColor"),s=t.backgroundColor||n||i.backgroundColor||r||e.opt("eventBackgroundColor")||o,l=t.borderColor||n||i.borderColor||r||e.opt("eventBorderColor")||o,a=t.textColor||i.textColor||e.opt("eventTextColor"),c=[];return s&&c.push("background-color:"+s),l&&c.push("border-color:"+l),a&&c.push("color:"+a),c.join(";")}}),ee.prototype=H(J.prototype),t.extend(ee.prototype,{numbersVisible:!1,cellDuration:e.duration({days:1}),bottomCoordPadding:0,rowEls:null,dayEls:null,helperEls:null,highlightEls:null,render:function(e){var i,n=this.view,r="";for(i=0;n.rowCnt>i;i++)r+=this.dayRowHtml(i,e);this.el.html(r),this.rowEls=this.el.find(".fc-row"),this.dayEls=this.el.find(".fc-day"),this.dayEls.each(function(e,i){var r=n.cellToDate(Math.floor(e/n.colCnt),e%n.colCnt);n.trigger("dayRender",null,r,t(i))}),J.prototype.render.call(this)},destroy:function(){this.destroySegPopover()},dayRowHtml:function(t,e){var i=this.view,n=["fc-row","fc-week",i.widgetContentClass];return e&&n.push("fc-rigid"),'
'+'
'+""+this.rowHtml("day",t)+"
"+"
"+'
'+""+(this.numbersVisible?""+this.rowHtml("number",t)+"":"")+"
"+"
"+"
"},dayCellHtml:function(t,e,i){return this.bgCellHtml(t,e,i)},buildCoords:function(e,i){var n,r,o,s=this.view.colCnt;this.dayEls.slice(0,s).each(function(e,s){n=t(s),r=n.offset().left,e&&(o[1]=r),o=[r],i[e]=o}),o[1]=r+n.outerWidth(),this.rowEls.each(function(i,s){n=t(s),r=n.offset().top,i&&(o[1]=r),o=[r],e[i]=o}),o[1]=r+n.outerHeight()+this.bottomCoordPadding},getCellDate:function(t){return this.view.cellToDate(t)},getCellDayEl:function(t){return this.dayEls.eq(t.row*this.view.colCnt+t.col)},rangeToSegs:function(t,e){return this.view.rangeToSegments(t,e)},renderDrag:function(t,e,i){var n;return this.renderHighlight(t,e||this.view.calendar.getDefaultEventEnd(!0,t)),i&&!i.el.closest(this.el).length?(this.renderRangeHelper(t,e,i),n=this.view.opt("dragOpacity"),void 0!==n&&this.helperEls.css("opacity",n),!0):void 0},destroyDrag:function(){this.destroyHighlight(),this.destroyHelper()},renderResize:function(t,e,i){this.renderHighlight(t,e),this.renderRangeHelper(t,e,i)},destroyResize:function(){this.destroyHighlight(),this.destroyHelper()},renderHelper:function(e,i){var n=[],r=this.renderEventRows([e]);this.rowEls.each(function(e,o){var s,l=t(o),a=t('
');s=i&&i.row===e?i.el.position().top:l.find(".fc-content-skeleton tbody").position().top,a.css("top",s).find("table").append(r[e].tbodyEl),l.append(a),n.push(a[0])}),this.helperEls=t(n)},destroyHelper:function(){this.helperEls&&(this.helperEls.remove(),this.helperEls=null)},renderHighlight:function(e,i){var n,r,o,s=this.rangeToSegs(e,i),l=[];for(n=0;s.length>n;n++)r=s[n],o=t(this.highlightSkeletonHtml(r.leftCol,r.rightCol+1)),o.appendTo(this.rowEls[r.row]),l.push(o[0]);this.highlightEls=t(l)},destroyHighlight:function(){this.highlightEls&&(this.highlightEls.remove(),this.highlightEls=null)},highlightSkeletonHtml:function(t,e){var i=this.view.colCnt,n="";return t>0&&(n+='"),m=[],v=[],y=[];for(r=0;g>r;r++){if(o=f[r],s=0,l=t(""),m.push([]),v.push([]),y.push([]),o)for(a=0;o.length>a;a++){for(c=o[a],n(c.leftCol),d=t('",l+=""+(s?"":n)+'",c.add(this.slotDuration);return l},processOptions:function(){var t=this.view,i=t.opt("slotDuration"),n=t.opt("snapDuration");i=e.duration(i),n=n?e.duration(n):i,this.slotDuration=i,this.snapDuration=n,this.cellDuration=n,this.minTime=e.duration(t.opt("minTime")),this.maxTime=e.duration(t.opt("maxTime"))},rangeToSegs:function(t,e){var i,n,r,o,s,l=this.view,a=[];for(t=t.clone().stripZone(),e=e.clone().stripZone(),n=0;l.colCnt>n;n++)r=l.cellToDate(0,n),o=r.clone().time(this.minTime),s=r.clone().time(this.maxTime),i=b(t,e,o,s),i&&(i.col=n,a.push(i));return a},resize:function(){this.computeSlatTops(),this.updateSegVerticals()},buildCoords:function(i,n){var r,o,s=this.view.colCnt,l=this.el.offset().top,a=e.duration(+this.minTime),c=null;for(this.dayEls.slice(0,s).each(function(e,i){r=t(i),o=r.offset().left,c&&(c[1]=o),c=[o],n[e]=c}),c[1]=o+r.outerWidth(),c=null;this.maxTime>a;)o=l+this.computeTimeTop(a),c&&(c[1]=o),c=[o],i.push(c),a.add(this.snapDuration);c[1]=l+this.computeTimeTop(a)},getCellDate:function(t){var e=this.view,i=e.calendar;return i.rezoneDate(e.cellToDate(0,t.col).time(this.minTime+this.snapDuration*t.row))},getCellDayEl:function(t){return this.dayEls.eq(t.col)},computeDateTop:function(t,i){return this.computeTimeTop(e.duration(t.clone().stripZone()-i.clone().stripTime()))},computeTimeTop:function(t){var e,i,n,r,o=(t-this.minTime)/this.slotDuration;return o=Math.max(0,o),o=Math.min(this.slatEls.length,o),e=Math.floor(o),i=o-e,n=this.slatTops[e],i?(r=this.slatTops[e+1],n+(r-n)*i):n},computeSlatTops:function(){var e,i=[];this.slatEls.each(function(n,r){e=t(r).position().top,i.push(e)}),i.push(e+this.slatEls.last().outerHeight()),this.slatTops=i},renderDrag:function(t,e,i){var n;return i?(this.renderRangeHelper(t,e,i),n=this.view.opt("dragOpacity"),void 0!==n&&this.helperEl.css("opacity",n),!0):(this.renderHighlight(t,e||this.view.calendar.getDefaultEventEnd(!1,t)),void 0)},destroyDrag:function(){this.destroyHelper(),this.destroyHighlight()},renderResize:function(t,e,i){this.renderRangeHelper(t,e,i)},destroyResize:function(){this.destroyHelper()},renderHelper:function(e,i){var n,r,o,s=this.renderEventTable([e]),l=s.tableEl,a=s.segs;for(n=0;a.length>n;n++)r=a[n],i&&i.col===r.col&&(o=i.el,r.el.css({left:o.css("left"),right:o.css("right"),"margin-left":o.css("margin-left"),"margin-right":o.css("margin-right")}));this.helperEl=t('
').append(l).appendTo(this.el)},destroyHelper:function(){this.helperEl&&(this.helperEl.remove(),this.helperEl=null)},renderSelection:function(t,e){this.view.opt("selectHelper")?this.renderRangeHelper(t,e):this.renderHighlight(t,e)},destroySelection:function(){this.destroyHelper(),this.destroyHighlight()},renderHighlight:function(e,i){this.highlightEl=t(this.highlightSkeletonHtml(e,i)).appendTo(this.el)},destroyHighlight:function(){this.highlightEl&&(this.highlightEl.remove(),this.highlightEl=null)},highlightSkeletonHtml:function(t,e){var i,n,r,o,s,l=this.view,a=this.rangeToSegs(t,e),c="",d=0;for(i=0;a.length>i;i++)n=a[i],n.col>d&&(c+='
",d++;return l.colCnt>d&&(c+='":void 0},numberIntroHtml:function(t){return this.weekNumbersVisible?'":void 0},dayIntroHtml:function(){return this.weekNumbersVisible?'":void 0},introHtml:function(){return this.weekNumbersVisible?'":void 0},numberCellHtml:function(t,e,i){var n;return this.dayNumbersVisible?(n=this.dayGrid.getDayClasses(i),n.unshift("fc-day-number"),'"):""):'"},dayIntroHtml:function(){return'"},slotBgIntroHtml:function(){return'"},introHtml:function(){return'"},axisStyleAttr:function(){return null!==this.axisWidth?'style="width:'+this.axisWidth+'px"':""},updateSize:function(t){t&&this.timeGrid.resize(),fe.prototype.updateSize.call(this,t)},updateWidth:function(){this.axisWidth=p(this.el.find(".fc-axis"))},setHeight:function(t,e){var i,n;null===this.bottomRuleHeight&&(this.bottomRuleHeight=this.bottomRuleEl.outerHeight()),this.bottomRuleEl.hide(),this.scrollerEl.css("overflow",""),v(this.scrollerEl),u(this.noScrollRowEls),this.dayGrid&&(this.dayGrid.destroySegPopover(),i=this.opt("eventLimit"),i&&"number"!=typeof i&&(i=Ye),i&&this.dayGrid.limitRows(i)),e||(n=this.computeScrollerHeight(t),m(this.scrollerEl,n)?(h(this.noScrollRowEls,w(this.scrollerEl)),n=this.computeScrollerHeight(t),this.scrollerEl.height(n),this.restoreScroll()):(this.scrollerEl.height(n).css("overflow","hidden"),this.bottomRuleEl.show()))},resetScroll:function(){function t(){i.scrollerEl.scrollTop(r)}var i=this,n=e.duration(this.opt("scrollTime")),r=this.timeGrid.computeTimeTop(n);r=Math.ceil(r),r&&r++,t(),setTimeout(t,0)},renderEvents:function(t){var e,i,n=[],r=[],o=[];for(i=0;t.length>i;i++)t[i].allDay?n.push(t[i]):r.push(t[i]);e=this.timeGrid.renderEvents(r),this.dayGrid&&(o=this.dayGrid.renderEvents(n)),this.updateHeight(),fe.prototype.renderEvents.call(this,t)},getSegs:function(){return this.timeGrid.getSegs().concat(this.dayGrid?this.dayGrid.getSegs():[])},destroyEvents:function(){fe.prototype.destroyEvents.call(this),this.recordScroll(),this.timeGrid.destroyEvents(),this.dayGrid&&this.dayGrid.destroyEvents()},renderDrag:function(t,e,i){return t.hasTime()?this.timeGrid.renderDrag(t,e,i):this.dayGrid?this.dayGrid.renderDrag(t,e,i):void 0},destroyDrag:function(){this.timeGrid.destroyDrag(),this.dayGrid&&this.dayGrid.destroyDrag()},renderSelection:function(t,e){t.hasTime()||e.hasTime()?this.timeGrid.renderSelection(t,e):this.dayGrid&&this.dayGrid.renderSelection(t,e)},destroySelection:function(){this.timeGrid.destroySelection(),this.dayGrid&&this.dayGrid.destroySelection()}}),xe.agendaWeek=be,be.prototype=H(Ee.prototype),t.extend(be.prototype,{name:"agendaWeek",incrementDate:function(t,e){return t.clone().stripTime().add(e,"weeks").startOf("week")},render:function(t){this.intervalStart=t.clone().stripTime().startOf("week"),this.intervalEnd=this.intervalStart.clone().add(1,"weeks"),this.start=this.skipHiddenDays(this.intervalStart),this.end=this.skipHiddenDays(this.intervalEnd,-1,!0),this.title=this.calendar.formatRange(this.start,this.end.clone().subtract(1),this.opt("titleFormat")," — "),Ee.prototype.render.call(this,this.getCellsPerWeek())}}),xe.agendaDay=Se,Se.prototype=H(Ee.prototype),t.extend(Se.prototype,{name:"agendaDay",incrementDate:function(t,e){var i=t.clone().stripTime().add(e,"days");return i=this.skipHiddenDays(i,0>e?-1:1)},render:function(t){this.start=this.intervalStart=t.clone().stripTime(),this.end=this.intervalEnd=this.start.clone().add(1,"days"),this.title=this.calendar.formatDate(this.start,this.opt("titleFormat")),Ee.prototype.render.call(this,1)}})}); \ No newline at end of file diff --git a/dist/fullcalendar.print.css b/dist/fullcalendar.print.css new file mode 100644 index 0000000..c276fe3 --- /dev/null +++ b/dist/fullcalendar.print.css @@ -0,0 +1,201 @@ +/*! + * FullCalendar v2.1.1 Print Stylesheet + * Docs & License: http://arshaw.com/fullcalendar/ + * (c) 2013 Adam Shaw + */ + +/* + * Include this stylesheet on your page to get a more printer-friendly calendar. + * When including this stylesheet, use the media='print' attribute of the tag. + * Make sure to include this stylesheet IN ADDITION to the regular fullcalendar.css. + */ + +.fc { + max-width: 100% !important; +} + + +/* Global Event Restyling +--------------------------------------------------------------------------------------------------*/ + +.fc-event { + background: #fff !important; + color: #000 !important; + page-break-inside: avoid; +} + +.fc-event .fc-resizer { + display: none; +} + + +/* Table & Day-Row Restyling +--------------------------------------------------------------------------------------------------*/ + +th, +td, +hr, +thead, +tbody, +.fc-row { + border-color: #ccc !important; + background: #fff !important; +} + +/* kill the overlaid, absolutely-positioned common components */ +.fc-bg, +.fc-highlight-skeleton, +.fc-helper-skeleton { + display: none; +} + +/* don't force a min-height on rows (for DayGrid) */ +.fc tbody .fc-row { + height: auto !important; /* undo height that JS set in distributeHeight */ + min-height: 0 !important; /* undo the min-height from each view's specific stylesheet */ +} + +.fc tbody .fc-row .fc-content-skeleton { + position: static; /* undo .fc-rigid */ + padding-bottom: 0 !important; /* use a more border-friendly method for this... */ +} + +.fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td { /* only works in newer browsers */ + padding-bottom: 1em; /* ...gives space within the skeleton. also ensures min height in a way */ +} + +.fc tbody .fc-row .fc-content-skeleton table { + /* provides a min-height for the row, but only effective for IE, which exaggerates this value, + making it look more like 3em. for other browers, it will already be this tall */ + height: 1em; +} + + +/* Undo month-view event limiting. Display all events and hide the "more" links +--------------------------------------------------------------------------------------------------*/ + +.fc-more-cell, +.fc-more { + display: none !important; +} + +.fc tr.fc-limited { + display: table-row !important; +} + +.fc td.fc-limited { + display: table-cell !important; +} + +.fc-popover { + display: none; /* never display the "more.." popover in print mode */ +} + + +/* TimeGrid Restyling +--------------------------------------------------------------------------------------------------*/ + +/* undo the min-height 100% trick used to fill the container's height */ +.fc-time-grid { + min-height: 0 !important; +} + +/* don't display the side axis at all ("all-day" and time cells) */ +.fc-agenda-view .fc-axis { + display: none; +} + +/* don't display the horizontal lines */ +.fc-slats, +.fc-time-grid hr { /* this hr is used when height is underused and needs to be filled */ + display: none !important; /* important overrides inline declaration */ +} + +/* let the container that holds the events be naturally positioned and create real height */ +.fc-time-grid .fc-content-skeleton { + position: static; +} + +/* in case there are no events, we still want some height */ +.fc-time-grid .fc-content-skeleton table { + height: 4em; +} + +/* kill the horizontal spacing made by the event container. event margins will be done below */ +.fc-time-grid .fc-event-container { + margin: 0 !important; +} + + +/* TimeGrid *Event* Restyling +--------------------------------------------------------------------------------------------------*/ + +/* naturally position events, vertically stacking them */ +.fc-time-grid .fc-event { + position: static !important; + margin: 3px 2px !important; +} + +/* for events that continue to a future day, give the bottom border back */ +.fc-time-grid .fc-event.fc-not-end { + border-bottom-width: 1px !important; +} + +/* indicate the event continues via "..." text */ +.fc-time-grid .fc-event.fc-not-end:after { + content: "..."; +} + +/* for events that are continuations from previous days, give the top border back */ +.fc-time-grid .fc-event.fc-not-start { + border-top-width: 1px !important; +} + +/* indicate the event is a continuation via "..." text */ +.fc-time-grid .fc-event.fc-not-start:before { + content: "..."; +} + +/* time */ + +/* undo a previous declaration and let the time text span to a second line */ +.fc-time-grid .fc-event .fc-time { + white-space: normal !important; +} + +/* hide the the time that is normally displayed... */ +.fc-time-grid .fc-event .fc-time span { + display: none; +} + +/* ...replace it with a more verbose version (includes AM/PM) stored in an html attribute */ +.fc-time-grid .fc-event .fc-time:after { + content: attr(data-full); +} + + +/* Vertical Scroller & Containers +--------------------------------------------------------------------------------------------------*/ + +/* kill the scrollbars and allow natural height */ +.fc-scroller, +.fc-day-grid-container, /* these divs might be assigned height, which we need to cleared */ +.fc-time-grid-container { /* */ + overflow: visible !important; + height: auto !important; +} + +/* kill the horizontal border/padding used to compensate for scrollbars */ +.fc-row { + border: 0 !important; + margin: 0 !important; +} + + +/* Button Controls +--------------------------------------------------------------------------------------------------*/ + +.fc-button-group, +.fc button { + display: none; /* don't display any button-related controls */ +} diff --git a/dist/gcal.js b/dist/gcal.js new file mode 100644 index 0000000..c7f3b3b --- /dev/null +++ b/dist/gcal.js @@ -0,0 +1,100 @@ +/*! + * FullCalendar v2.1.1 Google Calendar Plugin + * Docs & License: http://arshaw.com/fullcalendar/ + * (c) 2013 Adam Shaw + */ + +(function(factory) { + if (typeof define === 'function' && define.amd) { + define([ 'jquery' ], factory); + } + else { + factory(jQuery); + } +})(function($) { + + +var fc = $.fullCalendar; +var applyAll = fc.applyAll; + + +fc.sourceNormalizers.push(function(sourceOptions) { + if (sourceOptions.dataType == 'gcal' || + sourceOptions.dataType === undefined && + (sourceOptions.url || '').match(/^(http|https):\/\/www.google.com\/calendar\/feeds\//)) { + sourceOptions.dataType = 'gcal'; + if (sourceOptions.editable === undefined) { + sourceOptions.editable = false; + } + } +}); + + +fc.sourceFetchers.push(function(sourceOptions, start, end, timezone) { + if (sourceOptions.dataType == 'gcal') { + return transformOptions(sourceOptions, start, end, timezone); + } +}); + + +function transformOptions(sourceOptions, start, end, timezone) { + + var success = sourceOptions.success; + var data = $.extend({}, sourceOptions.data || {}, { + singleevents: true, + 'max-results': 9999 + }); + + return $.extend({}, sourceOptions, { + url: sourceOptions.url.replace(/\/basic$/, '/full') + '?alt=json-in-script&callback=?', + dataType: 'jsonp', + data: data, + timezoneParam: 'ctz', + startParam: 'start-min', + endParam: 'start-max', + success: function(data) { + var events = []; + if (data.feed.entry) { + $.each(data.feed.entry, function(i, entry) { + + var url; + $.each(entry.link, function(i, link) { + if (link.type == 'text/html') { + url = link.href; + if (timezone && timezone != 'local') { + url += (url.indexOf('?') == -1 ? '?' : '&') + 'ctz=' + encodeURIComponent(timezone); + } + } + }); + + events.push({ + id: entry.gCal$uid.value, + title: entry.title.$t, + start: entry.gd$when[0].startTime, + end: entry.gd$when[0].endTime, + url: url, + location: entry.gd$where[0].valueString, + description: entry.content.$t + }); + + }); + } + var args = [events].concat(Array.prototype.slice.call(arguments, 1)); + var res = applyAll(success, this, args); + if ($.isArray(res)) { + return res; + } + return events; + } + }); + +} + + +// legacy +fc.gcalFeed = function(url, sourceOptions) { + return $.extend({}, sourceOptions, { url: url, dataType: 'gcal' }); +}; + + +}); diff --git a/dist/lang-all.js b/dist/lang-all.js new file mode 100644 index 0000000..c6e94e4 --- /dev/null +++ b/dist/lang-all.js @@ -0,0 +1,3 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(function(){(t.defineLocale||t.lang).call(t,"ar-ma",{months:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),weekdays:"الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:6,doy:12}}),e.fullCalendar.datepickerLang("ar-ma","ar",{closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["كانون الثاني","شباط","آذار","نيسان","مايو","حزيران","تموز","آب","أيلول","تشرين الأول","تشرين الثاني","كانون الأول"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesMin:["ح","ن","ث","ر","خ","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:6,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("ar-ma",{defaultButtonText:{month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},allDayText:"اليوم كله",eventLimitText:"أخرى"})})(),function(){var a={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},n={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"};(t.defineLocale||t.lang).call(t,"ar-sa",{months:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},meridiem:function(e){return 12>e?"ص":"م"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},preparse:function(e){return e.replace(/[۰-۹]/g,function(e){return n[e]}).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]}).replace(/,/g,"،")},week:{dow:6,doy:12}}),e.fullCalendar.datepickerLang("ar-sa","ar",{closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["كانون الثاني","شباط","آذار","نيسان","مايو","حزيران","تموز","آب","أيلول","تشرين الأول","تشرين الثاني","كانون الأول"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesMin:["ح","ن","ث","ر","خ","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:6,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("ar-sa",{defaultButtonText:{month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},allDayText:"اليوم كله",eventLimitText:"أخرى"})}(),function(){var a={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},n={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"};(t.defineLocale||t.lang).call(t,"ar",{months:"يناير/ كانون الثاني_فبراير/ شباط_مارس/ آذار_أبريل/ نيسان_مايو/ أيار_يونيو/ حزيران_يوليو/ تموز_أغسطس/ آب_سبتمبر/ أيلول_أكتوبر/ تشرين الأول_نوفمبر/ تشرين الثاني_ديسمبر/ كانون الأول".split("_"),monthsShort:"يناير/ كانون الثاني_فبراير/ شباط_مارس/ آذار_أبريل/ نيسان_مايو/ أيار_يونيو/ حزيران_يوليو/ تموز_أغسطس/ آب_سبتمبر/ أيلول_أكتوبر/ تشرين الأول_نوفمبر/ تشرين الثاني_ديسمبر/ كانون الأول".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},meridiem:function(e){return 12>e?"ص":"م"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},preparse:function(e){return e.replace(/[۰-۹]/g,function(e){return n[e]}).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]}).replace(/,/g,"،")},week:{dow:6,doy:12}}),e.fullCalendar.datepickerLang("ar","ar",{closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["كانون الثاني","شباط","آذار","نيسان","مايو","حزيران","تموز","آب","أيلول","تشرين الأول","تشرين الثاني","كانون الأول"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesMin:["ح","ن","ث","ر","خ","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:6,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("ar",{defaultButtonText:{month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},allDayText:"اليوم كله",eventLimitText:"أخرى"})}(),function(){(t.defineLocale||t.lang).call(t,"bg",{months:"януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември".split("_"),monthsShort:"янр_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек".split("_"),weekdays:"неделя_понеделник_вторник_сряда_четвъртък_петък_събота".split("_"),weekdaysShort:"нед_пон_вто_сря_чет_пет_съб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"H:mm",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Днес в] LT",nextDay:"[Утре в] LT",nextWeek:"dddd [в] LT",lastDay:"[Вчера в] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[В изминалата] dddd [в] LT";case 1:case 2:case 4:case 5:return"[В изминалия] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"след %s",past:"преди %s",s:"няколко секунди",m:"минута",mm:"%d минути",h:"час",hh:"%d часа",d:"ден",dd:"%d дни",M:"месец",MM:"%d месеца",y:"година",yy:"%d години"},ordinal:function(e){var t=e%10,a=e%100;return 0===e?e+"-ев":0===a?e+"-ен":a>10&&20>a?e+"-ти":1===t?e+"-ви":2===t?e+"-ри":7===t||8===t?e+"-ми":e+"-ти"},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("bg","bg",{closeText:"затвори",prevText:"<назад",nextText:"напред>",nextBigText:">>",currentText:"днес",monthNames:["Януари","Февруари","Март","Април","Май","Юни","Юли","Август","Септември","Октомври","Ноември","Декември"],monthNamesShort:["Яну","Фев","Мар","Апр","Май","Юни","Юли","Авг","Сеп","Окт","Нов","Дек"],dayNames:["Неделя","Понеделник","Вторник","Сряда","Четвъртък","Петък","Събота"],dayNamesShort:["Нед","Пон","Вто","Сря","Чет","Пет","Съб"],dayNamesMin:["Не","По","Вт","Ср","Че","Пе","Съ"],weekHeader:"Wk",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("bg",{defaultButtonText:{month:"Месец",week:"Седмица",day:"Ден",list:"График"},allDayText:"Цял ден",eventLimitText:function(e){return"+още "+e}})}(),function(){(t.defineLocale||t.lang).call(t,"ca",{months:"gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre".split("_"),monthsShort:"gen._febr._mar._abr._mai._jun._jul._ag._set._oct._nov._des.".split("_"),weekdays:"diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dt._dc._dj._dv._ds.".split("_"),weekdaysMin:"Dg_Dl_Dt_Dc_Dj_Dv_Ds".split("_"),longDateFormat:{LT:"H:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:function(){return"[avui a "+(1!==this.hours()?"les":"la")+"] LT"},nextDay:function(){return"[demà a "+(1!==this.hours()?"les":"la")+"] LT"},nextWeek:function(){return"dddd [a "+(1!==this.hours()?"les":"la")+"] LT"},lastDay:function(){return"[ahir a "+(1!==this.hours()?"les":"la")+"] LT"},lastWeek:function(){return"[el] dddd [passat a "+(1!==this.hours()?"les":"la")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"fa %s",s:"uns segons",m:"un minut",mm:"%d minuts",h:"una hora",hh:"%d hores",d:"un dia",dd:"%d dies",M:"un mes",MM:"%d mesos",y:"un any",yy:"%d anys"},ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("ca","ca",{closeText:"Tanca",prevText:"Anterior",nextText:"Següent",currentText:"Avui",monthNames:["gener","febrer","març","abril","maig","juny","juliol","agost","setembre","octubre","novembre","desembre"],monthNamesShort:["gen","feb","març","abr","maig","juny","jul","ag","set","oct","nov","des"],dayNames:["diumenge","dilluns","dimarts","dimecres","dijous","divendres","dissabte"],dayNamesShort:["dg","dl","dt","dc","dj","dv","ds"],dayNamesMin:["dg","dl","dt","dc","dj","dv","ds"],weekHeader:"Set",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("ca",{defaultButtonText:{month:"Mes",week:"Setmana",day:"Dia",list:"Agenda"},allDayText:"Tot el dia",eventLimitText:"més"})}(),function(){function a(e){return e>1&&5>e&&1!==~~(e/10)}function n(e,t,n,r){var i=e+" ";switch(n){case"s":return t||r?"pár sekund":"pár sekundami";case"m":return t?"minuta":r?"minutu":"minutou";case"mm":return t||r?i+(a(e)?"minuty":"minut"):i+"minutami";case"h":return t?"hodina":r?"hodinu":"hodinou";case"hh":return t||r?i+(a(e)?"hodiny":"hodin"):i+"hodinami";case"d":return t||r?"den":"dnem";case"dd":return t||r?i+(a(e)?"dny":"dní"):i+"dny";case"M":return t||r?"měsíc":"měsícem";case"MM":return t||r?i+(a(e)?"měsíce":"měsíců"):i+"měsíci";case"y":return t||r?"rok":"rokem";case"yy":return t||r?i+(a(e)?"roky":"let"):i+"lety"}}var r="leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec".split("_"),i="led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro".split("_");(t.defineLocale||t.lang).call(t,"cs",{months:r,monthsShort:i,monthsParse:function(e,t){var a,n=[];for(a=0;12>a;a++)n[a]=RegExp("^"+e[a]+"$|^"+t[a]+"$","i");return n}(r,i),weekdays:"neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota".split("_"),weekdaysShort:"ne_po_út_st_čt_pá_so".split("_"),weekdaysMin:"ne_po_út_st_čt_pá_so".split("_"),longDateFormat:{LT:"H.mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd D. MMMM YYYY LT"},calendar:{sameDay:"[dnes v] LT",nextDay:"[zítra v] LT",nextWeek:function(){switch(this.day()){case 0:return"[v neděli v] LT";case 1:case 2:return"[v] dddd [v] LT";case 3:return"[ve středu v] LT";case 4:return"[ve čtvrtek v] LT";case 5:return"[v pátek v] LT";case 6:return"[v sobotu v] LT"}},lastDay:"[včera v] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulou neděli v] LT";case 1:case 2:return"[minulé] dddd [v] LT";case 3:return"[minulou středu v] LT";case 4:case 5:return"[minulý] dddd [v] LT";case 6:return"[minulou sobotu v] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"před %s",s:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("cs","cs",{closeText:"Zavřít",prevText:"<Dříve",nextText:"Později>",currentText:"Nyní",monthNames:["leden","únor","březen","duben","květen","červen","červenec","srpen","září","říjen","listopad","prosinec"],monthNamesShort:["led","úno","bře","dub","kvě","čer","čvc","srp","zář","říj","lis","pro"],dayNames:["neděle","pondělí","úterý","středa","čtvrtek","pátek","sobota"],dayNamesShort:["ne","po","út","st","čt","pá","so"],dayNamesMin:["ne","po","út","st","čt","pá","so"],weekHeader:"Týd",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("cs",{defaultButtonText:{month:"Měsíc",week:"Týden",day:"Den",list:"Agenda"},allDayText:"Celý den",eventLimitText:function(e){return"+další: "+e}})}(),function(){(t.defineLocale||t.lang).call(t,"da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tir_ons_tor_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd [d.] D. MMMM YYYY LT"},calendar:{sameDay:"[I dag kl.] LT",nextDay:"[I morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[I går kl.] LT",lastWeek:"[sidste] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"få sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en måned",MM:"%d måneder",y:"et år",yy:"%d år"},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("da","da",{closeText:"Luk",prevText:"<Forrige",nextText:"Næste>",currentText:"Idag",monthNames:["Januar","Februar","Marts","April","Maj","Juni","Juli","August","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNames:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag"],dayNamesShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],dayNamesMin:["Sø","Ma","Ti","On","To","Fr","Lø"],weekHeader:"Uge",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("da",{defaultButtonText:{month:"Måned",week:"Uge",day:"Dag",list:"Agenda"},allDayText:"Hele dagen",eventLimitText:"flere"})}(),function(){function a(e,t,a){var n={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return t?n[a][0]:n[a][1]}(t.defineLocale||t.lang).call(t,"de-at",{months:"Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jän._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm [Uhr]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Heute um] LT",sameElse:"L",nextDay:"[Morgen um] LT",nextWeek:"dddd [um] LT",lastDay:"[Gestern um] LT",lastWeek:"[letzten] dddd [um] LT"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:a,mm:"%d Minuten",h:a,hh:"%d Stunden",d:a,dd:a,M:a,MM:a,y:a,yy:a},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("de-at","de",{closeText:"Schließen",prevText:"<Zurück",nextText:"Vor>",currentText:"Heute",monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayNamesMin:["So","Mo","Di","Mi","Do","Fr","Sa"],weekHeader:"KW",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("de-at",{defaultButtonText:{month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},allDayText:"Ganztägig",eventLimitText:function(e){return"+ weitere "+e}})}(),function(){function a(e,t,a){var n={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return t?n[a][0]:n[a][1]}(t.defineLocale||t.lang).call(t,"de",{months:"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm [Uhr]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Heute um] LT",sameElse:"L",nextDay:"[Morgen um] LT",nextWeek:"dddd [um] LT",lastDay:"[Gestern um] LT",lastWeek:"[letzten] dddd [um] LT"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:a,mm:"%d Minuten",h:a,hh:"%d Stunden",d:a,dd:a,M:a,MM:a,y:a,yy:a},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("de","de",{closeText:"Schließen",prevText:"<Zurück",nextText:"Vor>",currentText:"Heute",monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayNamesMin:["So","Mo","Di","Mi","Do","Fr","Sa"],weekHeader:"KW",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("de",{defaultButtonText:{month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},allDayText:"Ganztägig",eventLimitText:function(e){return"+ weitere "+e}})}(),function(){(t.defineLocale||t.lang).call(t,"el",{monthsNominativeEl:"Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος".split("_"),monthsGenitiveEl:"Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου".split("_"),months:function(e,t){return/D/.test(t.substring(0,t.indexOf("MMMM")))?this._monthsGenitiveEl[e.month()]:this._monthsNominativeEl[e.month()]},monthsShort:"Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ".split("_"),weekdays:"Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο".split("_"),weekdaysShort:"Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ".split("_"),weekdaysMin:"Κυ_Δε_Τρ_Τε_Πε_Πα_Σα".split("_"),meridiem:function(e,t,a){return e>11?a?"μμ":"ΜΜ":a?"πμ":"ΠΜ"},longDateFormat:{LT:"h:mm A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendarEl:{sameDay:"[Σήμερα {}] LT",nextDay:"[Αύριο {}] LT",nextWeek:"dddd [{}] LT",lastDay:"[Χθες {}] LT",lastWeek:function(){switch(this.day()){case 6:return"[το προηγούμενο] dddd [{}] LT";default:return"[την προηγούμενη] dddd [{}] LT"}},sameElse:"L"},calendar:function(e,t){var a=this._calendarEl[e],n=t&&t.hours();return"function"==typeof a&&(a=a.apply(t)),a.replace("{}",1===n%12?"στη":"στις")},relativeTime:{future:"σε %s",past:"%s πριν",s:"δευτερόλεπτα",m:"ένα λεπτό",mm:"%d λεπτά",h:"μία ώρα",hh:"%d ώρες",d:"μία μέρα",dd:"%d μέρες",M:"ένας μήνας",MM:"%d μήνες",y:"ένας χρόνος",yy:"%d χρόνια"},ordinal:function(e){return e+"η"},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("el","el",{closeText:"Κλείσιμο",prevText:"Προηγούμενος",nextText:"Επόμενος",currentText:"Τρέχων Μήνας",monthNames:["Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος","Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος"],monthNamesShort:["Ιαν","Φεβ","Μαρ","Απρ","Μαι","Ιουν","Ιουλ","Αυγ","Σεπ","Οκτ","Νοε","Δεκ"],dayNames:["Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο"],dayNamesShort:["Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ"],dayNamesMin:["Κυ","Δε","Τρ","Τε","Πε","Πα","Σα"],weekHeader:"Εβδ",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("el",{defaultButtonText:{month:"Μήνας",week:"Εβδομάδα",day:"Ημέρα",list:"Ατζέντα"},allDayText:"Ολοήμερο",eventLimitText:"περισσότερα"})}(),function(){(t.defineLocale||t.lang).call(t,"en-au",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinal:function(e){var t=e%10,a=1===~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+a},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("en-au","en-AU",{closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",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"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("en-au")}(),function(){(t.defineLocale||t.lang).call(t,"en-ca",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",L:"YYYY-MM-DD",LL:"D MMMM, YYYY",LLL:"D MMMM, YYYY LT",LLLL:"dddd, D MMMM, YYYY LT"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinal:function(e){var t=e%10,a=1===~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+a}}),e.fullCalendar.lang("en-ca")}(),function(){(t.defineLocale||t.lang).call(t,"en-gb",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinal:function(e){var t=e%10,a=1===~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+a},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("en-gb","en-GB",{closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",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"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("en-gb",{columnFormat:{week:"ddd D/M"}})}(),function(){var a="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),n="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_");(t.defineLocale||t.lang).call(t,"es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){return/-MMM-/.test(t)?n[e.month()]:a[e.month()]},weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"Do_Lu_Ma_Mi_Ju_Vi_Sá".split("_"),longDateFormat:{LT:"H:mm",L:"DD/MM/YYYY",LL:"D [de] MMMM [del] YYYY",LLL:"D [de] MMMM [del] YYYY LT",LLLL:"dddd, D [de] MMMM [del] YYYY LT"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("es","es",{closeText:"Cerrar",prevText:"<Ant",nextText:"Sig>",currentText:"Hoy",monthNames:["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],monthNamesShort:["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"],dayNames:["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],dayNamesShort:["dom","lun","mar","mié","jue","vie","sáb"],dayNamesMin:["D","L","M","X","J","V","S"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("es",{defaultButtonText:{month:"Mes",week:"Semana",day:"Día",list:"Agenda"},allDayHtml:"Todo
el día",eventLimitText:"más"})}(),function(){var a={1:"۱",2:"۲",3:"۳",4:"۴",5:"۵",6:"۶",7:"۷",8:"۸",9:"۹",0:"۰"},n={"۱":"1","۲":"2","۳":"3","۴":"4","۵":"5","۶":"6","۷":"7","۸":"8","۹":"9","۰":"0"};(t.defineLocale||t.lang).call(t,"fa",{months:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),monthsShort:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),weekdays:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysShort:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysMin:"ی_د_س_چ_پ_ج_ش".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},meridiem:function(e){return 12>e?"قبل از ظهر":"بعد از ظهر"},calendar:{sameDay:"[امروز ساعت] LT",nextDay:"[فردا ساعت] LT",nextWeek:"dddd [ساعت] LT",lastDay:"[دیروز ساعت] LT",lastWeek:"dddd [پیش] [ساعت] LT",sameElse:"L"},relativeTime:{future:"در %s",past:"%s پیش",s:"چندین ثانیه",m:"یک دقیقه",mm:"%d دقیقه",h:"یک ساعت",hh:"%d ساعت",d:"یک روز",dd:"%d روز",M:"یک ماه",MM:"%d ماه",y:"یک سال",yy:"%d سال"},preparse:function(e){return e.replace(/[۰-۹]/g,function(e){return n[e]}).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]}).replace(/,/g,"،")},ordinal:"%dم",week:{dow:6,doy:12}}),e.fullCalendar.datepickerLang("fa","fa",{closeText:"بستن",prevText:"<قبلی",nextText:"بعدی>",currentText:"امروز",monthNames:["فروردين","ارديبهشت","خرداد","تير","مرداد","شهريور","مهر","آبان","آذر","دی","بهمن","اسفند"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["يکشنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"],dayNamesShort:["ی","د","س","چ","پ","ج","ش"],dayNamesMin:["ی","د","س","چ","پ","ج","ش"],weekHeader:"هف",dateFormat:"yy/mm/dd",firstDay:6,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fa",{defaultButtonText:{month:"ماه",week:"هفته",day:"روز",list:"برنامه"},allDayText:"تمام روز",eventLimitText:function(e){return"بیش از "+e}})}(),function(){function a(e,t,a,r){var i="";switch(a){case"s":return r?"muutaman sekunnin":"muutama sekunti";case"m":return r?"minuutin":"minuutti";case"mm":i=r?"minuutin":"minuuttia";break;case"h":return r?"tunnin":"tunti";case"hh":i=r?"tunnin":"tuntia";break;case"d":return r?"päivän":"päivä";case"dd":i=r?"päivän":"päivää";break;case"M":return r?"kuukauden":"kuukausi";case"MM":i=r?"kuukauden":"kuukautta";break;case"y":return r?"vuoden":"vuosi";case"yy":i=r?"vuoden":"vuotta"}return i=n(e,r)+" "+i}function n(e,t){return 10>e?t?i[e]:r[e]:e}var r="nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän".split(" "),i=["nolla","yhden","kahden","kolmen","neljän","viiden","kuuden",r[7],r[8],r[9]];(t.defineLocale||t.lang).call(t,"fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] LT",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] LT",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] LT",llll:"ddd, Do MMM YYYY, [klo] LT"},calendar:{sameDay:"[tänään] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s päästä",past:"%s sitten",s:a,m:a,mm:a,h:a,hh:a,d:a,dd:a,M:a,MM:a,y:a,yy:a},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("fi","fi",{closeText:"Sulje",prevText:"«Edellinen",nextText:"Seuraava»",currentText:"Tänään",monthNames:["Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu","Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu"],monthNamesShort:["Tammi","Helmi","Maalis","Huhti","Touko","Kesä","Heinä","Elo","Syys","Loka","Marras","Joulu"],dayNamesShort:["Su","Ma","Ti","Ke","To","Pe","La"],dayNames:["Sunnuntai","Maanantai","Tiistai","Keskiviikko","Torstai","Perjantai","Lauantai"],dayNamesMin:["Su","Ma","Ti","Ke","To","Pe","La"],weekHeader:"Vk",dateFormat:"d.m.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fi",{defaultButtonText:{month:"Kuukausi",week:"Viikko",day:"Päivä",list:"Tapahtumat"},allDayText:"Koko päivä",eventLimitText:"lisää"})}(),function(){(t.defineLocale||t.lang).call(t,"fr-ca",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinal:function(e){return e+(1===e?"er":"") +}}),e.fullCalendar.datepickerLang("fr-ca","fr-CA",{closeText:"Fermer",prevText:"Précédent",nextText:"Suivant",currentText:"Aujourd'hui",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sem.",dateFormat:"yy-mm-dd",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fr-ca",{defaultButtonText:{month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},allDayHtml:"Toute la
journée",eventLimitText:"en plus"})}(),function(){(t.defineLocale||t.lang).call(t,"fr",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinal:function(e){return e+(1===e?"er":"")},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("fr","fr",{closeText:"Fermer",prevText:"Précédent",nextText:"Suivant",currentText:"Aujourd'hui",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sem.",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fr",{defaultButtonText:{month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},allDayHtml:"Toute la
journée",eventLimitText:"en plus"})}(),function(){var a={1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"},n={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"};(t.defineLocale||t.lang).call(t,"hi",{months:"जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर".split("_"),monthsShort:"जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.".split("_"),weekdays:"रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि".split("_"),weekdaysMin:"र_सो_मं_बु_गु_शु_श".split("_"),longDateFormat:{LT:"A h:mm बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},calendar:{sameDay:"[आज] LT",nextDay:"[कल] LT",nextWeek:"dddd, LT",lastDay:"[कल] LT",lastWeek:"[पिछले] dddd, LT",sameElse:"L"},relativeTime:{future:"%s में",past:"%s पहले",s:"कुछ ही क्षण",m:"एक मिनट",mm:"%d मिनट",h:"एक घंटा",hh:"%d घंटे",d:"एक दिन",dd:"%d दिन",M:"एक महीने",MM:"%d महीने",y:"एक वर्ष",yy:"%d वर्ष"},preparse:function(e){return e.replace(/[१२३४५६७८९०]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return a[e]})},meridiem:function(e){return 4>e?"रात":10>e?"सुबह":17>e?"दोपहर":20>e?"शाम":"रात"},week:{dow:0,doy:6}}),e.fullCalendar.datepickerLang("hi","hi",{closeText:"बंद",prevText:"पिछला",nextText:"अगला",currentText:"आज",monthNames:["जनवरी ","फरवरी","मार्च","अप्रेल","मई","जून","जूलाई","अगस्त ","सितम्बर","अक्टूबर","नवम्बर","दिसम्बर"],monthNamesShort:["जन","फर","मार्च","अप्रेल","मई","जून","जूलाई","अग","सित","अक्ट","नव","दि"],dayNames:["रविवार","सोमवार","मंगलवार","बुधवार","गुरुवार","शुक्रवार","शनिवार"],dayNamesShort:["रवि","सोम","मंगल","बुध","गुरु","शुक्र","शनि"],dayNamesMin:["रवि","सोम","मंगल","बुध","गुरु","शुक्र","शनि"],weekHeader:"हफ्ता",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("hi",{defaultButtonText:{month:"महीना",week:"सप्ताह",day:"दिन",list:"कार्यसूची"},allDayText:"सभी दिन",eventLimitText:function(e){return"+अधिक "+e}})}(),function(){function a(e,t,a){var n=e+" ";switch(a){case"m":return t?"jedna minuta":"jedne minute";case"mm":return n+=1===e?"minuta":2===e||3===e||4===e?"minute":"minuta";case"h":return t?"jedan sat":"jednog sata";case"hh":return n+=1===e?"sat":2===e||3===e||4===e?"sata":"sati";case"dd":return n+=1===e?"dan":"dana";case"MM":return n+=1===e?"mjesec":2===e||3===e||4===e?"mjeseca":"mjeseci";case"yy":return n+=1===e?"godina":2===e||3===e||4===e?"godine":"godina"}}(t.defineLocale||t.lang).call(t,"hr",{months:"sječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac".split("_"),monthsShort:"sje._vel._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.".split("_"),weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),longDateFormat:{LT:"H:mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[prošlu] dddd [u] LT";case 6:return"[prošle] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[prošli] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",m:a,mm:a,h:a,hh:a,d:"dan",dd:a,M:"mjesec",MM:a,y:"godinu",yy:a},ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("hr","hr",{closeText:"Zatvori",prevText:"<",nextText:">",currentText:"Danas",monthNames:["Siječanj","Veljača","Ožujak","Travanj","Svibanj","Lipanj","Srpanj","Kolovoz","Rujan","Listopad","Studeni","Prosinac"],monthNamesShort:["Sij","Velj","Ožu","Tra","Svi","Lip","Srp","Kol","Ruj","Lis","Stu","Pro"],dayNames:["Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota"],dayNamesShort:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"],dayNamesMin:["Ne","Po","Ut","Sr","Če","Pe","Su"],weekHeader:"Tje",dateFormat:"dd.mm.yy.",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("hr",{defaultButtonText:{month:"Mjesec",week:"Tjedan",day:"Dan",list:"Raspored"},allDayText:"Cijeli dan",eventLimitText:function(e){return"+ još "+e}})}(),function(){function a(e,t,a,n){var r=e;switch(a){case"s":return n||t?"néhány másodperc":"néhány másodperce";case"m":return"egy"+(n||t?" perc":" perce");case"mm":return r+(n||t?" perc":" perce");case"h":return"egy"+(n||t?" óra":" órája");case"hh":return r+(n||t?" óra":" órája");case"d":return"egy"+(n||t?" nap":" napja");case"dd":return r+(n||t?" nap":" napja");case"M":return"egy"+(n||t?" hónap":" hónapja");case"MM":return r+(n||t?" hónap":" hónapja");case"y":return"egy"+(n||t?" év":" éve");case"yy":return r+(n||t?" év":" éve")}return""}function n(e){return(e?"":"[múlt] ")+"["+r[this.day()]+"] LT[-kor]"}var r="vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton".split(" ");(t.defineLocale||t.lang).call(t,"hu",{months:"január_február_március_április_május_június_július_augusztus_szeptember_október_november_december".split("_"),monthsShort:"jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec".split("_"),weekdays:"vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat".split("_"),weekdaysShort:"vas_hét_kedd_sze_csüt_pén_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D., LT",LLLL:"YYYY. MMMM D., dddd LT"},meridiem:function(e,t,a){return 12>e?a===!0?"de":"DE":a===!0?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return n.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return n.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s múlva",past:"%s",s:a,m:a,mm:a,h:a,hh:a,d:a,dd:a,M:a,MM:a,y:a,yy:a},ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("hu","hu",{closeText:"bezár",prevText:"vissza",nextText:"előre",currentText:"ma",monthNames:["Január","Február","Március","Április","Május","Június","Július","Augusztus","Szeptember","Október","November","December"],monthNamesShort:["Jan","Feb","Már","Ápr","Máj","Jún","Júl","Aug","Szep","Okt","Nov","Dec"],dayNames:["Vasárnap","Hétfő","Kedd","Szerda","Csütörtök","Péntek","Szombat"],dayNamesShort:["Vas","Hét","Ked","Sze","Csü","Pén","Szo"],dayNamesMin:["V","H","K","Sze","Cs","P","Szo"],weekHeader:"Hét",dateFormat:"yy.mm.dd.",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:""}),e.fullCalendar.lang("hu",{defaultButtonText:{month:"Hónap",week:"Hét",day:"Nap",list:"Napló"},allDayText:"Egész nap",eventLimitText:"további"})}(),function(){(t.defineLocale||t.lang).call(t,"id",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nov_Des".split("_"),weekdays:"Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),weekdaysShort:"Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] LT",LLLL:"dddd, D MMMM YYYY [pukul] LT"},meridiem:function(e){return 11>e?"pagi":15>e?"siang":19>e?"sore":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Besok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kemarin pukul] LT",lastWeek:"dddd [lalu pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lalu",s:"beberapa detik",m:"semenit",mm:"%d menit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("id","id",{closeText:"Tutup",prevText:"<mundur",nextText:"maju>",currentText:"hari ini",monthNames:["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","Nopember","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agus","Sep","Okt","Nop","Des"],dayNames:["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"],dayNamesShort:["Min","Sen","Sel","Rab","kam","Jum","Sab"],dayNamesMin:["Mg","Sn","Sl","Rb","Km","jm","Sb"],weekHeader:"Mg",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("id",{defaultButtonText:{month:"Bulan",week:"Minggu",day:"Hari",list:"Agenda"},allDayHtml:"Sehari
penuh",eventLimitText:"lebih"})}(),function(){function a(e){return 11===e%100?!0:1===e%10?!1:!0}function n(e,t,n,r){var i=e+" ";switch(n){case"s":return t||r?"nokkrar sekúndur":"nokkrum sekúndum";case"m":return t?"mínúta":"mínútu";case"mm":return a(e)?i+(t||r?"mínútur":"mínútum"):t?i+"mínúta":i+"mínútu";case"hh":return a(e)?i+(t||r?"klukkustundir":"klukkustundum"):i+"klukkustund";case"d":return t?"dagur":r?"dag":"degi";case"dd":return a(e)?t?i+"dagar":i+(r?"daga":"dögum"):t?i+"dagur":i+(r?"dag":"degi");case"M":return t?"mánuður":r?"mánuð":"mánuði";case"MM":return a(e)?t?i+"mánuðir":i+(r?"mánuði":"mánuðum"):t?i+"mánuður":i+(r?"mánuð":"mánuði");case"y":return t||r?"ár":"ári";case"yy":return a(e)?i+(t||r?"ár":"árum"):i+(t||r?"ár":"ári")}}(t.defineLocale||t.lang).call(t,"is",{months:"janúar_febrúar_mars_apríl_maí_júní_júlí_ágúst_september_október_nóvember_desember".split("_"),monthsShort:"jan_feb_mar_apr_maí_jún_júl_ágú_sep_okt_nóv_des".split("_"),weekdays:"sunnudagur_mánudagur_þriðjudagur_miðvikudagur_fimmtudagur_föstudagur_laugardagur".split("_"),weekdaysShort:"sun_mán_þri_mið_fim_fös_lau".split("_"),weekdaysMin:"Su_Má_Þr_Mi_Fi_Fö_La".split("_"),longDateFormat:{LT:"H:mm",L:"DD/MM/YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] LT",LLLL:"dddd, D. MMMM YYYY [kl.] LT"},calendar:{sameDay:"[í dag kl.] LT",nextDay:"[á morgun kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[í gær kl.] LT",lastWeek:"[síðasta] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"eftir %s",past:"fyrir %s síðan",s:n,m:n,mm:n,h:"klukkustund",hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("is","is",{closeText:"Loka",prevText:"< Fyrri",nextText:"Næsti >",currentText:"Í dag",monthNames:["Janúar","Febrúar","Mars","Apríl","Maí","Júní","Júlí","Ágúst","September","Október","Nóvember","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","Maí","Jún","Júl","Ágú","Sep","Okt","Nóv","Des"],dayNames:["Sunnudagur","Mánudagur","Þriðjudagur","Miðvikudagur","Fimmtudagur","Föstudagur","Laugardagur"],dayNamesShort:["Sun","Mán","Þri","Mið","Fim","Fös","Lau"],dayNamesMin:["Su","Má","Þr","Mi","Fi","Fö","La"],weekHeader:"Vika",dateFormat:"dd.mm.yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("is",{defaultButtonText:{month:"Mánuður",week:"Vika",day:"Dagur",list:"Dagskrá"},allDayHtml:"Allan
daginn",eventLimitText:"meira"})}(),function(){(t.defineLocale||t.lang).call(t,"it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato".split("_"),weekdaysShort:"Dom_Lun_Mar_Mer_Gio_Ven_Sab".split("_"),weekdaysMin:"D_L_Ma_Me_G_V_S".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:"[lo scorso] dddd [alle] LT",sameElse:"L"},relativeTime:{future:function(e){return(/^[0-9].+$/.test(e)?"tra":"in")+" "+e},past:"%s fa",s:"alcuni secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("it","it",{closeText:"Chiudi",prevText:"<Prec",nextText:"Succ>",currentText:"Oggi",monthNames:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],monthNamesShort:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],dayNames:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],dayNamesShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],dayNamesMin:["Do","Lu","Ma","Me","Gi","Ve","Sa"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("it",{defaultButtonText:{month:"Mese",week:"Settimana",day:"Giorno",list:"Agenda"},allDayHtml:"Tutto il
giorno",eventLimitText:function(e){return"+altri "+e}})}(),function(){(t.defineLocale||t.lang).call(t,"ja",{months:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"),weekdaysShort:"日_月_火_水_木_金_土".split("_"),weekdaysMin:"日_月_火_水_木_金_土".split("_"),longDateFormat:{LT:"Ah時m分",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日LT",LLLL:"YYYY年M月D日LT dddd"},meridiem:function(e){return 12>e?"午前":"午後"},calendar:{sameDay:"[今日] LT",nextDay:"[明日] LT",nextWeek:"[来週]dddd LT",lastDay:"[昨日] LT",lastWeek:"[前週]dddd LT",sameElse:"L"},relativeTime:{future:"%s後",past:"%s前",s:"数秒",m:"1分",mm:"%d分",h:"1時間",hh:"%d時間",d:"1日",dd:"%d日",M:"1ヶ月",MM:"%dヶ月",y:"1年",yy:"%d年"}}),e.fullCalendar.datepickerLang("ja","ja",{closeText:"閉じる",prevText:"<前",nextText:"次>",currentText:"今日",monthNames:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],monthNamesShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayNames:["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"],dayNamesShort:["日","月","火","水","木","金","土"],dayNamesMin:["日","月","火","水","木","金","土"],weekHeader:"週",dateFormat:"yy/mm/dd",firstDay:0,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"年"}),e.fullCalendar.lang("ja",{defaultButtonText:{month:"月",week:"週",day:"日",list:"予定リスト"},allDayText:"終日",eventLimitText:function(e){return"他 "+e+" 件"}})}(),function(){(t.defineLocale||t.lang).call(t,"ko",{months:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),monthsShort:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),weekdays:"일요일_월요일_화요일_수요일_목요일_금요일_토요일".split("_"),weekdaysShort:"일_월_화_수_목_금_토".split("_"),weekdaysMin:"일_월_화_수_목_금_토".split("_"),longDateFormat:{LT:"A h시 mm분",L:"YYYY.MM.DD",LL:"YYYY년 MMMM D일",LLL:"YYYY년 MMMM D일 LT",LLLL:"YYYY년 MMMM D일 dddd LT"},meridiem:function(e){return 12>e?"오전":"오후"},calendar:{sameDay:"오늘 LT",nextDay:"내일 LT",nextWeek:"dddd LT",lastDay:"어제 LT",lastWeek:"지난주 dddd LT",sameElse:"L"},relativeTime:{future:"%s 후",past:"%s 전",s:"몇초",ss:"%d초",m:"일분",mm:"%d분",h:"한시간",hh:"%d시간",d:"하루",dd:"%d일",M:"한달",MM:"%d달",y:"일년",yy:"%d년"},ordinal:"%d일",meridiemParse:/(오전|오후)/,isPM:function(e){return"오후"===e}}),e.fullCalendar.datepickerLang("ko","ko",{closeText:"닫기",prevText:"이전달",nextText:"다음달",currentText:"오늘",monthNames:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],monthNamesShort:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],dayNames:["일요일","월요일","화요일","수요일","목요일","금요일","토요일"],dayNamesShort:["일","월","화","수","목","금","토"],dayNamesMin:["일","월","화","수","목","금","토"],weekHeader:"Wk",dateFormat:"yy-mm-dd",firstDay:0,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"년"}),e.fullCalendar.lang("ko",{defaultButtonText:{month:"월",week:"주",day:"일",list:"일정목록"},allDayText:"종일",eventLimitText:"개"})}(),function(){function a(e,t,a,n){return t?"kelios sekundės":n?"kelių sekundžių":"kelias sekundes"}function n(e,t,a,n){return t?i(a)[0]:n?i(a)[1]:i(a)[2]}function r(e){return 0===e%10||e>10&&20>e}function i(e){return d[e].split("_")}function s(e,t,a,s){var o=e+" ";return 1===e?o+n(e,t,a[0],s):t?o+(r(e)?i(a)[1]:i(a)[0]):s?o+i(a)[1]:o+(r(e)?i(a)[1]:i(a)[2])}function o(e,t){var a=-1===t.indexOf("dddd HH:mm"),n=l[e.day()];return a?n:n.substring(0,n.length-2)+"į"}var d={m:"minutė_minutės_minutę",mm:"minutės_minučių_minutes",h:"valanda_valandos_valandą",hh:"valandos_valandų_valandas",d:"diena_dienos_dieną",dd:"dienos_dienų_dienas",M:"mėnuo_mėnesio_mėnesį",MM:"mėnesiai_mėnesių_mėnesius",y:"metai_metų_metus",yy:"metai_metų_metus"},l="sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_šeštadienis".split("_");(t.defineLocale||t.lang).call(t,"lt",{months:"sausio_vasario_kovo_balandžio_gegužės_birželio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio".split("_"),monthsShort:"sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd".split("_"),weekdays:o,weekdaysShort:"Sek_Pir_Ant_Tre_Ket_Pen_Šeš".split("_"),weekdaysMin:"S_P_A_T_K_Pn_Š".split("_"),longDateFormat:{LT:"HH:mm",L:"YYYY-MM-DD",LL:"YYYY [m.] MMMM D [d.]",LLL:"YYYY [m.] MMMM D [d.], LT [val.]",LLLL:"YYYY [m.] MMMM D [d.], dddd, LT [val.]",l:"YYYY-MM-DD",ll:"YYYY [m.] MMMM D [d.]",lll:"YYYY [m.] MMMM D [d.], LT [val.]",llll:"YYYY [m.] MMMM D [d.], ddd, LT [val.]"},calendar:{sameDay:"[Šiandien] LT",nextDay:"[Rytoj] LT",nextWeek:"dddd LT",lastDay:"[Vakar] LT",lastWeek:"[Praėjusį] dddd LT",sameElse:"L"},relativeTime:{future:"po %s",past:"prieš %s",s:a,m:n,mm:s,h:n,hh:s,d:n,dd:s,M:n,MM:s,y:n,yy:s},ordinal:function(e){return e+"-oji"},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("lt","lt",{closeText:"Uždaryti",prevText:"<Atgal",nextText:"Pirmyn>",currentText:"Šiandien",monthNames:["Sausis","Vasaris","Kovas","Balandis","Gegužė","Birželis","Liepa","Rugpjūtis","Rugsėjis","Spalis","Lapkritis","Gruodis"],monthNamesShort:["Sau","Vas","Kov","Bal","Geg","Bir","Lie","Rugp","Rugs","Spa","Lap","Gru"],dayNames:["sekmadienis","pirmadienis","antradienis","trečiadienis","ketvirtadienis","penktadienis","šeštadienis"],dayNamesShort:["sek","pir","ant","tre","ket","pen","šeš"],dayNamesMin:["Se","Pr","An","Tr","Ke","Pe","Še"],weekHeader:"SAV",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:""}),e.fullCalendar.lang("lt",{defaultButtonText:{month:"Mėnuo",week:"Savaitė",day:"Diena",list:"Darbotvarkė"},allDayText:"Visą dieną",eventLimitText:"daugiau"})}(),function(){function a(e,t,a){var n=e.split("_");return a?1===t%10&&11!==t?n[2]:n[3]:1===t%10&&11!==t?n[0]:n[1]}function n(e,t,n){return e+" "+a(r[n],e,t)}var r={mm:"minūti_minūtes_minūte_minūtes",hh:"stundu_stundas_stunda_stundas",dd:"dienu_dienas_diena_dienas",MM:"mēnesi_mēnešus_mēnesis_mēneši",yy:"gadu_gadus_gads_gadi"};(t.defineLocale||t.lang).call(t,"lv",{months:"janvāris_februāris_marts_aprīlis_maijs_jūnijs_jūlijs_augusts_septembris_oktobris_novembris_decembris".split("_"),monthsShort:"jan_feb_mar_apr_mai_jūn_jūl_aug_sep_okt_nov_dec".split("_"),weekdays:"svētdiena_pirmdiena_otrdiena_trešdiena_ceturtdiena_piektdiena_sestdiena".split("_"),weekdaysShort:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysMin:"Sv_P_O_T_C_Pk_S".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"YYYY. [gada] D. MMMM",LLL:"YYYY. [gada] D. MMMM, LT",LLLL:"YYYY. [gada] D. MMMM, dddd, LT"},calendar:{sameDay:"[Šodien pulksten] LT",nextDay:"[Rīt pulksten] LT",nextWeek:"dddd [pulksten] LT",lastDay:"[Vakar pulksten] LT",lastWeek:"[Pagājušā] dddd [pulksten] LT",sameElse:"L"},relativeTime:{future:"%s vēlāk",past:"%s agrāk",s:"dažas sekundes",m:"minūti",mm:n,h:"stundu",hh:n,d:"dienu",dd:n,M:"mēnesi",MM:n,y:"gadu",yy:n},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("lv","lv",{closeText:"Aizvērt",prevText:"Iepr.",nextText:"Nāk.",currentText:"Šodien",monthNames:["Janvāris","Februāris","Marts","Aprīlis","Maijs","Jūnijs","Jūlijs","Augusts","Septembris","Oktobris","Novembris","Decembris"],monthNamesShort:["Jan","Feb","Mar","Apr","Mai","Jūn","Jūl","Aug","Sep","Okt","Nov","Dec"],dayNames:["svētdiena","pirmdiena","otrdiena","trešdiena","ceturtdiena","piektdiena","sestdiena"],dayNamesShort:["svt","prm","otr","tre","ctr","pkt","sst"],dayNamesMin:["Sv","Pr","Ot","Tr","Ct","Pk","Ss"],weekHeader:"Ned.",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("lv",{defaultButtonText:{month:"Mēnesis",week:"Nedēļa",day:"Diena",list:"Dienas kārtība"},allDayText:"Visu dienu",eventLimitText:function(e){return"+vēl "+e}})}(),function(){var a="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),n="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_");(t.defineLocale||t.lang).call(t,"nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(e,t){return/-MMM-/.test(t)?n[e.month()]:a[e.month()]},weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"Zo_Ma_Di_Wo_Do_Vr_Za".split("_"),longDateFormat:{LT:"HH:mm",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("nl","nl",{closeText:"Sluiten",prevText:"←",nextText:"→",currentText:"Vandaag",monthNames:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthNamesShort:["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],dayNames:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],dayNamesShort:["zon","maa","din","woe","don","vri","zat"],dayNamesMin:["zo","ma","di","wo","do","vr","za"],weekHeader:"Wk",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("nl",{defaultButtonText:{month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayText:"Hele dag",eventLimitText:"extra"})}(),function(){function a(e){return 5>e%10&&e%10>1&&1!==~~(e/10)%10}function n(e,t,n){var r=e+" ";switch(n){case"m":return t?"minuta":"minutę";case"mm":return r+(a(e)?"minuty":"minut");case"h":return t?"godzina":"godzinę";case"hh":return r+(a(e)?"godziny":"godzin");case"MM":return r+(a(e)?"miesiące":"miesięcy");case"yy":return r+(a(e)?"lata":"lat")}}var r="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),i="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_");(t.defineLocale||t.lang).call(t,"pl",{months:function(e,t){return/D MMMM/.test(t)?i[e.month()]:r[e.month()]},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"nie_pon_wt_śr_czw_pt_sb".split("_"),weekdaysMin:"N_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:"[W] dddd [o] LT",lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",m:n,mm:n,h:n,hh:n,d:"1 dzień",dd:"%d dni",M:"miesiąc",MM:n,y:"rok",yy:n},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("pl","pl",{closeText:"Zamknij",prevText:"<Poprzedni",nextText:"Następny>",currentText:"Dziś",monthNames:["Styczeń","Luty","Marzec","Kwiecień","Maj","Czerwiec","Lipiec","Sierpień","Wrzesień","Październik","Listopad","Grudzień"],monthNamesShort:["Sty","Lu","Mar","Kw","Maj","Cze","Lip","Sie","Wrz","Pa","Lis","Gru"],dayNames:["Niedziela","Poniedziałek","Wtorek","Środa","Czwartek","Piątek","Sobota"],dayNamesShort:["Nie","Pn","Wt","Śr","Czw","Pt","So"],dayNamesMin:["N","Pn","Wt","Śr","Cz","Pt","So"],weekHeader:"Tydz",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("pl",{defaultButtonText:{month:"Miesiąc",week:"Tydzień",day:"Dzień",list:"Plan dnia"},allDayText:"Cały dzień",eventLimitText:"więcej"})}(),function(){(t.defineLocale||t.lang).call(t,"pt-br",{months:"janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_sáb".split("_"),weekdaysMin:"dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY [às] LT",LLLL:"dddd, D [de] MMMM [de] YYYY [às] LT"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"%s atrás",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinal:"%dº"}),e.fullCalendar.datepickerLang("pt-br","pt-BR",{closeText:"Fechar",prevText:"<Anterior",nextText:"Próximo>",currentText:"Hoje",monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("pt-br",{defaultButtonText:{month:"Mês",week:"Semana",day:"Dia",list:"Compromissos"},allDayText:"dia inteiro",eventLimitText:function(e){return"mais +"+e}})}(),function(){(t.defineLocale||t.lang).call(t,"pt",{months:"janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_sáb".split("_"),weekdaysMin:"dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY LT",LLLL:"dddd, D [de] MMMM [de] YYYY LT"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"há %s",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("pt","pt",{closeText:"Fechar",prevText:"Anterior",nextText:"Seguinte",currentText:"Hoje",monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],weekHeader:"Sem",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("pt",{defaultButtonText:{month:"Mês",week:"Semana",day:"Dia",list:"Agenda"},allDayText:"Todo o dia",eventLimitText:"mais"})}(),function(){function a(e,t,a){var n={mm:"minute",hh:"ore",dd:"zile",MM:"luni",yy:"ani"},r=" ";return(e%100>=20||e>=100&&0===e%100)&&(r=" de "),e+r+n[a]}(t.defineLocale||t.lang).call(t,"ro",{months:"ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"),monthsShort:"ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"),weekdays:"duminică_luni_marți_miercuri_joi_vineri_sâmbătă".split("_"),weekdaysShort:"Dum_Lun_Mar_Mie_Joi_Vin_Sâm".split("_"),weekdaysMin:"Du_Lu_Ma_Mi_Jo_Vi_Sâ".split("_"),longDateFormat:{LT:"H:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[azi la] LT",nextDay:"[mâine la] LT",nextWeek:"dddd [la] LT",lastDay:"[ieri la] LT",lastWeek:"[fosta] dddd [la] LT",sameElse:"L"},relativeTime:{future:"peste %s",past:"%s în urmă",s:"câteva secunde",m:"un minut",mm:a,h:"o oră",hh:a,d:"o zi",dd:a,M:"o lună",MM:a,y:"un an",yy:a},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("ro","ro",{closeText:"Închide",prevText:"« Luna precedentă",nextText:"Luna următoare »",currentText:"Azi",monthNames:["Ianuarie","Februarie","Martie","Aprilie","Mai","Iunie","Iulie","August","Septembrie","Octombrie","Noiembrie","Decembrie"],monthNamesShort:["Ian","Feb","Mar","Apr","Mai","Iun","Iul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Duminică","Luni","Marţi","Miercuri","Joi","Vineri","Sâmbătă"],dayNamesShort:["Dum","Lun","Mar","Mie","Joi","Vin","Sâm"],dayNamesMin:["Du","Lu","Ma","Mi","Jo","Vi","Sâ"],weekHeader:"Săpt",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("ro",{defaultButtonText:{prev:"precedentă",next:"următoare",month:"Lună",week:"Săptămână",day:"Zi",list:"Agendă"},allDayText:"Toată ziua",eventLimitText:function(e){return"+alte "+e +}})}(),function(){function a(e,t){var a=e.split("_");return 1===t%10&&11!==t%100?a[0]:t%10>=2&&4>=t%10&&(10>t%100||t%100>=20)?a[1]:a[2]}function n(e,t,n){var r={mm:t?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",MM:"месяц_месяца_месяцев",yy:"год_года_лет"};return"m"===n?t?"минута":"минуту":e+" "+a(r[n],+e)}function r(e,t){var a={nominative:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),accusative:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_")},n=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(t)?"accusative":"nominative";return a[n][e.month()]}function i(e,t){var a={nominative:"янв_фев_мар_апр_май_июнь_июль_авг_сен_окт_ноя_дек".split("_"),accusative:"янв_фев_мар_апр_мая_июня_июля_авг_сен_окт_ноя_дек".split("_")},n=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(t)?"accusative":"nominative";return a[n][e.month()]}function s(e,t){var a={nominative:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),accusative:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_")},n=/\[ ?[Вв] ?(?:прошлую|следующую)? ?\] ?dddd/.test(t)?"accusative":"nominative";return a[n][e.day()]}(t.defineLocale||t.lang).call(t,"ru",{months:r,monthsShort:i,weekdays:s,weekdaysShort:"вс_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"вс_пн_вт_ср_чт_пт_сб".split("_"),monthsParse:[/^янв/i,/^фев/i,/^мар/i,/^апр/i,/^ма[й|я]/i,/^июн/i,/^июл/i,/^авг/i,/^сен/i,/^окт/i,/^ноя/i,/^дек/i],longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., LT",LLLL:"dddd, D MMMM YYYY г., LT"},calendar:{sameDay:"[Сегодня в] LT",nextDay:"[Завтра в] LT",lastDay:"[Вчера в] LT",nextWeek:function(){return 2===this.day()?"[Во] dddd [в] LT":"[В] dddd [в] LT"},lastWeek:function(){switch(this.day()){case 0:return"[В прошлое] dddd [в] LT";case 1:case 2:case 4:return"[В прошлый] dddd [в] LT";case 3:case 5:case 6:return"[В прошлую] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"через %s",past:"%s назад",s:"несколько секунд",m:n,mm:n,h:"час",hh:n,d:"день",dd:n,M:"месяц",MM:n,y:"год",yy:n},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(e){return/^(дня|вечера)$/.test(e)},meridiem:function(e){return 4>e?"ночи":12>e?"утра":17>e?"дня":"вечера"},ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":return e+"-й";case"D":return e+"-го";case"w":case"W":return e+"-я";default:return e}},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("ru","ru",{closeText:"Закрыть",prevText:"<Пред",nextText:"След>",currentText:"Сегодня",monthNames:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthNamesShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],dayNames:["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"],dayNamesShort:["вск","пнд","втр","срд","чтв","птн","сбт"],dayNamesMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],weekHeader:"Нед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("ru",{defaultButtonText:{month:"Месяц",week:"Неделя",day:"День",list:"Повестка дня"},allDayText:"Весь день",eventLimitText:function(e){return"+ ещё "+e}})}(),function(){function a(e){return e>1&&5>e}function n(e,t,n,r){var i=e+" ";switch(n){case"s":return t||r?"pár sekúnd":"pár sekundami";case"m":return t?"minúta":r?"minútu":"minútou";case"mm":return t||r?i+(a(e)?"minúty":"minút"):i+"minútami";case"h":return t?"hodina":r?"hodinu":"hodinou";case"hh":return t||r?i+(a(e)?"hodiny":"hodín"):i+"hodinami";case"d":return t||r?"deň":"dňom";case"dd":return t||r?i+(a(e)?"dni":"dní"):i+"dňami";case"M":return t||r?"mesiac":"mesiacom";case"MM":return t||r?i+(a(e)?"mesiace":"mesiacov"):i+"mesiacmi";case"y":return t||r?"rok":"rokom";case"yy":return t||r?i+(a(e)?"roky":"rokov"):i+"rokmi"}}var r="január_február_marec_apríl_máj_jún_júl_august_september_október_november_december".split("_"),i="jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec".split("_");(t.defineLocale||t.lang).call(t,"sk",{months:r,monthsShort:i,monthsParse:function(e,t){var a,n=[];for(a=0;12>a;a++)n[a]=RegExp("^"+e[a]+"$|^"+t[a]+"$","i");return n}(r,i),weekdays:"nedeľa_pondelok_utorok_streda_štvrtok_piatok_sobota".split("_"),weekdaysShort:"ne_po_ut_st_št_pi_so".split("_"),weekdaysMin:"ne_po_ut_st_št_pi_so".split("_"),longDateFormat:{LT:"H:mm",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd D. MMMM YYYY LT"},calendar:{sameDay:"[dnes o] LT",nextDay:"[zajtra o] LT",nextWeek:function(){switch(this.day()){case 0:return"[v nedeľu o] LT";case 1:case 2:return"[v] dddd [o] LT";case 3:return"[v stredu o] LT";case 4:return"[vo štvrtok o] LT";case 5:return"[v piatok o] LT";case 6:return"[v sobotu o] LT"}},lastDay:"[včera o] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulú nedeľu o] LT";case 1:case 2:return"[minulý] dddd [o] LT";case 3:return"[minulú stredu o] LT";case 4:case 5:return"[minulý] dddd [o] LT";case 6:return"[minulú sobotu o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"pred %s",s:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("sk","sk",{closeText:"Zavrieť",prevText:"<Predchádzajúci",nextText:"Nasledujúci>",currentText:"Dnes",monthNames:["január","február","marec","apríl","máj","jún","júl","august","september","október","november","december"],monthNamesShort:["Jan","Feb","Mar","Apr","Máj","Jún","Júl","Aug","Sep","Okt","Nov","Dec"],dayNames:["nedeľa","pondelok","utorok","streda","štvrtok","piatok","sobota"],dayNamesShort:["Ned","Pon","Uto","Str","Štv","Pia","Sob"],dayNamesMin:["Ne","Po","Ut","St","Št","Pia","So"],weekHeader:"Ty",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sk",{defaultButtonText:{month:"Mesiac",week:"Týždeň",day:"Deň",list:"Rozvrh"},allDayText:"Celý deň",eventLimitText:function(e){return"+ďalšie: "+e}})}(),function(){function a(e,t,a){var n=e+" ";switch(a){case"m":return t?"ena minuta":"eno minuto";case"mm":return n+=1===e?"minuta":2===e?"minuti":3===e||4===e?"minute":"minut";case"h":return t?"ena ura":"eno uro";case"hh":return n+=1===e?"ura":2===e?"uri":3===e||4===e?"ure":"ur";case"dd":return n+=1===e?"dan":"dni";case"MM":return n+=1===e?"mesec":2===e?"meseca":3===e||4===e?"mesece":"mesecev";case"yy":return n+=1===e?"leto":2===e?"leti":3===e||4===e?"leta":"let"}}(t.defineLocale||t.lang).call(t,"sl",{months:"januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"),weekdays:"nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota".split("_"),weekdaysShort:"ned._pon._tor._sre._čet._pet._sob.".split("_"),weekdaysMin:"ne_po_to_sr_če_pe_so".split("_"),longDateFormat:{LT:"H:mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danes ob] LT",nextDay:"[jutri ob] LT",nextWeek:function(){switch(this.day()){case 0:return"[v] [nedeljo] [ob] LT";case 3:return"[v] [sredo] [ob] LT";case 6:return"[v] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[v] dddd [ob] LT"}},lastDay:"[včeraj ob] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[prejšnja] dddd [ob] LT";case 1:case 2:case 4:case 5:return"[prejšnji] dddd [ob] LT"}},sameElse:"L"},relativeTime:{future:"čez %s",past:"%s nazaj",s:"nekaj sekund",m:a,mm:a,h:a,hh:a,d:"en dan",dd:a,M:"en mesec",MM:a,y:"eno leto",yy:a},ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("sl","sl",{closeText:"Zapri",prevText:"<Prejšnji",nextText:"Naslednji>",currentText:"Trenutni",monthNames:["Januar","Februar","Marec","April","Maj","Junij","Julij","Avgust","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],dayNames:["Nedelja","Ponedeljek","Torek","Sreda","Četrtek","Petek","Sobota"],dayNamesShort:["Ned","Pon","Tor","Sre","Čet","Pet","Sob"],dayNamesMin:["Ne","Po","To","Sr","Če","Pe","So"],weekHeader:"Teden",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sl",{defaultButtonText:{month:"Mesec",week:"Teden",day:"Dan",list:"Dnevni red"},allDayText:"Ves dan",eventLimitText:"več"})}(),function(){var a={words:{m:["један минут","једне минуте"],mm:["минут","минуте","минута"],h:["један сат","једног сата"],hh:["сат","сата","сати"],dd:["дан","дана","дана"],MM:["месец","месеца","месеци"],yy:["година","године","година"]},correctGrammaticalCase:function(e,t){return 1===e?t[0]:e>=2&&4>=e?t[1]:t[2]},translate:function(e,t,n){var r=a.words[n];return 1===n.length?t?r[0]:r[1]:e+" "+a.correctGrammaticalCase(e,r)}};(t.defineLocale||t.lang).call(t,"sr-cyrl",{months:["јануар","фебруар","март","април","мај","јун","јул","август","септембар","октобар","новембар","децембар"],monthsShort:["јан.","феб.","мар.","апр.","мај","јун","јул","авг.","сеп.","окт.","нов.","дец."],weekdays:["недеља","понедељак","уторак","среда","четвртак","петак","субота"],weekdaysShort:["нед.","пон.","уто.","сре.","чет.","пет.","суб."],weekdaysMin:["не","по","ут","ср","че","пе","су"],longDateFormat:{LT:"H:mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[данас у] LT",nextDay:"[сутра у] LT",nextWeek:function(){switch(this.day()){case 0:return"[у] [недељу] [у] LT";case 3:return"[у] [среду] [у] LT";case 6:return"[у] [суботу] [у] LT";case 1:case 2:case 4:case 5:return"[у] dddd [у] LT"}},lastDay:"[јуче у] LT",lastWeek:function(){var e=["[прошле] [недеље] [у] LT","[прошлог] [понедељка] [у] LT","[прошлог] [уторка] [у] LT","[прошле] [среде] [у] LT","[прошлог] [четвртка] [у] LT","[прошлог] [петка] [у] LT","[прошле] [суботе] [у] LT"];return e[this.day()]},sameElse:"L"},relativeTime:{future:"за %s",past:"пре %s",s:"неколико секунди",m:a.translate,mm:a.translate,h:a.translate,hh:a.translate,d:"дан",dd:a.translate,M:"месец",MM:a.translate,y:"годину",yy:a.translate},ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("sr-cyrl","sr",{closeText:"Затвори",prevText:"<",nextText:">",currentText:"Данас",monthNames:["Јануар","Фебруар","Март","Април","Мај","Јун","Јул","Август","Септембар","Октобар","Новембар","Децембар"],monthNamesShort:["Јан","Феб","Мар","Апр","Мај","Јун","Јул","Авг","Сеп","Окт","Нов","Дец"],dayNames:["Недеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"],dayNamesShort:["Нед","Пон","Уто","Сре","Чет","Пет","Суб"],dayNamesMin:["Не","По","Ут","Ср","Че","Пе","Су"],weekHeader:"Сед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sr-cyrl",{defaultButtonText:{month:"Месец",week:"Недеља",day:"Дан",list:"Планер"},allDayText:"Цео дан",eventLimitText:function(e){return"+ још "+e}})}(),function(){var a={words:{m:["jedan minut","jedne minute"],mm:["minut","minute","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mesec","meseca","meseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(e,t){return 1===e?t[0]:e>=2&&4>=e?t[1]:t[2]},translate:function(e,t,n){var r=a.words[n];return 1===n.length?t?r[0]:r[1]:e+" "+a.correctGrammaticalCase(e,r)}};(t.defineLocale||t.lang).call(t,"sr",{months:["januar","februar","mart","april","maj","jun","jul","avgust","septembar","oktobar","novembar","decembar"],monthsShort:["jan.","feb.","mar.","apr.","maj","jun","jul","avg.","sep.","okt.","nov.","dec."],weekdays:["nedelja","ponedeljak","utorak","sreda","četvrtak","petak","subota"],weekdaysShort:["ned.","pon.","uto.","sre.","čet.","pet.","sub."],weekdaysMin:["ne","po","ut","sr","če","pe","su"],longDateFormat:{LT:"H:mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedelju] [u] LT";case 3:return"[u] [sredu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var e=["[prošle] [nedelje] [u] LT","[prošlog] [ponedeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT"];return e[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"pre %s",s:"nekoliko sekundi",m:a.translate,mm:a.translate,h:a.translate,hh:a.translate,d:"dan",dd:a.translate,M:"mesec",MM:a.translate,y:"godinu",yy:a.translate},ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("sr","sr",{closeText:"Затвори",prevText:"<",nextText:">",currentText:"Данас",monthNames:["Јануар","Фебруар","Март","Април","Мај","Јун","Јул","Август","Септембар","Октобар","Новембар","Децембар"],monthNamesShort:["Јан","Феб","Мар","Апр","Мај","Јун","Јул","Авг","Сеп","Окт","Нов","Дец"],dayNames:["Недеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"],dayNamesShort:["Нед","Пон","Уто","Сре","Чет","Пет","Суб"],dayNamesMin:["Не","По","Ут","Ср","Че","Пе","Су"],weekHeader:"Сед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sr",{defaultButtonText:{month:"Месец",week:"Недеља",day:"Дан",list:"Планер"},allDayText:"Цео дан",eventLimitText:function(e){return"+ још "+e}})}(),function(){(t.defineLocale||t.lang).call(t,"sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag".split("_"),weekdaysShort:"sön_mån_tis_ons_tor_fre_lör".split("_"),weekdaysMin:"sö_må_ti_on_to_fr_lö".split("_"),longDateFormat:{LT:"HH:mm",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[Igår] LT",nextWeek:"dddd LT",lastWeek:"[Förra] dddd[en] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"för %s sedan",s:"några sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en månad",MM:"%d månader",y:"ett år",yy:"%d år"},ordinal:function(e){var t=e%10,a=1===~~(e%100/10)?"e":1===t?"a":2===t?"a":3===t?"e":"e";return e+a},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("sv","sv",{closeText:"Stäng",prevText:"«Förra",nextText:"Nästa»",currentText:"Idag",monthNames:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNamesShort:["Sön","Mån","Tis","Ons","Tor","Fre","Lör"],dayNames:["Söndag","Måndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag"],dayNamesMin:["Sö","Må","Ti","On","To","Fr","Lö"],weekHeader:"Ve",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sv",{defaultButtonText:{month:"Månad",week:"Vecka",day:"Dag",list:"Program"},allDayText:"Heldag",eventLimitText:"till"})}(),function(){(t.defineLocale||t.lang).call(t,"th",{months:"มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม".split("_"),monthsShort:"มกรา_กุมภา_มีนา_เมษา_พฤษภา_มิถุนา_กรกฎา_สิงหา_กันยา_ตุลา_พฤศจิกา_ธันวา".split("_"),weekdays:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์".split("_"),weekdaysShort:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์".split("_"),weekdaysMin:"อา._จ._อ._พ._พฤ._ศ._ส.".split("_"),longDateFormat:{LT:"H นาฬิกา m นาที",L:"YYYY/MM/DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY เวลา LT",LLLL:"วันddddที่ D MMMM YYYY เวลา LT"},meridiem:function(e){return 12>e?"ก่อนเที่ยง":"หลังเที่ยง"},calendar:{sameDay:"[วันนี้ เวลา] LT",nextDay:"[พรุ่งนี้ เวลา] LT",nextWeek:"dddd[หน้า เวลา] LT",lastDay:"[เมื่อวานนี้ เวลา] LT",lastWeek:"[วัน]dddd[ที่แล้ว เวลา] LT",sameElse:"L"},relativeTime:{future:"อีก %s",past:"%sที่แล้ว",s:"ไม่กี่วินาที",m:"1 นาที",mm:"%d นาที",h:"1 ชั่วโมง",hh:"%d ชั่วโมง",d:"1 วัน",dd:"%d วัน",M:"1 เดือน",MM:"%d เดือน",y:"1 ปี",yy:"%d ปี"}}),e.fullCalendar.datepickerLang("th","th",{closeText:"ปิด",prevText:"« ย้อน",nextText:"ถัดไป »",currentText:"วันนี้",monthNames:["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],monthNamesShort:["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."],dayNames:["อาทิตย์","จันทร์","อังคาร","พุธ","พฤหัสบดี","ศุกร์","เสาร์"],dayNamesShort:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],dayNamesMin:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("th",{defaultButtonText:{month:"เดือน",week:"สัปดาห์",day:"วัน",list:"แผนงาน"},allDayText:"ตลอดวัน",eventLimitText:"เพิ่มเติม"})}(),function(){var a={1:"'inci",5:"'inci",8:"'inci",70:"'inci",80:"'inci",2:"'nci",7:"'nci",20:"'nci",50:"'nci",3:"'üncü",4:"'üncü",100:"'üncü",6:"'ncı",9:"'uncu",10:"'uncu",30:"'uncu",60:"'ıncı",90:"'ıncı"};(t.defineLocale||t.lang).call(t,"tr",{months:"Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık".split("_"),monthsShort:"Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara".split("_"),weekdays:"Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi".split("_"),weekdaysShort:"Paz_Pts_Sal_Çar_Per_Cum_Cts".split("_"),weekdaysMin:"Pz_Pt_Sa_Ça_Pe_Cu_Ct".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[bugün saat] LT",nextDay:"[yarın saat] LT",nextWeek:"[haftaya] dddd [saat] LT",lastDay:"[dün] LT",lastWeek:"[geçen hafta] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s önce",s:"birkaç saniye",m:"bir dakika",mm:"%d dakika",h:"bir saat",hh:"%d saat",d:"bir gün",dd:"%d gün",M:"bir ay",MM:"%d ay",y:"bir yıl",yy:"%d yıl"},ordinal:function(e){if(0===e)return e+"'ıncı";var t=e%10,n=e%100-t,r=e>=100?100:null;return e+(a[t]||a[n]||a[r])},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("tr","tr",{closeText:"kapat",prevText:"<geri",nextText:"ileri>",currentText:"bugün",monthNames:["Ocak","Şubat","Mart","Nisan","Mayıs","Haziran","Temmuz","Ağustos","Eylül","Ekim","Kasım","Aralık"],monthNamesShort:["Oca","Şub","Mar","Nis","May","Haz","Tem","Ağu","Eyl","Eki","Kas","Ara"],dayNames:["Pazar","Pazartesi","Salı","Çarşamba","Perşembe","Cuma","Cumartesi"],dayNamesShort:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],dayNamesMin:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],weekHeader:"Hf",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("tr",{defaultButtonText:{next:"ileri",month:"Ay",week:"Hafta",day:"Gün",list:"Ajanda"},allDayText:"Tüm gün",eventLimitText:"daha fazla"})}(),function(){function a(e,t){var a=e.split("_");return 1===t%10&&11!==t%100?a[0]:t%10>=2&&4>=t%10&&(10>t%100||t%100>=20)?a[1]:a[2]}function n(e,t,n){var r={mm:"хвилина_хвилини_хвилин",hh:"година_години_годин",dd:"день_дні_днів",MM:"місяць_місяці_місяців",yy:"рік_роки_років"};return"m"===n?t?"хвилина":"хвилину":"h"===n?t?"година":"годину":e+" "+a(r[n],+e)}function r(e,t){var a={nominative:"січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень".split("_"),accusative:"січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня".split("_")},n=/D[oD]? *MMMM?/.test(t)?"accusative":"nominative";return a[n][e.month()]}function i(e,t){var a={nominative:"неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота".split("_"),accusative:"неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу".split("_"),genitive:"неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи".split("_")},n=/(\[[ВвУу]\]) ?dddd/.test(t)?"accusative":/\[?(?:минулої|наступної)? ?\] ?dddd/.test(t)?"genitive":"nominative";return a[n][e.day()]}function s(e){return function(){return e+"о"+(11===this.hours()?"б":"")+"] LT"}}(t.defineLocale||t.lang).call(t,"uk",{months:r,monthsShort:"січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд".split("_"),weekdays:i,weekdaysShort:"нд_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY р.",LLL:"D MMMM YYYY р., LT",LLLL:"dddd, D MMMM YYYY р., LT"},calendar:{sameDay:s("[Сьогодні "),nextDay:s("[Завтра "),lastDay:s("[Вчора "),nextWeek:s("[У] dddd ["),lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return s("[Минулої] dddd [").call(this);case 1:case 2:case 4:return s("[Минулого] dddd [").call(this)}},sameElse:"L"},relativeTime:{future:"за %s",past:"%s тому",s:"декілька секунд",m:n,mm:n,h:"годину",hh:n,d:"день",dd:n,M:"місяць",MM:n,y:"рік",yy:n},meridiem:function(e){return 4>e?"ночі":12>e?"ранку":17>e?"дня":"вечора"},ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":case"w":case"W":return e+"-й";case"D":return e+"-го";default:return e}},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("uk","uk",{closeText:"Закрити",prevText:"<",nextText:">",currentText:"Сьогодні",monthNames:["Січень","Лютий","Березень","Квітень","Травень","Червень","Липень","Серпень","Вересень","Жовтень","Листопад","Грудень"],monthNamesShort:["Січ","Лют","Бер","Кві","Тра","Чер","Лип","Сер","Вер","Жов","Лис","Гру"],dayNames:["неділя","понеділок","вівторок","середа","четвер","п’ятниця","субота"],dayNamesShort:["нед","пнд","вів","срд","чтв","птн","сбт"],dayNamesMin:["Нд","Пн","Вт","Ср","Чт","Пт","Сб"],weekHeader:"Тиж",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("uk",{defaultButtonText:{month:"Місяць",week:"Тиждень",day:"День",list:"Порядок денний"},allDayText:"Увесь день",eventLimitText:function(e){return"+ще "+e+"..."}})}(),function(){(t.defineLocale||t.lang).call(t,"vi",{months:"tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12".split("_"),monthsShort:"Th01_Th02_Th03_Th04_Th05_Th06_Th07_Th08_Th09_Th10_Th11_Th12".split("_"),weekdays:"chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy".split("_"),weekdaysShort:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysMin:"CN_T2_T3_T4_T5_T6_T7".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM [năm] YYYY",LLL:"D MMMM [năm] YYYY LT",LLLL:"dddd, D MMMM [năm] YYYY LT",l:"DD/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY LT",llll:"ddd, D MMM YYYY LT"},calendar:{sameDay:"[Hôm nay lúc] LT",nextDay:"[Ngày mai lúc] LT",nextWeek:"dddd [tuần tới lúc] LT",lastDay:"[Hôm qua lúc] LT",lastWeek:"dddd [tuần rồi lúc] LT",sameElse:"L"},relativeTime:{future:"%s tới",past:"%s trước",s:"vài giây",m:"một phút",mm:"%d phút",h:"một giờ",hh:"%d giờ",d:"một ngày",dd:"%d ngày",M:"một tháng",MM:"%d tháng",y:"một năm",yy:"%d năm"},ordinal:function(e){return e},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("vi","vi",{closeText:"Đóng",prevText:"<Trước",nextText:"Tiếp>",currentText:"Hôm nay",monthNames:["Tháng Một","Tháng Hai","Tháng Ba","Tháng Tư","Tháng Năm","Tháng Sáu","Tháng Bảy","Tháng Tám","Tháng Chín","Tháng Mười","Tháng Mười Một","Tháng Mười Hai"],monthNamesShort:["Tháng 1","Tháng 2","Tháng 3","Tháng 4","Tháng 5","Tháng 6","Tháng 7","Tháng 8","Tháng 9","Tháng 10","Tháng 11","Tháng 12"],dayNames:["Chủ Nhật","Thứ Hai","Thứ Ba","Thứ Tư","Thứ Năm","Thứ Sáu","Thứ Bảy"],dayNamesShort:["CN","T2","T3","T4","T5","T6","T7"],dayNamesMin:["CN","T2","T3","T4","T5","T6","T7"],weekHeader:"Tu",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("vi",{defaultButtonText:{month:"Tháng",week:"Tuần",day:"Ngày",list:"Lịch biểu"},allDayText:"Cả ngày",eventLimitText:function(e){return"+ thêm "+e}})}(),function(){(t.defineLocale||t.lang).call(t,"zh-cn",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"周日_周一_周二_周三_周四_周五_周六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"Ah点mm",L:"YYYY-MM-DD",LL:"YYYY年MMMD日",LLL:"YYYY年MMMD日LT",LLLL:"YYYY年MMMD日ddddLT",l:"YYYY-MM-DD",ll:"YYYY年MMMD日",lll:"YYYY年MMMD日LT",llll:"YYYY年MMMD日ddddLT"},meridiem:function(e,t){var a=100*e+t;return 600>a?"凌晨":900>a?"早上":1130>a?"上午":1230>a?"中午":1800>a?"下午":"晚上"},calendar:{sameDay:function(){return 0===this.minutes()?"[今天]Ah[点整]":"[今天]LT"},nextDay:function(){return 0===this.minutes()?"[明天]Ah[点整]":"[明天]LT"},lastDay:function(){return 0===this.minutes()?"[昨天]Ah[点整]":"[昨天]LT"},nextWeek:function(){var e,a;return e=t().startOf("week"),a=this.unix()-e.unix()>=604800?"[下]":"[本]",0===this.minutes()?a+"dddAh点整":a+"dddAh点mm"},lastWeek:function(){var e,a;return e=t().startOf("week"),a=this.unix()a?"早上":1130>a?"上午":1230>a?"中午":1800>a?"下午":"晚上"},calendar:{sameDay:"[今天]LT",nextDay:"[明天]LT",nextWeek:"[下]ddddLT",lastDay:"[昨天]LT",lastWeek:"[上]ddddLT",sameElse:"L"},ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s內",past:"%s前",s:"幾秒",m:"一分鐘",mm:"%d分鐘",h:"一小時",hh:"%d小時",d:"一天",dd:"%d天",M:"一個月",MM:"%d個月",y:"一年",yy:"%d年"}}),e.fullCalendar.datepickerLang("zh-tw","zh-TW",{closeText:"關閉",prevText:"<上月",nextText:"下月>",currentText:"今天",monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthNamesShort:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周六"],dayNamesMin:["日","一","二","三","四","五","六"],weekHeader:"周",dateFormat:"yy/mm/dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"年"}),e.fullCalendar.lang("zh-tw",{defaultButtonText:{month:"月",week:"週",day:"天",list:"待辦事項"},allDayText:"全天",eventLimitText:"更多"})}(),(t.locale||t.lang).call(t,"en"),e.fullCalendar.lang("en"),e.datepicker&&e.datepicker.setDefaults(e.datepicker.regional[""])}); \ No newline at end of file diff --git a/dist/lang/ar-ma.js b/dist/lang/ar-ma.js new file mode 100644 index 0000000..952b0d1 --- /dev/null +++ b/dist/lang/ar-ma.js @@ -0,0 +1 @@ +(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){(e.defineLocale||e.lang).call(e,"ar-ma",{months:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),weekdays:"الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:6,doy:12}}),t.fullCalendar.datepickerLang("ar-ma","ar",{closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["كانون الثاني","شباط","آذار","نيسان","مايو","حزيران","تموز","آب","أيلول","تشرين الأول","تشرين الثاني","كانون الأول"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesMin:["ح","ن","ث","ر","خ","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:6,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("ar-ma",{defaultButtonText:{month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},allDayText:"اليوم كله",eventLimitText:"أخرى"})}); \ No newline at end of file diff --git a/dist/lang/ar-sa.js b/dist/lang/ar-sa.js new file mode 100644 index 0000000..2ca28b3 --- /dev/null +++ b/dist/lang/ar-sa.js @@ -0,0 +1 @@ +(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){var n={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},i={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"};(e.defineLocale||e.lang).call(e,"ar-sa",{months:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},meridiem:function(t){return 12>t?"ص":"م"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},preparse:function(t){return t.replace(/[۰-۹]/g,function(t){return i[t]}).replace(/،/g,",")},postformat:function(t){return t.replace(/\d/g,function(t){return n[t]}).replace(/,/g,"،")},week:{dow:6,doy:12}}),t.fullCalendar.datepickerLang("ar-sa","ar",{closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["كانون الثاني","شباط","آذار","نيسان","مايو","حزيران","تموز","آب","أيلول","تشرين الأول","تشرين الثاني","كانون الأول"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesMin:["ح","ن","ث","ر","خ","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:6,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("ar-sa",{defaultButtonText:{month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},allDayText:"اليوم كله",eventLimitText:"أخرى"})}); \ No newline at end of file diff --git a/dist/lang/ar.js b/dist/lang/ar.js new file mode 100644 index 0000000..e6f2163 --- /dev/null +++ b/dist/lang/ar.js @@ -0,0 +1 @@ +(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){var n={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},i={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"};(e.defineLocale||e.lang).call(e,"ar",{months:"يناير/ كانون الثاني_فبراير/ شباط_مارس/ آذار_أبريل/ نيسان_مايو/ أيار_يونيو/ حزيران_يوليو/ تموز_أغسطس/ آب_سبتمبر/ أيلول_أكتوبر/ تشرين الأول_نوفمبر/ تشرين الثاني_ديسمبر/ كانون الأول".split("_"),monthsShort:"يناير/ كانون الثاني_فبراير/ شباط_مارس/ آذار_أبريل/ نيسان_مايو/ أيار_يونيو/ حزيران_يوليو/ تموز_أغسطس/ آب_سبتمبر/ أيلول_أكتوبر/ تشرين الأول_نوفمبر/ تشرين الثاني_ديسمبر/ كانون الأول".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},meridiem:function(t){return 12>t?"ص":"م"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},preparse:function(t){return t.replace(/[۰-۹]/g,function(t){return i[t]}).replace(/،/g,",")},postformat:function(t){return t.replace(/\d/g,function(t){return n[t]}).replace(/,/g,"،")},week:{dow:6,doy:12}}),t.fullCalendar.datepickerLang("ar","ar",{closeText:"إغلاق",prevText:"<السابق",nextText:"التالي>",currentText:"اليوم",monthNames:["كانون الثاني","شباط","آذار","نيسان","مايو","حزيران","تموز","آب","أيلول","تشرين الأول","تشرين الثاني","كانون الأول"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesMin:["ح","ن","ث","ر","خ","ج","س"],weekHeader:"أسبوع",dateFormat:"dd/mm/yy",firstDay:6,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("ar",{defaultButtonText:{month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},allDayText:"اليوم كله",eventLimitText:"أخرى"})}); \ No newline at end of file diff --git a/dist/lang/bg.js b/dist/lang/bg.js new file mode 100644 index 0000000..e11e2ad --- /dev/null +++ b/dist/lang/bg.js @@ -0,0 +1 @@ +(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){(e.defineLocale||e.lang).call(e,"bg",{months:"януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември".split("_"),monthsShort:"янр_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек".split("_"),weekdays:"неделя_понеделник_вторник_сряда_четвъртък_петък_събота".split("_"),weekdaysShort:"нед_пон_вто_сря_чет_пет_съб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"H:mm",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Днес в] LT",nextDay:"[Утре в] LT",nextWeek:"dddd [в] LT",lastDay:"[Вчера в] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[В изминалата] dddd [в] LT";case 1:case 2:case 4:case 5:return"[В изминалия] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"след %s",past:"преди %s",s:"няколко секунди",m:"минута",mm:"%d минути",h:"час",hh:"%d часа",d:"ден",dd:"%d дни",M:"месец",MM:"%d месеца",y:"година",yy:"%d години"},ordinal:function(t){var e=t%10,n=t%100;return 0===t?t+"-ев":0===n?t+"-ен":n>10&&20>n?t+"-ти":1===e?t+"-ви":2===e?t+"-ри":7===e||8===e?t+"-ми":t+"-ти"},week:{dow:1,doy:7}}),t.fullCalendar.datepickerLang("bg","bg",{closeText:"затвори",prevText:"<назад",nextText:"напред>",nextBigText:">>",currentText:"днес",monthNames:["Януари","Февруари","Март","Април","Май","Юни","Юли","Август","Септември","Октомври","Ноември","Декември"],monthNamesShort:["Яну","Фев","Мар","Апр","Май","Юни","Юли","Авг","Сеп","Окт","Нов","Дек"],dayNames:["Неделя","Понеделник","Вторник","Сряда","Четвъртък","Петък","Събота"],dayNamesShort:["Нед","Пон","Вто","Сря","Чет","Пет","Съб"],dayNamesMin:["Не","По","Вт","Ср","Че","Пе","Съ"],weekHeader:"Wk",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("bg",{defaultButtonText:{month:"Месец",week:"Седмица",day:"Ден",list:"График"},allDayText:"Цял ден",eventLimitText:function(t){return"+още "+t}})}); \ No newline at end of file diff --git a/dist/lang/ca.js b/dist/lang/ca.js new file mode 100644 index 0000000..dc67a8a --- /dev/null +++ b/dist/lang/ca.js @@ -0,0 +1 @@ +(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){(e.defineLocale||e.lang).call(e,"ca",{months:"gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre".split("_"),monthsShort:"gen._febr._mar._abr._mai._jun._jul._ag._set._oct._nov._des.".split("_"),weekdays:"diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dt._dc._dj._dv._ds.".split("_"),weekdaysMin:"Dg_Dl_Dt_Dc_Dj_Dv_Ds".split("_"),longDateFormat:{LT:"H:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:function(){return"[avui a "+(1!==this.hours()?"les":"la")+"] LT"},nextDay:function(){return"[demà a "+(1!==this.hours()?"les":"la")+"] LT"},nextWeek:function(){return"dddd [a "+(1!==this.hours()?"les":"la")+"] LT"},lastDay:function(){return"[ahir a "+(1!==this.hours()?"les":"la")+"] LT"},lastWeek:function(){return"[el] dddd [passat a "+(1!==this.hours()?"les":"la")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"fa %s",s:"uns segons",m:"un minut",mm:"%d minuts",h:"una hora",hh:"%d hores",d:"un dia",dd:"%d dies",M:"un mes",MM:"%d mesos",y:"un any",yy:"%d anys"},ordinal:"%dº",week:{dow:1,doy:4}}),t.fullCalendar.datepickerLang("ca","ca",{closeText:"Tanca",prevText:"Anterior",nextText:"Següent",currentText:"Avui",monthNames:["gener","febrer","març","abril","maig","juny","juliol","agost","setembre","octubre","novembre","desembre"],monthNamesShort:["gen","feb","març","abr","maig","juny","jul","ag","set","oct","nov","des"],dayNames:["diumenge","dilluns","dimarts","dimecres","dijous","divendres","dissabte"],dayNamesShort:["dg","dl","dt","dc","dj","dv","ds"],dayNamesMin:["dg","dl","dt","dc","dj","dv","ds"],weekHeader:"Set",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("ca",{defaultButtonText:{month:"Mes",week:"Setmana",day:"Dia",list:"Agenda"},allDayText:"Tot el dia",eventLimitText:"més"})}); \ No newline at end of file diff --git a/dist/lang/cs.js b/dist/lang/cs.js new file mode 100644 index 0000000..0e7f002 --- /dev/null +++ b/dist/lang/cs.js @@ -0,0 +1 @@ +(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){function n(t){return t>1&&5>t&&1!==~~(t/10)}function i(t,e,i,r){var s=t+" ";switch(i){case"s":return e||r?"pár sekund":"pár sekundami";case"m":return e?"minuta":r?"minutu":"minutou";case"mm":return e||r?s+(n(t)?"minuty":"minut"):s+"minutami";case"h":return e?"hodina":r?"hodinu":"hodinou";case"hh":return e||r?s+(n(t)?"hodiny":"hodin"):s+"hodinami";case"d":return e||r?"den":"dnem";case"dd":return e||r?s+(n(t)?"dny":"dní"):s+"dny";case"M":return e||r?"měsíc":"měsícem";case"MM":return e||r?s+(n(t)?"měsíce":"měsíců"):s+"měsíci";case"y":return e||r?"rok":"rokem";case"yy":return e||r?s+(n(t)?"roky":"let"):s+"lety"}}var r="leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec".split("_"),s="led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro".split("_");(e.defineLocale||e.lang).call(e,"cs",{months:r,monthsShort:s,monthsParse:function(t,e){var n,i=[];for(n=0;12>n;n++)i[n]=RegExp("^"+t[n]+"$|^"+e[n]+"$","i");return i}(r,s),weekdays:"neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota".split("_"),weekdaysShort:"ne_po_út_st_čt_pá_so".split("_"),weekdaysMin:"ne_po_út_st_čt_pá_so".split("_"),longDateFormat:{LT:"H.mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd D. MMMM YYYY LT"},calendar:{sameDay:"[dnes v] LT",nextDay:"[zítra v] LT",nextWeek:function(){switch(this.day()){case 0:return"[v neděli v] LT";case 1:case 2:return"[v] dddd [v] LT";case 3:return"[ve středu v] LT";case 4:return"[ve čtvrtek v] LT";case 5:return"[v pátek v] LT";case 6:return"[v sobotu v] LT"}},lastDay:"[včera v] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulou neděli v] LT";case 1:case 2:return"[minulé] dddd [v] LT";case 3:return"[minulou středu v] LT";case 4:case 5:return"[minulý] dddd [v] LT";case 6:return"[minulou sobotu v] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"před %s",s:i,m:i,mm:i,h:i,hh:i,d:i,dd:i,M:i,MM:i,y:i,yy:i},ordinal:"%d.",week:{dow:1,doy:4}}),t.fullCalendar.datepickerLang("cs","cs",{closeText:"Zavřít",prevText:"<Dříve",nextText:"Později>",currentText:"Nyní",monthNames:["leden","únor","březen","duben","květen","červen","červenec","srpen","září","říjen","listopad","prosinec"],monthNamesShort:["led","úno","bře","dub","kvě","čer","čvc","srp","zář","říj","lis","pro"],dayNames:["neděle","pondělí","úterý","středa","čtvrtek","pátek","sobota"],dayNamesShort:["ne","po","út","st","čt","pá","so"],dayNamesMin:["ne","po","út","st","čt","pá","so"],weekHeader:"Týd",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("cs",{defaultButtonText:{month:"Měsíc",week:"Týden",day:"Den",list:"Agenda"},allDayText:"Celý den",eventLimitText:function(t){return"+další: "+t}})}); \ No newline at end of file diff --git a/dist/lang/da.js b/dist/lang/da.js new file mode 100644 index 0000000..637b54e --- /dev/null +++ b/dist/lang/da.js @@ -0,0 +1 @@ +(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){(e.defineLocale||e.lang).call(e,"da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag".split("_"),weekdaysShort:"søn_man_tir_ons_tor_fre_lør".split("_"),weekdaysMin:"sø_ma_ti_on_to_fr_lø".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd [d.] D. MMMM YYYY LT"},calendar:{sameDay:"[I dag kl.] LT",nextDay:"[I morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[I går kl.] LT",lastWeek:"[sidste] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"få sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en måned",MM:"%d måneder",y:"et år",yy:"%d år"},ordinal:"%d.",week:{dow:1,doy:4}}),t.fullCalendar.datepickerLang("da","da",{closeText:"Luk",prevText:"<Forrige",nextText:"Næste>",currentText:"Idag",monthNames:["Januar","Februar","Marts","April","Maj","Juni","Juli","August","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNames:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag"],dayNamesShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],dayNamesMin:["Sø","Ma","Ti","On","To","Fr","Lø"],weekHeader:"Uge",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("da",{defaultButtonText:{month:"Måned",week:"Uge",day:"Dag",list:"Agenda"},allDayText:"Hele dagen",eventLimitText:"flere"})}); \ No newline at end of file diff --git a/dist/lang/de-at.js b/dist/lang/de-at.js new file mode 100644 index 0000000..dbd8562 --- /dev/null +++ b/dist/lang/de-at.js @@ -0,0 +1 @@ +(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){function n(t,e,n){var i={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[t+" Tage",t+" Tagen"],M:["ein Monat","einem Monat"],MM:[t+" Monate",t+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[t+" Jahre",t+" Jahren"]};return e?i[n][0]:i[n][1]}(e.defineLocale||e.lang).call(e,"de-at",{months:"Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jän._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm [Uhr]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Heute um] LT",sameElse:"L",nextDay:"[Morgen um] LT",nextWeek:"dddd [um] LT",lastDay:"[Gestern um] LT",lastWeek:"[letzten] dddd [um] LT"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:n,mm:"%d Minuten",h:n,hh:"%d Stunden",d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinal:"%d.",week:{dow:1,doy:4}}),t.fullCalendar.datepickerLang("de-at","de",{closeText:"Schließen",prevText:"<Zurück",nextText:"Vor>",currentText:"Heute",monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayNamesMin:["So","Mo","Di","Mi","Do","Fr","Sa"],weekHeader:"KW",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("de-at",{defaultButtonText:{month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},allDayText:"Ganztägig",eventLimitText:function(t){return"+ weitere "+t}})}); \ No newline at end of file diff --git a/dist/lang/de.js b/dist/lang/de.js new file mode 100644 index 0000000..a79c3ea --- /dev/null +++ b/dist/lang/de.js @@ -0,0 +1 @@ +(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){function n(t,e,n){var i={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[t+" Tage",t+" Tagen"],M:["ein Monat","einem Monat"],MM:[t+" Monate",t+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[t+" Jahre",t+" Jahren"]};return e?i[n][0]:i[n][1]}(e.defineLocale||e.lang).call(e,"de",{months:"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm [Uhr]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Heute um] LT",sameElse:"L",nextDay:"[Morgen um] LT",nextWeek:"dddd [um] LT",lastDay:"[Gestern um] LT",lastWeek:"[letzten] dddd [um] LT"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:n,mm:"%d Minuten",h:n,hh:"%d Stunden",d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinal:"%d.",week:{dow:1,doy:4}}),t.fullCalendar.datepickerLang("de","de",{closeText:"Schließen",prevText:"<Zurück",nextText:"Vor>",currentText:"Heute",monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayNamesMin:["So","Mo","Di","Mi","Do","Fr","Sa"],weekHeader:"KW",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("de",{defaultButtonText:{month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},allDayText:"Ganztägig",eventLimitText:function(t){return"+ weitere "+t}})}); \ No newline at end of file diff --git a/dist/lang/el.js b/dist/lang/el.js new file mode 100644 index 0000000..96ee518 --- /dev/null +++ b/dist/lang/el.js @@ -0,0 +1 @@ +(function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){(e.defineLocale||e.lang).call(e,"el",{monthsNominativeEl:"Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος".split("_"),monthsGenitiveEl:"Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου".split("_"),months:function(t,e){return/D/.test(e.substring(0,e.indexOf("MMMM")))?this._monthsGenitiveEl[t.month()]:this._monthsNominativeEl[t.month()]},monthsShort:"Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ".split("_"),weekdays:"Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο".split("_"),weekdaysShort:"Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ".split("_"),weekdaysMin:"Κυ_Δε_Τρ_Τε_Πε_Πα_Σα".split("_"),meridiem:function(t,e,n){return t>11?n?"μμ":"ΜΜ":n?"πμ":"ΠΜ"},longDateFormat:{LT:"h:mm A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendarEl:{sameDay:"[Σήμερα {}] LT",nextDay:"[Αύριο {}] LT",nextWeek:"dddd [{}] LT",lastDay:"[Χθες {}] LT",lastWeek:function(){switch(this.day()){case 6:return"[το προηγούμενο] dddd [{}] LT";default:return"[την προηγούμενη] dddd [{}] LT"}},sameElse:"L"},calendar:function(t,e){var n=this._calendarEl[t],i=e&&e.hours();return"function"==typeof n&&(n=n.apply(e)),n.replace("{}",1===i%12?"στη":"στις")},relativeTime:{future:"σε %s",past:"%s πριν",s:"δευτερόλεπτα",m:"ένα λεπτό",mm:"%d λεπτά",h:"μία ώρα",hh:"%d ώρες",d:"μία μέρα",dd:"%d μέρες",M:"ένας μήνας",MM:"%d μήνες",y:"ένας χρόνος",yy:"%d χρόνια"},ordinal:function(t){return t+"η"},week:{dow:1,doy:4}}),t.fullCalendar.datepickerLang("el","el",{closeText:"Κλείσιμο",prevText:"Προηγούμενος",nextText:"Επόμενος",currentText:"Τρέχων Μήνας",monthNames:["Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος","Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος"],monthNamesShort:["Ιαν","Φεβ","Μαρ","Απρ","Μαι","Ιουν","Ιουλ","Αυγ","Σεπ","Οκτ","Νοε","Δεκ"],dayNames:["Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο"],dayNamesShort:["Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ"],dayNamesMin:["Κυ","Δε","Τρ","Τε","Πε","Πα","Σα"],weekHeader:"Εβδ",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("el",{defaultButtonText:{month:"Μήνας",week:"Εβδομάδα",day:"Ημέρα",list:"Ατζέντα"},allDayText:"Ολοήμερο",eventLimitText:"περισσότερα"})}); \ No newline at end of file diff --git a/dist/lang/en-au.js b/dist/lang/en-au.js new file mode 100644 index 0000000..a22e013 --- /dev/null +++ b/dist/lang/en-au.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"en-au",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinal:function(e){var t=e%10,n=1===~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("en-au","en-AU",{closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",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"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("en-au")}); \ No newline at end of file diff --git a/dist/lang/en-ca.js b/dist/lang/en-ca.js new file mode 100644 index 0000000..5b8a13a --- /dev/null +++ b/dist/lang/en-ca.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"en-ca",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",L:"YYYY-MM-DD",LL:"D MMMM, YYYY",LLL:"D MMMM, YYYY LT",LLLL:"dddd, D MMMM, YYYY LT"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinal:function(e){var t=e%10,n=1===~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n}}),e.fullCalendar.lang("en-ca")}); \ No newline at end of file diff --git a/dist/lang/en-gb.js b/dist/lang/en-gb.js new file mode 100644 index 0000000..54d2b95 --- /dev/null +++ b/dist/lang/en-gb.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"en-gb",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},ordinal:function(e){var t=e%10,n=1===~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("en-gb","en-GB",{closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",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"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("en-gb",{columnFormat:{week:"ddd D/M"}})}); \ No newline at end of file diff --git a/dist/lang/es.js b/dist/lang/es.js new file mode 100644 index 0000000..c4af976 --- /dev/null +++ b/dist/lang/es.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),i="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_");(t.defineLocale||t.lang).call(t,"es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){return/-MMM-/.test(t)?i[e.month()]:n[e.month()]},weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"Do_Lu_Ma_Mi_Ju_Vi_Sá".split("_"),longDateFormat:{LT:"H:mm",L:"DD/MM/YYYY",LL:"D [de] MMMM [del] YYYY",LLL:"D [de] MMMM [del] YYYY LT",LLLL:"dddd, D [de] MMMM [del] YYYY LT"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("es","es",{closeText:"Cerrar",prevText:"<Ant",nextText:"Sig>",currentText:"Hoy",monthNames:["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],monthNamesShort:["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"],dayNames:["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],dayNamesShort:["dom","lun","mar","mié","jue","vie","sáb"],dayNamesMin:["D","L","M","X","J","V","S"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("es",{defaultButtonText:{month:"Mes",week:"Semana",day:"Día",list:"Agenda"},allDayHtml:"Todo
el día",eventLimitText:"más"})}); \ No newline at end of file diff --git a/dist/lang/fa.js b/dist/lang/fa.js new file mode 100644 index 0000000..4a73051 --- /dev/null +++ b/dist/lang/fa.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n={1:"۱",2:"۲",3:"۳",4:"۴",5:"۵",6:"۶",7:"۷",8:"۸",9:"۹",0:"۰"},i={"۱":"1","۲":"2","۳":"3","۴":"4","۵":"5","۶":"6","۷":"7","۸":"8","۹":"9","۰":"0"};(t.defineLocale||t.lang).call(t,"fa",{months:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),monthsShort:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),weekdays:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysShort:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysMin:"ی_د_س_چ_پ_ج_ش".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},meridiem:function(e){return 12>e?"قبل از ظهر":"بعد از ظهر"},calendar:{sameDay:"[امروز ساعت] LT",nextDay:"[فردا ساعت] LT",nextWeek:"dddd [ساعت] LT",lastDay:"[دیروز ساعت] LT",lastWeek:"dddd [پیش] [ساعت] LT",sameElse:"L"},relativeTime:{future:"در %s",past:"%s پیش",s:"چندین ثانیه",m:"یک دقیقه",mm:"%d دقیقه",h:"یک ساعت",hh:"%d ساعت",d:"یک روز",dd:"%d روز",M:"یک ماه",MM:"%d ماه",y:"یک سال",yy:"%d سال"},preparse:function(e){return e.replace(/[۰-۹]/g,function(e){return i[e]}).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return n[e]}).replace(/,/g,"،")},ordinal:"%dم",week:{dow:6,doy:12}}),e.fullCalendar.datepickerLang("fa","fa",{closeText:"بستن",prevText:"<قبلی",nextText:"بعدی>",currentText:"امروز",monthNames:["فروردين","ارديبهشت","خرداد","تير","مرداد","شهريور","مهر","آبان","آذر","دی","بهمن","اسفند"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["يکشنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"],dayNamesShort:["ی","د","س","چ","پ","ج","ش"],dayNamesMin:["ی","د","س","چ","پ","ج","ش"],weekHeader:"هف",dateFormat:"yy/mm/dd",firstDay:6,isRTL:!0,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fa",{defaultButtonText:{month:"ماه",week:"هفته",day:"روز",list:"برنامه"},allDayText:"تمام روز",eventLimitText:function(e){return"بیش از "+e}})}); \ No newline at end of file diff --git a/dist/lang/fi.js b/dist/lang/fi.js new file mode 100644 index 0000000..c73045c --- /dev/null +++ b/dist/lang/fi.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n,r){var o="";switch(n){case"s":return r?"muutaman sekunnin":"muutama sekunti";case"m":return r?"minuutin":"minuutti";case"mm":o=r?"minuutin":"minuuttia";break;case"h":return r?"tunnin":"tunti";case"hh":o=r?"tunnin":"tuntia";break;case"d":return r?"päivän":"päivä";case"dd":o=r?"päivän":"päivää";break;case"M":return r?"kuukauden":"kuukausi";case"MM":o=r?"kuukauden":"kuukautta";break;case"y":return r?"vuoden":"vuosi";case"yy":o=r?"vuoden":"vuotta"}return o=i(e,r)+" "+o}function i(e,t){return 10>e?t?o[e]:r[e]:e}var r="nolla yksi kaksi kolme neljä viisi kuusi seitsemän kahdeksan yhdeksän".split(" "),o=["nolla","yhden","kahden","kolmen","neljän","viiden","kuuden",r[7],r[8],r[9]];(t.defineLocale||t.lang).call(t,"fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kesäkuu_heinäkuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kesä_heinä_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] LT",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] LT",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] LT",llll:"ddd, Do MMM YYYY, [klo] LT"},calendar:{sameDay:"[tänään] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s päästä",past:"%s sitten",s:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("fi","fi",{closeText:"Sulje",prevText:"«Edellinen",nextText:"Seuraava»",currentText:"Tänään",monthNames:["Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu","Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu"],monthNamesShort:["Tammi","Helmi","Maalis","Huhti","Touko","Kesä","Heinä","Elo","Syys","Loka","Marras","Joulu"],dayNamesShort:["Su","Ma","Ti","Ke","To","Pe","La"],dayNames:["Sunnuntai","Maanantai","Tiistai","Keskiviikko","Torstai","Perjantai","Lauantai"],dayNamesMin:["Su","Ma","Ti","Ke","To","Pe","La"],weekHeader:"Vk",dateFormat:"d.m.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fi",{defaultButtonText:{month:"Kuukausi",week:"Viikko",day:"Päivä",list:"Tapahtumat"},allDayText:"Koko päivä",eventLimitText:"lisää"})}); \ No newline at end of file diff --git a/dist/lang/fr-ca.js b/dist/lang/fr-ca.js new file mode 100644 index 0000000..f5b4066 --- /dev/null +++ b/dist/lang/fr-ca.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"fr-ca",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinal:function(e){return e+(1===e?"er":"")}}),e.fullCalendar.datepickerLang("fr-ca","fr-CA",{closeText:"Fermer",prevText:"Précédent",nextText:"Suivant",currentText:"Aujourd'hui",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sem.",dateFormat:"yy-mm-dd",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fr-ca",{defaultButtonText:{month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},allDayHtml:"Toute la
journée",eventLimitText:"en plus"})}); \ No newline at end of file diff --git a/dist/lang/fr.js b/dist/lang/fr.js new file mode 100644 index 0000000..9c024c0 --- /dev/null +++ b/dist/lang/fr.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"fr",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinal:function(e){return e+(1===e?"er":"")},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("fr","fr",{closeText:"Fermer",prevText:"Précédent",nextText:"Suivant",currentText:"Aujourd'hui",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sem.",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fr",{defaultButtonText:{month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},allDayHtml:"Toute la
journée",eventLimitText:"en plus"})}); \ No newline at end of file diff --git a/dist/lang/hi.js b/dist/lang/hi.js new file mode 100644 index 0000000..45713ca --- /dev/null +++ b/dist/lang/hi.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n={1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"},i={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"};(t.defineLocale||t.lang).call(t,"hi",{months:"जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर".split("_"),monthsShort:"जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.".split("_"),weekdays:"रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि".split("_"),weekdaysMin:"र_सो_मं_बु_गु_शु_श".split("_"),longDateFormat:{LT:"A h:mm बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, LT",LLLL:"dddd, D MMMM YYYY, LT"},calendar:{sameDay:"[आज] LT",nextDay:"[कल] LT",nextWeek:"dddd, LT",lastDay:"[कल] LT",lastWeek:"[पिछले] dddd, LT",sameElse:"L"},relativeTime:{future:"%s में",past:"%s पहले",s:"कुछ ही क्षण",m:"एक मिनट",mm:"%d मिनट",h:"एक घंटा",hh:"%d घंटे",d:"एक दिन",dd:"%d दिन",M:"एक महीने",MM:"%d महीने",y:"एक वर्ष",yy:"%d वर्ष"},preparse:function(e){return e.replace(/[१२३४५६७८९०]/g,function(e){return i[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return n[e]})},meridiem:function(e){return 4>e?"रात":10>e?"सुबह":17>e?"दोपहर":20>e?"शाम":"रात"},week:{dow:0,doy:6}}),e.fullCalendar.datepickerLang("hi","hi",{closeText:"बंद",prevText:"पिछला",nextText:"अगला",currentText:"आज",monthNames:["जनवरी ","फरवरी","मार्च","अप्रेल","मई","जून","जूलाई","अगस्त ","सितम्बर","अक्टूबर","नवम्बर","दिसम्बर"],monthNamesShort:["जन","फर","मार्च","अप्रेल","मई","जून","जूलाई","अग","सित","अक्ट","नव","दि"],dayNames:["रविवार","सोमवार","मंगलवार","बुधवार","गुरुवार","शुक्रवार","शनिवार"],dayNamesShort:["रवि","सोम","मंगल","बुध","गुरु","शुक्र","शनि"],dayNamesMin:["रवि","सोम","मंगल","बुध","गुरु","शुक्र","शनि"],weekHeader:"हफ्ता",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("hi",{defaultButtonText:{month:"महीना",week:"सप्ताह",day:"दिन",list:"कार्यसूची"},allDayText:"सभी दिन",eventLimitText:function(e){return"+अधिक "+e}})}); \ No newline at end of file diff --git a/dist/lang/hr.js b/dist/lang/hr.js new file mode 100644 index 0000000..6e36a77 --- /dev/null +++ b/dist/lang/hr.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n){var i=e+" ";switch(n){case"m":return t?"jedna minuta":"jedne minute";case"mm":return i+=1===e?"minuta":2===e||3===e||4===e?"minute":"minuta";case"h":return t?"jedan sat":"jednog sata";case"hh":return i+=1===e?"sat":2===e||3===e||4===e?"sata":"sati";case"dd":return i+=1===e?"dan":"dana";case"MM":return i+=1===e?"mjesec":2===e||3===e||4===e?"mjeseca":"mjeseci";case"yy":return i+=1===e?"godina":2===e||3===e||4===e?"godine":"godina"}}(t.defineLocale||t.lang).call(t,"hr",{months:"sječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac".split("_"),monthsShort:"sje._vel._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.".split("_"),weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),longDateFormat:{LT:"H:mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[prošlu] dddd [u] LT";case 6:return"[prošle] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[prošli] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",m:n,mm:n,h:n,hh:n,d:"dan",dd:n,M:"mjesec",MM:n,y:"godinu",yy:n},ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("hr","hr",{closeText:"Zatvori",prevText:"<",nextText:">",currentText:"Danas",monthNames:["Siječanj","Veljača","Ožujak","Travanj","Svibanj","Lipanj","Srpanj","Kolovoz","Rujan","Listopad","Studeni","Prosinac"],monthNamesShort:["Sij","Velj","Ožu","Tra","Svi","Lip","Srp","Kol","Ruj","Lis","Stu","Pro"],dayNames:["Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota"],dayNamesShort:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"],dayNamesMin:["Ne","Po","Ut","Sr","Če","Pe","Su"],weekHeader:"Tje",dateFormat:"dd.mm.yy.",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("hr",{defaultButtonText:{month:"Mjesec",week:"Tjedan",day:"Dan",list:"Raspored"},allDayText:"Cijeli dan",eventLimitText:function(e){return"+ još "+e}})}); \ No newline at end of file diff --git a/dist/lang/hu.js b/dist/lang/hu.js new file mode 100644 index 0000000..44a1b74 --- /dev/null +++ b/dist/lang/hu.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n,i){var r=e;switch(n){case"s":return i||t?"néhány másodperc":"néhány másodperce";case"m":return"egy"+(i||t?" perc":" perce");case"mm":return r+(i||t?" perc":" perce");case"h":return"egy"+(i||t?" óra":" órája");case"hh":return r+(i||t?" óra":" órája");case"d":return"egy"+(i||t?" nap":" napja");case"dd":return r+(i||t?" nap":" napja");case"M":return"egy"+(i||t?" hónap":" hónapja");case"MM":return r+(i||t?" hónap":" hónapja");case"y":return"egy"+(i||t?" év":" éve");case"yy":return r+(i||t?" év":" éve")}return""}function i(e){return(e?"":"[múlt] ")+"["+r[this.day()]+"] LT[-kor]"}var r="vasárnap hétfőn kedden szerdán csütörtökön pénteken szombaton".split(" ");(t.defineLocale||t.lang).call(t,"hu",{months:"január_február_március_április_május_június_július_augusztus_szeptember_október_november_december".split("_"),monthsShort:"jan_feb_márc_ápr_máj_jún_júl_aug_szept_okt_nov_dec".split("_"),weekdays:"vasárnap_hétfő_kedd_szerda_csütörtök_péntek_szombat".split("_"),weekdaysShort:"vas_hét_kedd_sze_csüt_pén_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D., LT",LLLL:"YYYY. MMMM D., dddd LT"},meridiem:function(e,t,n){return 12>e?n===!0?"de":"DE":n===!0?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return i.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return i.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s múlva",past:"%s",s:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("hu","hu",{closeText:"bezár",prevText:"vissza",nextText:"előre",currentText:"ma",monthNames:["Január","Február","Március","Április","Május","Június","Július","Augusztus","Szeptember","Október","November","December"],monthNamesShort:["Jan","Feb","Már","Ápr","Máj","Jún","Júl","Aug","Szep","Okt","Nov","Dec"],dayNames:["Vasárnap","Hétfő","Kedd","Szerda","Csütörtök","Péntek","Szombat"],dayNamesShort:["Vas","Hét","Ked","Sze","Csü","Pén","Szo"],dayNamesMin:["V","H","K","Sze","Cs","P","Szo"],weekHeader:"Hét",dateFormat:"yy.mm.dd.",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:""}),e.fullCalendar.lang("hu",{defaultButtonText:{month:"Hónap",week:"Hét",day:"Nap",list:"Napló"},allDayText:"Egész nap",eventLimitText:"további"})}); \ No newline at end of file diff --git a/dist/lang/id.js b/dist/lang/id.js new file mode 100644 index 0000000..4bdc3d7 --- /dev/null +++ b/dist/lang/id.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"id",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nov_Des".split("_"),weekdays:"Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),weekdaysShort:"Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] LT",LLLL:"dddd, D MMMM YYYY [pukul] LT"},meridiem:function(e){return 11>e?"pagi":15>e?"siang":19>e?"sore":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Besok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kemarin pukul] LT",lastWeek:"dddd [lalu pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lalu",s:"beberapa detik",m:"semenit",mm:"%d menit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("id","id",{closeText:"Tutup",prevText:"<mundur",nextText:"maju>",currentText:"hari ini",monthNames:["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","Nopember","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agus","Sep","Okt","Nop","Des"],dayNames:["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"],dayNamesShort:["Min","Sen","Sel","Rab","kam","Jum","Sab"],dayNamesMin:["Mg","Sn","Sl","Rb","Km","jm","Sb"],weekHeader:"Mg",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("id",{defaultButtonText:{month:"Bulan",week:"Minggu",day:"Hari",list:"Agenda"},allDayHtml:"Sehari
penuh",eventLimitText:"lebih"})}); \ No newline at end of file diff --git a/dist/lang/is.js b/dist/lang/is.js new file mode 100644 index 0000000..7bbdebf --- /dev/null +++ b/dist/lang/is.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e){return 11===e%100?!0:1===e%10?!1:!0}function i(e,t,i,r){var a=e+" ";switch(i){case"s":return t||r?"nokkrar sekúndur":"nokkrum sekúndum";case"m":return t?"mínúta":"mínútu";case"mm":return n(e)?a+(t||r?"mínútur":"mínútum"):t?a+"mínúta":a+"mínútu";case"hh":return n(e)?a+(t||r?"klukkustundir":"klukkustundum"):a+"klukkustund";case"d":return t?"dagur":r?"dag":"degi";case"dd":return n(e)?t?a+"dagar":a+(r?"daga":"dögum"):t?a+"dagur":a+(r?"dag":"degi");case"M":return t?"mánuður":r?"mánuð":"mánuði";case"MM":return n(e)?t?a+"mánuðir":a+(r?"mánuði":"mánuðum"):t?a+"mánuður":a+(r?"mánuð":"mánuði");case"y":return t||r?"ár":"ári";case"yy":return n(e)?a+(t||r?"ár":"árum"):a+(t||r?"ár":"ári")}}(t.defineLocale||t.lang).call(t,"is",{months:"janúar_febrúar_mars_apríl_maí_júní_júlí_ágúst_september_október_nóvember_desember".split("_"),monthsShort:"jan_feb_mar_apr_maí_jún_júl_ágú_sep_okt_nóv_des".split("_"),weekdays:"sunnudagur_mánudagur_þriðjudagur_miðvikudagur_fimmtudagur_föstudagur_laugardagur".split("_"),weekdaysShort:"sun_mán_þri_mið_fim_fös_lau".split("_"),weekdaysMin:"Su_Má_Þr_Mi_Fi_Fö_La".split("_"),longDateFormat:{LT:"H:mm",L:"DD/MM/YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] LT",LLLL:"dddd, D. MMMM YYYY [kl.] LT"},calendar:{sameDay:"[í dag kl.] LT",nextDay:"[á morgun kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[í gær kl.] LT",lastWeek:"[síðasta] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"eftir %s",past:"fyrir %s síðan",s:i,m:i,mm:i,h:"klukkustund",hh:i,d:i,dd:i,M:i,MM:i,y:i,yy:i},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("is","is",{closeText:"Loka",prevText:"< Fyrri",nextText:"Næsti >",currentText:"Í dag",monthNames:["Janúar","Febrúar","Mars","Apríl","Maí","Júní","Júlí","Ágúst","September","Október","Nóvember","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","Maí","Jún","Júl","Ágú","Sep","Okt","Nóv","Des"],dayNames:["Sunnudagur","Mánudagur","Þriðjudagur","Miðvikudagur","Fimmtudagur","Föstudagur","Laugardagur"],dayNamesShort:["Sun","Mán","Þri","Mið","Fim","Fös","Lau"],dayNamesMin:["Su","Má","Þr","Mi","Fi","Fö","La"],weekHeader:"Vika",dateFormat:"dd.mm.yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("is",{defaultButtonText:{month:"Mánuður",week:"Vika",day:"Dagur",list:"Dagskrá"},allDayHtml:"Allan
daginn",eventLimitText:"meira"})}); \ No newline at end of file diff --git a/dist/lang/it.js b/dist/lang/it.js new file mode 100644 index 0000000..629d86d --- /dev/null +++ b/dist/lang/it.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"Domenica_Lunedì_Martedì_Mercoledì_Giovedì_Venerdì_Sabato".split("_"),weekdaysShort:"Dom_Lun_Mar_Mer_Gio_Ven_Sab".split("_"),weekdaysMin:"D_L_Ma_Me_G_V_S".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:"[lo scorso] dddd [alle] LT",sameElse:"L"},relativeTime:{future:function(e){return(/^[0-9].+$/.test(e)?"tra":"in")+" "+e},past:"%s fa",s:"alcuni secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("it","it",{closeText:"Chiudi",prevText:"<Prec",nextText:"Succ>",currentText:"Oggi",monthNames:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],monthNamesShort:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],dayNames:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],dayNamesShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],dayNamesMin:["Do","Lu","Ma","Me","Gi","Ve","Sa"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("it",{defaultButtonText:{month:"Mese",week:"Settimana",day:"Giorno",list:"Agenda"},allDayHtml:"Tutto il
giorno",eventLimitText:function(e){return"+altri "+e}})}); \ No newline at end of file diff --git a/dist/lang/ja.js b/dist/lang/ja.js new file mode 100644 index 0000000..5552780 --- /dev/null +++ b/dist/lang/ja.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"ja",{months:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"),weekdaysShort:"日_月_火_水_木_金_土".split("_"),weekdaysMin:"日_月_火_水_木_金_土".split("_"),longDateFormat:{LT:"Ah時m分",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日LT",LLLL:"YYYY年M月D日LT dddd"},meridiem:function(e){return 12>e?"午前":"午後"},calendar:{sameDay:"[今日] LT",nextDay:"[明日] LT",nextWeek:"[来週]dddd LT",lastDay:"[昨日] LT",lastWeek:"[前週]dddd LT",sameElse:"L"},relativeTime:{future:"%s後",past:"%s前",s:"数秒",m:"1分",mm:"%d分",h:"1時間",hh:"%d時間",d:"1日",dd:"%d日",M:"1ヶ月",MM:"%dヶ月",y:"1年",yy:"%d年"}}),e.fullCalendar.datepickerLang("ja","ja",{closeText:"閉じる",prevText:"<前",nextText:"次>",currentText:"今日",monthNames:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],monthNamesShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayNames:["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"],dayNamesShort:["日","月","火","水","木","金","土"],dayNamesMin:["日","月","火","水","木","金","土"],weekHeader:"週",dateFormat:"yy/mm/dd",firstDay:0,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"年"}),e.fullCalendar.lang("ja",{defaultButtonText:{month:"月",week:"週",day:"日",list:"予定リスト"},allDayText:"終日",eventLimitText:function(e){return"他 "+e+" 件"}})}); \ No newline at end of file diff --git a/dist/lang/ko.js b/dist/lang/ko.js new file mode 100644 index 0000000..0a6d902 --- /dev/null +++ b/dist/lang/ko.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"ko",{months:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),monthsShort:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),weekdays:"일요일_월요일_화요일_수요일_목요일_금요일_토요일".split("_"),weekdaysShort:"일_월_화_수_목_금_토".split("_"),weekdaysMin:"일_월_화_수_목_금_토".split("_"),longDateFormat:{LT:"A h시 mm분",L:"YYYY.MM.DD",LL:"YYYY년 MMMM D일",LLL:"YYYY년 MMMM D일 LT",LLLL:"YYYY년 MMMM D일 dddd LT"},meridiem:function(e){return 12>e?"오전":"오후"},calendar:{sameDay:"오늘 LT",nextDay:"내일 LT",nextWeek:"dddd LT",lastDay:"어제 LT",lastWeek:"지난주 dddd LT",sameElse:"L"},relativeTime:{future:"%s 후",past:"%s 전",s:"몇초",ss:"%d초",m:"일분",mm:"%d분",h:"한시간",hh:"%d시간",d:"하루",dd:"%d일",M:"한달",MM:"%d달",y:"일년",yy:"%d년"},ordinal:"%d일",meridiemParse:/(오전|오후)/,isPM:function(e){return"오후"===e}}),e.fullCalendar.datepickerLang("ko","ko",{closeText:"닫기",prevText:"이전달",nextText:"다음달",currentText:"오늘",monthNames:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],monthNamesShort:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],dayNames:["일요일","월요일","화요일","수요일","목요일","금요일","토요일"],dayNamesShort:["일","월","화","수","목","금","토"],dayNamesMin:["일","월","화","수","목","금","토"],weekHeader:"Wk",dateFormat:"yy-mm-dd",firstDay:0,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"년"}),e.fullCalendar.lang("ko",{defaultButtonText:{month:"월",week:"주",day:"일",list:"일정목록"},allDayText:"종일",eventLimitText:"개"})}); \ No newline at end of file diff --git a/dist/lang/lt.js b/dist/lang/lt.js new file mode 100644 index 0000000..d379e55 --- /dev/null +++ b/dist/lang/lt.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n,i){return t?"kelios sekundės":i?"kelių sekundžių":"kelias sekundes"}function i(e,t,n,i){return t?a(n)[0]:i?a(n)[1]:a(n)[2]}function r(e){return 0===e%10||e>10&&20>e}function a(e){return l[e].split("_")}function s(e,t,n,s){var o=e+" ";return 1===e?o+i(e,t,n[0],s):t?o+(r(e)?a(n)[1]:a(n)[0]):s?o+a(n)[1]:o+(r(e)?a(n)[1]:a(n)[2])}function o(e,t){var n=-1===t.indexOf("dddd HH:mm"),i=d[e.day()];return n?i:i.substring(0,i.length-2)+"į"}var l={m:"minutė_minutės_minutę",mm:"minutės_minučių_minutes",h:"valanda_valandos_valandą",hh:"valandos_valandų_valandas",d:"diena_dienos_dieną",dd:"dienos_dienų_dienas",M:"mėnuo_mėnesio_mėnesį",MM:"mėnesiai_mėnesių_mėnesius",y:"metai_metų_metus",yy:"metai_metų_metus"},d="sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_šeštadienis".split("_");(t.defineLocale||t.lang).call(t,"lt",{months:"sausio_vasario_kovo_balandžio_gegužės_birželio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio".split("_"),monthsShort:"sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd".split("_"),weekdays:o,weekdaysShort:"Sek_Pir_Ant_Tre_Ket_Pen_Šeš".split("_"),weekdaysMin:"S_P_A_T_K_Pn_Š".split("_"),longDateFormat:{LT:"HH:mm",L:"YYYY-MM-DD",LL:"YYYY [m.] MMMM D [d.]",LLL:"YYYY [m.] MMMM D [d.], LT [val.]",LLLL:"YYYY [m.] MMMM D [d.], dddd, LT [val.]",l:"YYYY-MM-DD",ll:"YYYY [m.] MMMM D [d.]",lll:"YYYY [m.] MMMM D [d.], LT [val.]",llll:"YYYY [m.] MMMM D [d.], ddd, LT [val.]"},calendar:{sameDay:"[Šiandien] LT",nextDay:"[Rytoj] LT",nextWeek:"dddd LT",lastDay:"[Vakar] LT",lastWeek:"[Praėjusį] dddd LT",sameElse:"L"},relativeTime:{future:"po %s",past:"prieš %s",s:n,m:i,mm:s,h:i,hh:s,d:i,dd:s,M:i,MM:s,y:i,yy:s},ordinal:function(e){return e+"-oji"},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("lt","lt",{closeText:"Uždaryti",prevText:"<Atgal",nextText:"Pirmyn>",currentText:"Šiandien",monthNames:["Sausis","Vasaris","Kovas","Balandis","Gegužė","Birželis","Liepa","Rugpjūtis","Rugsėjis","Spalis","Lapkritis","Gruodis"],monthNamesShort:["Sau","Vas","Kov","Bal","Geg","Bir","Lie","Rugp","Rugs","Spa","Lap","Gru"],dayNames:["sekmadienis","pirmadienis","antradienis","trečiadienis","ketvirtadienis","penktadienis","šeštadienis"],dayNamesShort:["sek","pir","ant","tre","ket","pen","šeš"],dayNamesMin:["Se","Pr","An","Tr","Ke","Pe","Še"],weekHeader:"SAV",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:""}),e.fullCalendar.lang("lt",{defaultButtonText:{month:"Mėnuo",week:"Savaitė",day:"Diena",list:"Darbotvarkė"},allDayText:"Visą dieną",eventLimitText:"daugiau"})}); \ No newline at end of file diff --git a/dist/lang/lv.js b/dist/lang/lv.js new file mode 100644 index 0000000..b1591bb --- /dev/null +++ b/dist/lang/lv.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n){var i=e.split("_");return n?1===t%10&&11!==t?i[2]:i[3]:1===t%10&&11!==t?i[0]:i[1]}function i(e,t,i){return e+" "+n(r[i],e,t)}var r={mm:"minūti_minūtes_minūte_minūtes",hh:"stundu_stundas_stunda_stundas",dd:"dienu_dienas_diena_dienas",MM:"mēnesi_mēnešus_mēnesis_mēneši",yy:"gadu_gadus_gads_gadi"};(t.defineLocale||t.lang).call(t,"lv",{months:"janvāris_februāris_marts_aprīlis_maijs_jūnijs_jūlijs_augusts_septembris_oktobris_novembris_decembris".split("_"),monthsShort:"jan_feb_mar_apr_mai_jūn_jūl_aug_sep_okt_nov_dec".split("_"),weekdays:"svētdiena_pirmdiena_otrdiena_trešdiena_ceturtdiena_piektdiena_sestdiena".split("_"),weekdaysShort:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysMin:"Sv_P_O_T_C_Pk_S".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"YYYY. [gada] D. MMMM",LLL:"YYYY. [gada] D. MMMM, LT",LLLL:"YYYY. [gada] D. MMMM, dddd, LT"},calendar:{sameDay:"[Šodien pulksten] LT",nextDay:"[Rīt pulksten] LT",nextWeek:"dddd [pulksten] LT",lastDay:"[Vakar pulksten] LT",lastWeek:"[Pagājušā] dddd [pulksten] LT",sameElse:"L"},relativeTime:{future:"%s vēlāk",past:"%s agrāk",s:"dažas sekundes",m:"minūti",mm:i,h:"stundu",hh:i,d:"dienu",dd:i,M:"mēnesi",MM:i,y:"gadu",yy:i},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("lv","lv",{closeText:"Aizvērt",prevText:"Iepr.",nextText:"Nāk.",currentText:"Šodien",monthNames:["Janvāris","Februāris","Marts","Aprīlis","Maijs","Jūnijs","Jūlijs","Augusts","Septembris","Oktobris","Novembris","Decembris"],monthNamesShort:["Jan","Feb","Mar","Apr","Mai","Jūn","Jūl","Aug","Sep","Okt","Nov","Dec"],dayNames:["svētdiena","pirmdiena","otrdiena","trešdiena","ceturtdiena","piektdiena","sestdiena"],dayNamesShort:["svt","prm","otr","tre","ctr","pkt","sst"],dayNamesMin:["Sv","Pr","Ot","Tr","Ct","Pk","Ss"],weekHeader:"Ned.",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("lv",{defaultButtonText:{month:"Mēnesis",week:"Nedēļa",day:"Diena",list:"Dienas kārtība"},allDayText:"Visu dienu",eventLimitText:function(e){return"+vēl "+e}})}); \ No newline at end of file diff --git a/dist/lang/nl.js b/dist/lang/nl.js new file mode 100644 index 0000000..c3fe4a5 --- /dev/null +++ b/dist/lang/nl.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),i="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_");(t.defineLocale||t.lang).call(t,"nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(e,t){return/-MMM-/.test(t)?i[e.month()]:n[e.month()]},weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"Zo_Ma_Di_Wo_Do_Vr_Za".split("_"),longDateFormat:{LT:"HH:mm",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",m:"één minuut",mm:"%d minuten",h:"één uur",hh:"%d uur",d:"één dag",dd:"%d dagen",M:"één maand",MM:"%d maanden",y:"één jaar",yy:"%d jaar"},ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("nl","nl",{closeText:"Sluiten",prevText:"←",nextText:"→",currentText:"Vandaag",monthNames:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthNamesShort:["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],dayNames:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],dayNamesShort:["zon","maa","din","woe","don","vri","zat"],dayNamesMin:["zo","ma","di","wo","do","vr","za"],weekHeader:"Wk",dateFormat:"dd-mm-yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("nl",{defaultButtonText:{month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayText:"Hele dag",eventLimitText:"extra"})}); \ No newline at end of file diff --git a/dist/lang/pl.js b/dist/lang/pl.js new file mode 100644 index 0000000..f49e6a4 --- /dev/null +++ b/dist/lang/pl.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e){return 5>e%10&&e%10>1&&1!==~~(e/10)%10}function i(e,t,i){var a=e+" ";switch(i){case"m":return t?"minuta":"minutę";case"mm":return a+(n(e)?"minuty":"minut");case"h":return t?"godzina":"godzinę";case"hh":return a+(n(e)?"godziny":"godzin");case"MM":return a+(n(e)?"miesiące":"miesięcy");case"yy":return a+(n(e)?"lata":"lat")}}var a="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),r="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_");(t.defineLocale||t.lang).call(t,"pl",{months:function(e,t){return/D MMMM/.test(t)?r[e.month()]:a[e.month()]},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"nie_pon_wt_śr_czw_pt_sb".split("_"),weekdaysMin:"N_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:"[W] dddd [o] LT",lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",m:i,mm:i,h:i,hh:i,d:"1 dzień",dd:"%d dni",M:"miesiąc",MM:i,y:"rok",yy:i},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("pl","pl",{closeText:"Zamknij",prevText:"<Poprzedni",nextText:"Następny>",currentText:"Dziś",monthNames:["Styczeń","Luty","Marzec","Kwiecień","Maj","Czerwiec","Lipiec","Sierpień","Wrzesień","Październik","Listopad","Grudzień"],monthNamesShort:["Sty","Lu","Mar","Kw","Maj","Cze","Lip","Sie","Wrz","Pa","Lis","Gru"],dayNames:["Niedziela","Poniedziałek","Wtorek","Środa","Czwartek","Piątek","Sobota"],dayNamesShort:["Nie","Pn","Wt","Śr","Czw","Pt","So"],dayNamesMin:["N","Pn","Wt","Śr","Cz","Pt","So"],weekHeader:"Tydz",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("pl",{defaultButtonText:{month:"Miesiąc",week:"Tydzień",day:"Dzień",list:"Plan dnia"},allDayText:"Cały dzień",eventLimitText:"więcej"})}); \ No newline at end of file diff --git a/dist/lang/pt-br.js b/dist/lang/pt-br.js new file mode 100644 index 0000000..824e02e --- /dev/null +++ b/dist/lang/pt-br.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"pt-br",{months:"janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_sáb".split("_"),weekdaysMin:"dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY [às] LT",LLLL:"dddd, D [de] MMMM [de] YYYY [às] LT"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"%s atrás",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinal:"%dº"}),e.fullCalendar.datepickerLang("pt-br","pt-BR",{closeText:"Fechar",prevText:"<Anterior",nextText:"Próximo>",currentText:"Hoje",monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("pt-br",{defaultButtonText:{month:"Mês",week:"Semana",day:"Dia",list:"Compromissos"},allDayText:"dia inteiro",eventLimitText:function(e){return"mais +"+e}})}); \ No newline at end of file diff --git a/dist/lang/pt.js b/dist/lang/pt.js new file mode 100644 index 0000000..fcb5935 --- /dev/null +++ b/dist/lang/pt.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"pt",{months:"janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_sáb".split("_"),weekdaysMin:"dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY LT",LLLL:"dddd, D [de] MMMM [de] YYYY LT"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"há %s",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("pt","pt",{closeText:"Fechar",prevText:"Anterior",nextText:"Seguinte",currentText:"Hoje",monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],weekHeader:"Sem",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("pt",{defaultButtonText:{month:"Mês",week:"Semana",day:"Dia",list:"Agenda"},allDayText:"Todo o dia",eventLimitText:"mais"})}); \ No newline at end of file diff --git a/dist/lang/ro.js b/dist/lang/ro.js new file mode 100644 index 0000000..6de85ad --- /dev/null +++ b/dist/lang/ro.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n){var i={mm:"minute",hh:"ore",dd:"zile",MM:"luni",yy:"ani"},a=" ";return(e%100>=20||e>=100&&0===e%100)&&(a=" de "),e+a+i[n]}(t.defineLocale||t.lang).call(t,"ro",{months:"ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"),monthsShort:"ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"),weekdays:"duminică_luni_marți_miercuri_joi_vineri_sâmbătă".split("_"),weekdaysShort:"Dum_Lun_Mar_Mie_Joi_Vin_Sâm".split("_"),weekdaysMin:"Du_Lu_Ma_Mi_Jo_Vi_Sâ".split("_"),longDateFormat:{LT:"H:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[azi la] LT",nextDay:"[mâine la] LT",nextWeek:"dddd [la] LT",lastDay:"[ieri la] LT",lastWeek:"[fosta] dddd [la] LT",sameElse:"L"},relativeTime:{future:"peste %s",past:"%s în urmă",s:"câteva secunde",m:"un minut",mm:n,h:"o oră",hh:n,d:"o zi",dd:n,M:"o lună",MM:n,y:"un an",yy:n},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("ro","ro",{closeText:"Închide",prevText:"« Luna precedentă",nextText:"Luna următoare »",currentText:"Azi",monthNames:["Ianuarie","Februarie","Martie","Aprilie","Mai","Iunie","Iulie","August","Septembrie","Octombrie","Noiembrie","Decembrie"],monthNamesShort:["Ian","Feb","Mar","Apr","Mai","Iun","Iul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Duminică","Luni","Marţi","Miercuri","Joi","Vineri","Sâmbătă"],dayNamesShort:["Dum","Lun","Mar","Mie","Joi","Vin","Sâm"],dayNamesMin:["Du","Lu","Ma","Mi","Jo","Vi","Sâ"],weekHeader:"Săpt",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("ro",{defaultButtonText:{prev:"precedentă",next:"următoare",month:"Lună",week:"Săptămână",day:"Zi",list:"Agendă"},allDayText:"Toată ziua",eventLimitText:function(e){return"+alte "+e}})}); \ No newline at end of file diff --git a/dist/lang/ru.js b/dist/lang/ru.js new file mode 100644 index 0000000..07c92f9 --- /dev/null +++ b/dist/lang/ru.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t){var n=e.split("_");return 1===t%10&&11!==t%100?n[0]:t%10>=2&&4>=t%10&&(10>t%100||t%100>=20)?n[1]:n[2]}function i(e,t,i){var a={mm:t?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",MM:"месяц_месяца_месяцев",yy:"год_года_лет"};return"m"===i?t?"минута":"минуту":e+" "+n(a[i],+e)}function a(e,t){var n={nominative:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),accusative:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_")},i=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(t)?"accusative":"nominative";return n[i][e.month()]}function r(e,t){var n={nominative:"янв_фев_мар_апр_май_июнь_июль_авг_сен_окт_ноя_дек".split("_"),accusative:"янв_фев_мар_апр_мая_июня_июля_авг_сен_окт_ноя_дек".split("_")},i=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/.test(t)?"accusative":"nominative";return n[i][e.month()]}function s(e,t){var n={nominative:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),accusative:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_")},i=/\[ ?[Вв] ?(?:прошлую|следующую)? ?\] ?dddd/.test(t)?"accusative":"nominative";return n[i][e.day()]}(t.defineLocale||t.lang).call(t,"ru",{months:a,monthsShort:r,weekdays:s,weekdaysShort:"вс_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"вс_пн_вт_ср_чт_пт_сб".split("_"),monthsParse:[/^янв/i,/^фев/i,/^мар/i,/^апр/i,/^ма[й|я]/i,/^июн/i,/^июл/i,/^авг/i,/^сен/i,/^окт/i,/^ноя/i,/^дек/i],longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., LT",LLLL:"dddd, D MMMM YYYY г., LT"},calendar:{sameDay:"[Сегодня в] LT",nextDay:"[Завтра в] LT",lastDay:"[Вчера в] LT",nextWeek:function(){return 2===this.day()?"[Во] dddd [в] LT":"[В] dddd [в] LT"},lastWeek:function(){switch(this.day()){case 0:return"[В прошлое] dddd [в] LT";case 1:case 2:case 4:return"[В прошлый] dddd [в] LT";case 3:case 5:case 6:return"[В прошлую] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"через %s",past:"%s назад",s:"несколько секунд",m:i,mm:i,h:"час",hh:i,d:"день",dd:i,M:"месяц",MM:i,y:"год",yy:i},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(e){return/^(дня|вечера)$/.test(e)},meridiem:function(e){return 4>e?"ночи":12>e?"утра":17>e?"дня":"вечера"},ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":return e+"-й";case"D":return e+"-го";case"w":case"W":return e+"-я";default:return e}},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("ru","ru",{closeText:"Закрыть",prevText:"<Пред",nextText:"След>",currentText:"Сегодня",monthNames:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthNamesShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],dayNames:["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"],dayNamesShort:["вск","пнд","втр","срд","чтв","птн","сбт"],dayNamesMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],weekHeader:"Нед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("ru",{defaultButtonText:{month:"Месяц",week:"Неделя",day:"День",list:"Повестка дня"},allDayText:"Весь день",eventLimitText:function(e){return"+ ещё "+e}})}); \ No newline at end of file diff --git a/dist/lang/sk.js b/dist/lang/sk.js new file mode 100644 index 0000000..aaf69a2 --- /dev/null +++ b/dist/lang/sk.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e){return e>1&&5>e}function a(e,t,a,i){var r=e+" ";switch(a){case"s":return t||i?"pár sekúnd":"pár sekundami";case"m":return t?"minúta":i?"minútu":"minútou";case"mm":return t||i?r+(n(e)?"minúty":"minút"):r+"minútami";case"h":return t?"hodina":i?"hodinu":"hodinou";case"hh":return t||i?r+(n(e)?"hodiny":"hodín"):r+"hodinami";case"d":return t||i?"deň":"dňom";case"dd":return t||i?r+(n(e)?"dni":"dní"):r+"dňami";case"M":return t||i?"mesiac":"mesiacom";case"MM":return t||i?r+(n(e)?"mesiace":"mesiacov"):r+"mesiacmi";case"y":return t||i?"rok":"rokom";case"yy":return t||i?r+(n(e)?"roky":"rokov"):r+"rokmi"}}var i="január_február_marec_apríl_máj_jún_júl_august_september_október_november_december".split("_"),r="jan_feb_mar_apr_máj_jún_júl_aug_sep_okt_nov_dec".split("_");(t.defineLocale||t.lang).call(t,"sk",{months:i,monthsShort:r,monthsParse:function(e,t){var n,a=[];for(n=0;12>n;n++)a[n]=RegExp("^"+e[n]+"$|^"+t[n]+"$","i");return a}(i,r),weekdays:"nedeľa_pondelok_utorok_streda_štvrtok_piatok_sobota".split("_"),weekdaysShort:"ne_po_ut_st_št_pi_so".split("_"),weekdaysMin:"ne_po_ut_st_št_pi_so".split("_"),longDateFormat:{LT:"H:mm",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd D. MMMM YYYY LT"},calendar:{sameDay:"[dnes o] LT",nextDay:"[zajtra o] LT",nextWeek:function(){switch(this.day()){case 0:return"[v nedeľu o] LT";case 1:case 2:return"[v] dddd [o] LT";case 3:return"[v stredu o] LT";case 4:return"[vo štvrtok o] LT";case 5:return"[v piatok o] LT";case 6:return"[v sobotu o] LT"}},lastDay:"[včera o] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulú nedeľu o] LT";case 1:case 2:return"[minulý] dddd [o] LT";case 3:return"[minulú stredu o] LT";case 4:case 5:return"[minulý] dddd [o] LT";case 6:return"[minulú sobotu o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"pred %s",s:a,m:a,mm:a,h:a,hh:a,d:a,dd:a,M:a,MM:a,y:a,yy:a},ordinal:"%d.",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("sk","sk",{closeText:"Zavrieť",prevText:"<Predchádzajúci",nextText:"Nasledujúci>",currentText:"Dnes",monthNames:["január","február","marec","apríl","máj","jún","júl","august","september","október","november","december"],monthNamesShort:["Jan","Feb","Mar","Apr","Máj","Jún","Júl","Aug","Sep","Okt","Nov","Dec"],dayNames:["nedeľa","pondelok","utorok","streda","štvrtok","piatok","sobota"],dayNamesShort:["Ned","Pon","Uto","Str","Štv","Pia","Sob"],dayNamesMin:["Ne","Po","Ut","St","Št","Pia","So"],weekHeader:"Ty",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sk",{defaultButtonText:{month:"Mesiac",week:"Týždeň",day:"Deň",list:"Rozvrh"},allDayText:"Celý deň",eventLimitText:function(e){return"+ďalšie: "+e}})}); \ No newline at end of file diff --git a/dist/lang/sl.js b/dist/lang/sl.js new file mode 100644 index 0000000..1b18665 --- /dev/null +++ b/dist/lang/sl.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n){var a=e+" ";switch(n){case"m":return t?"ena minuta":"eno minuto";case"mm":return a+=1===e?"minuta":2===e?"minuti":3===e||4===e?"minute":"minut";case"h":return t?"ena ura":"eno uro";case"hh":return a+=1===e?"ura":2===e?"uri":3===e||4===e?"ure":"ur";case"dd":return a+=1===e?"dan":"dni";case"MM":return a+=1===e?"mesec":2===e?"meseca":3===e||4===e?"mesece":"mesecev";case"yy":return a+=1===e?"leto":2===e?"leti":3===e||4===e?"leta":"let"}}(t.defineLocale||t.lang).call(t,"sl",{months:"januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"),weekdays:"nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota".split("_"),weekdaysShort:"ned._pon._tor._sre._čet._pet._sob.".split("_"),weekdaysMin:"ne_po_to_sr_če_pe_so".split("_"),longDateFormat:{LT:"H:mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danes ob] LT",nextDay:"[jutri ob] LT",nextWeek:function(){switch(this.day()){case 0:return"[v] [nedeljo] [ob] LT";case 3:return"[v] [sredo] [ob] LT";case 6:return"[v] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[v] dddd [ob] LT"}},lastDay:"[včeraj ob] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[prejšnja] dddd [ob] LT";case 1:case 2:case 4:case 5:return"[prejšnji] dddd [ob] LT"}},sameElse:"L"},relativeTime:{future:"čez %s",past:"%s nazaj",s:"nekaj sekund",m:n,mm:n,h:n,hh:n,d:"en dan",dd:n,M:"en mesec",MM:n,y:"eno leto",yy:n},ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("sl","sl",{closeText:"Zapri",prevText:"<Prejšnji",nextText:"Naslednji>",currentText:"Trenutni",monthNames:["Januar","Februar","Marec","April","Maj","Junij","Julij","Avgust","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],dayNames:["Nedelja","Ponedeljek","Torek","Sreda","Četrtek","Petek","Sobota"],dayNamesShort:["Ned","Pon","Tor","Sre","Čet","Pet","Sob"],dayNamesMin:["Ne","Po","To","Sr","Če","Pe","So"],weekHeader:"Teden",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sl",{defaultButtonText:{month:"Mesec",week:"Teden",day:"Dan",list:"Dnevni red"},allDayText:"Ves dan",eventLimitText:"več"})}); \ No newline at end of file diff --git a/dist/lang/sr-cyrl.js b/dist/lang/sr-cyrl.js new file mode 100644 index 0000000..36c4e39 --- /dev/null +++ b/dist/lang/sr-cyrl.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n={words:{m:["један минут","једне минуте"],mm:["минут","минуте","минута"],h:["један сат","једног сата"],hh:["сат","сата","сати"],dd:["дан","дана","дана"],MM:["месец","месеца","месеци"],yy:["година","године","година"]},correctGrammaticalCase:function(e,t){return 1===e?t[0]:e>=2&&4>=e?t[1]:t[2]},translate:function(e,t,a){var i=n.words[a];return 1===a.length?t?i[0]:i[1]:e+" "+n.correctGrammaticalCase(e,i)}};(t.defineLocale||t.lang).call(t,"sr-cyrl",{months:["јануар","фебруар","март","април","мај","јун","јул","август","септембар","октобар","новембар","децембар"],monthsShort:["јан.","феб.","мар.","апр.","мај","јун","јул","авг.","сеп.","окт.","нов.","дец."],weekdays:["недеља","понедељак","уторак","среда","четвртак","петак","субота"],weekdaysShort:["нед.","пон.","уто.","сре.","чет.","пет.","суб."],weekdaysMin:["не","по","ут","ср","че","пе","су"],longDateFormat:{LT:"H:mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[данас у] LT",nextDay:"[сутра у] LT",nextWeek:function(){switch(this.day()){case 0:return"[у] [недељу] [у] LT";case 3:return"[у] [среду] [у] LT";case 6:return"[у] [суботу] [у] LT";case 1:case 2:case 4:case 5:return"[у] dddd [у] LT"}},lastDay:"[јуче у] LT",lastWeek:function(){var e=["[прошле] [недеље] [у] LT","[прошлог] [понедељка] [у] LT","[прошлог] [уторка] [у] LT","[прошле] [среде] [у] LT","[прошлог] [четвртка] [у] LT","[прошлог] [петка] [у] LT","[прошле] [суботе] [у] LT"];return e[this.day()]},sameElse:"L"},relativeTime:{future:"за %s",past:"пре %s",s:"неколико секунди",m:n.translate,mm:n.translate,h:n.translate,hh:n.translate,d:"дан",dd:n.translate,M:"месец",MM:n.translate,y:"годину",yy:n.translate},ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("sr-cyrl","sr",{closeText:"Затвори",prevText:"<",nextText:">",currentText:"Данас",monthNames:["Јануар","Фебруар","Март","Април","Мај","Јун","Јул","Август","Септембар","Октобар","Новембар","Децембар"],monthNamesShort:["Јан","Феб","Мар","Апр","Мај","Јун","Јул","Авг","Сеп","Окт","Нов","Дец"],dayNames:["Недеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"],dayNamesShort:["Нед","Пон","Уто","Сре","Чет","Пет","Суб"],dayNamesMin:["Не","По","Ут","Ср","Че","Пе","Су"],weekHeader:"Сед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sr-cyrl",{defaultButtonText:{month:"Месец",week:"Недеља",day:"Дан",list:"Планер"},allDayText:"Цео дан",eventLimitText:function(e){return"+ још "+e}})}); \ No newline at end of file diff --git a/dist/lang/sr.js b/dist/lang/sr.js new file mode 100644 index 0000000..73de5c8 --- /dev/null +++ b/dist/lang/sr.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n={words:{m:["jedan minut","jedne minute"],mm:["minut","minute","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mesec","meseca","meseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(e,t){return 1===e?t[0]:e>=2&&4>=e?t[1]:t[2]},translate:function(e,t,a){var r=n.words[a];return 1===a.length?t?r[0]:r[1]:e+" "+n.correctGrammaticalCase(e,r)}};(t.defineLocale||t.lang).call(t,"sr",{months:["januar","februar","mart","april","maj","jun","jul","avgust","septembar","oktobar","novembar","decembar"],monthsShort:["jan.","feb.","mar.","apr.","maj","jun","jul","avg.","sep.","okt.","nov.","dec."],weekdays:["nedelja","ponedeljak","utorak","sreda","četvrtak","petak","subota"],weekdaysShort:["ned.","pon.","uto.","sre.","čet.","pet.","sub."],weekdaysMin:["ne","po","ut","sr","če","pe","su"],longDateFormat:{LT:"H:mm",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedelju] [u] LT";case 3:return"[u] [sredu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){var e=["[prošle] [nedelje] [u] LT","[prošlog] [ponedeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT"];return e[this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"pre %s",s:"nekoliko sekundi",m:n.translate,mm:n.translate,h:n.translate,hh:n.translate,d:"dan",dd:n.translate,M:"mesec",MM:n.translate,y:"godinu",yy:n.translate},ordinal:"%d.",week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("sr","sr",{closeText:"Затвори",prevText:"<",nextText:">",currentText:"Данас",monthNames:["Јануар","Фебруар","Март","Април","Мај","Јун","Јул","Август","Септембар","Октобар","Новембар","Децембар"],monthNamesShort:["Јан","Феб","Мар","Апр","Мај","Јун","Јул","Авг","Сеп","Окт","Нов","Дец"],dayNames:["Недеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"],dayNamesShort:["Нед","Пон","Уто","Сре","Чет","Пет","Суб"],dayNamesMin:["Не","По","Ут","Ср","Че","Пе","Су"],weekHeader:"Сед",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sr",{defaultButtonText:{month:"Месец",week:"Недеља",day:"Дан",list:"Планер"},allDayText:"Цео дан",eventLimitText:function(e){return"+ још "+e}})}); \ No newline at end of file diff --git a/dist/lang/sv.js b/dist/lang/sv.js new file mode 100644 index 0000000..09cdcee --- /dev/null +++ b/dist/lang/sv.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"söndag_måndag_tisdag_onsdag_torsdag_fredag_lördag".split("_"),weekdaysShort:"sön_mån_tis_ons_tor_fre_lör".split("_"),weekdaysMin:"sö_må_ti_on_to_fr_lö".split("_"),longDateFormat:{LT:"HH:mm",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[Igår] LT",nextWeek:"dddd LT",lastWeek:"[Förra] dddd[en] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"för %s sedan",s:"några sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en månad",MM:"%d månader",y:"ett år",yy:"%d år"},ordinal:function(e){var t=e%10,n=1===~~(e%100/10)?"e":1===t?"a":2===t?"a":3===t?"e":"e";return e+n},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("sv","sv",{closeText:"Stäng",prevText:"«Förra",nextText:"Nästa»",currentText:"Idag",monthNames:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNamesShort:["Sön","Mån","Tis","Ons","Tor","Fre","Lör"],dayNames:["Söndag","Måndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag"],dayNamesMin:["Sö","Må","Ti","On","To","Fr","Lö"],weekHeader:"Ve",dateFormat:"yy-mm-dd",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("sv",{defaultButtonText:{month:"Månad",week:"Vecka",day:"Dag",list:"Program"},allDayText:"Heldag",eventLimitText:"till"})}); \ No newline at end of file diff --git a/dist/lang/th.js b/dist/lang/th.js new file mode 100644 index 0000000..e8bea65 --- /dev/null +++ b/dist/lang/th.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"th",{months:"มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม".split("_"),monthsShort:"มกรา_กุมภา_มีนา_เมษา_พฤษภา_มิถุนา_กรกฎา_สิงหา_กันยา_ตุลา_พฤศจิกา_ธันวา".split("_"),weekdays:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์".split("_"),weekdaysShort:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์".split("_"),weekdaysMin:"อา._จ._อ._พ._พฤ._ศ._ส.".split("_"),longDateFormat:{LT:"H นาฬิกา m นาที",L:"YYYY/MM/DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY เวลา LT",LLLL:"วันddddที่ D MMMM YYYY เวลา LT"},meridiem:function(e){return 12>e?"ก่อนเที่ยง":"หลังเที่ยง"},calendar:{sameDay:"[วันนี้ เวลา] LT",nextDay:"[พรุ่งนี้ เวลา] LT",nextWeek:"dddd[หน้า เวลา] LT",lastDay:"[เมื่อวานนี้ เวลา] LT",lastWeek:"[วัน]dddd[ที่แล้ว เวลา] LT",sameElse:"L"},relativeTime:{future:"อีก %s",past:"%sที่แล้ว",s:"ไม่กี่วินาที",m:"1 นาที",mm:"%d นาที",h:"1 ชั่วโมง",hh:"%d ชั่วโมง",d:"1 วัน",dd:"%d วัน",M:"1 เดือน",MM:"%d เดือน",y:"1 ปี",yy:"%d ปี"}}),e.fullCalendar.datepickerLang("th","th",{closeText:"ปิด",prevText:"« ย้อน",nextText:"ถัดไป »",currentText:"วันนี้",monthNames:["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],monthNamesShort:["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."],dayNames:["อาทิตย์","จันทร์","อังคาร","พุธ","พฤหัสบดี","ศุกร์","เสาร์"],dayNamesShort:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],dayNamesMin:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],weekHeader:"Wk",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("th",{defaultButtonText:{month:"เดือน",week:"สัปดาห์",day:"วัน",list:"แผนงาน"},allDayText:"ตลอดวัน",eventLimitText:"เพิ่มเติม"})}); \ No newline at end of file diff --git a/dist/lang/tr.js b/dist/lang/tr.js new file mode 100644 index 0000000..d7265a7 --- /dev/null +++ b/dist/lang/tr.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n={1:"'inci",5:"'inci",8:"'inci",70:"'inci",80:"'inci",2:"'nci",7:"'nci",20:"'nci",50:"'nci",3:"'üncü",4:"'üncü",100:"'üncü",6:"'ncı",9:"'uncu",10:"'uncu",30:"'uncu",60:"'ıncı",90:"'ıncı"};(t.defineLocale||t.lang).call(t,"tr",{months:"Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eylül_Ekim_Kasım_Aralık".split("_"),monthsShort:"Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara".split("_"),weekdays:"Pazar_Pazartesi_Salı_Çarşamba_Perşembe_Cuma_Cumartesi".split("_"),weekdaysShort:"Paz_Pts_Sal_Çar_Per_Cum_Cts".split("_"),weekdaysMin:"Pz_Pt_Sa_Ça_Pe_Cu_Ct".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd, D MMMM YYYY LT"},calendar:{sameDay:"[bugün saat] LT",nextDay:"[yarın saat] LT",nextWeek:"[haftaya] dddd [saat] LT",lastDay:"[dün] LT",lastWeek:"[geçen hafta] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s önce",s:"birkaç saniye",m:"bir dakika",mm:"%d dakika",h:"bir saat",hh:"%d saat",d:"bir gün",dd:"%d gün",M:"bir ay",MM:"%d ay",y:"bir yıl",yy:"%d yıl"},ordinal:function(e){if(0===e)return e+"'ıncı";var t=e%10,a=e%100-t,r=e>=100?100:null;return e+(n[t]||n[a]||n[r])},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("tr","tr",{closeText:"kapat",prevText:"<geri",nextText:"ileri>",currentText:"bugün",monthNames:["Ocak","Şubat","Mart","Nisan","Mayıs","Haziran","Temmuz","Ağustos","Eylül","Ekim","Kasım","Aralık"],monthNamesShort:["Oca","Şub","Mar","Nis","May","Haz","Tem","Ağu","Eyl","Eki","Kas","Ara"],dayNames:["Pazar","Pazartesi","Salı","Çarşamba","Perşembe","Cuma","Cumartesi"],dayNamesShort:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],dayNamesMin:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],weekHeader:"Hf",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("tr",{defaultButtonText:{next:"ileri",month:"Ay",week:"Hafta",day:"Gün",list:"Ajanda"},allDayText:"Tüm gün",eventLimitText:"daha fazla"})}); \ No newline at end of file diff --git a/dist/lang/uk.js b/dist/lang/uk.js new file mode 100644 index 0000000..a472f52 --- /dev/null +++ b/dist/lang/uk.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t){var n=e.split("_");return 1===t%10&&11!==t%100?n[0]:t%10>=2&&4>=t%10&&(10>t%100||t%100>=20)?n[1]:n[2]}function a(e,t,a){var r={mm:"хвилина_хвилини_хвилин",hh:"година_години_годин",dd:"день_дні_днів",MM:"місяць_місяці_місяців",yy:"рік_роки_років"};return"m"===a?t?"хвилина":"хвилину":"h"===a?t?"година":"годину":e+" "+n(r[a],+e)}function r(e,t){var n={nominative:"січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень".split("_"),accusative:"січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня".split("_")},a=/D[oD]? *MMMM?/.test(t)?"accusative":"nominative";return n[a][e.month()]}function i(e,t){var n={nominative:"неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота".split("_"),accusative:"неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу".split("_"),genitive:"неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи".split("_")},a=/(\[[ВвУу]\]) ?dddd/.test(t)?"accusative":/\[?(?:минулої|наступної)? ?\] ?dddd/.test(t)?"genitive":"nominative";return n[a][e.day()]}function s(e){return function(){return e+"о"+(11===this.hours()?"б":"")+"] LT"}}(t.defineLocale||t.lang).call(t,"uk",{months:r,monthsShort:"січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд".split("_"),weekdays:i,weekdaysShort:"нд_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"HH:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY р.",LLL:"D MMMM YYYY р., LT",LLLL:"dddd, D MMMM YYYY р., LT"},calendar:{sameDay:s("[Сьогодні "),nextDay:s("[Завтра "),lastDay:s("[Вчора "),nextWeek:s("[У] dddd ["),lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return s("[Минулої] dddd [").call(this);case 1:case 2:case 4:return s("[Минулого] dddd [").call(this)}},sameElse:"L"},relativeTime:{future:"за %s",past:"%s тому",s:"декілька секунд",m:a,mm:a,h:"годину",hh:a,d:"день",dd:a,M:"місяць",MM:a,y:"рік",yy:a},meridiem:function(e){return 4>e?"ночі":12>e?"ранку":17>e?"дня":"вечора"},ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":case"w":case"W":return e+"-й";case"D":return e+"-го";default:return e}},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("uk","uk",{closeText:"Закрити",prevText:"<",nextText:">",currentText:"Сьогодні",monthNames:["Січень","Лютий","Березень","Квітень","Травень","Червень","Липень","Серпень","Вересень","Жовтень","Листопад","Грудень"],monthNamesShort:["Січ","Лют","Бер","Кві","Тра","Чер","Лип","Сер","Вер","Жов","Лис","Гру"],dayNames:["неділя","понеділок","вівторок","середа","четвер","п’ятниця","субота"],dayNamesShort:["нед","пнд","вів","срд","чтв","птн","сбт"],dayNamesMin:["Нд","Пн","Вт","Ср","Чт","Пт","Сб"],weekHeader:"Тиж",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("uk",{defaultButtonText:{month:"Місяць",week:"Тиждень",day:"День",list:"Порядок денний"},allDayText:"Увесь день",eventLimitText:function(e){return"+ще "+e+"..."}})}); \ No newline at end of file diff --git a/dist/lang/vi.js b/dist/lang/vi.js new file mode 100644 index 0000000..c92ac28 --- /dev/null +++ b/dist/lang/vi.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"vi",{months:"tháng 1_tháng 2_tháng 3_tháng 4_tháng 5_tháng 6_tháng 7_tháng 8_tháng 9_tháng 10_tháng 11_tháng 12".split("_"),monthsShort:"Th01_Th02_Th03_Th04_Th05_Th06_Th07_Th08_Th09_Th10_Th11_Th12".split("_"),weekdays:"chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ sáu_thứ bảy".split("_"),weekdaysShort:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysMin:"CN_T2_T3_T4_T5_T6_T7".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM [năm] YYYY",LLL:"D MMMM [năm] YYYY LT",LLLL:"dddd, D MMMM [năm] YYYY LT",l:"DD/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY LT",llll:"ddd, D MMM YYYY LT"},calendar:{sameDay:"[Hôm nay lúc] LT",nextDay:"[Ngày mai lúc] LT",nextWeek:"dddd [tuần tới lúc] LT",lastDay:"[Hôm qua lúc] LT",lastWeek:"dddd [tuần rồi lúc] LT",sameElse:"L"},relativeTime:{future:"%s tới",past:"%s trước",s:"vài giây",m:"một phút",mm:"%d phút",h:"một giờ",hh:"%d giờ",d:"một ngày",dd:"%d ngày",M:"một tháng",MM:"%d tháng",y:"một năm",yy:"%d năm"},ordinal:function(e){return e},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("vi","vi",{closeText:"Đóng",prevText:"<Trước",nextText:"Tiếp>",currentText:"Hôm nay",monthNames:["Tháng Một","Tháng Hai","Tháng Ba","Tháng Tư","Tháng Năm","Tháng Sáu","Tháng Bảy","Tháng Tám","Tháng Chín","Tháng Mười","Tháng Mười Một","Tháng Mười Hai"],monthNamesShort:["Tháng 1","Tháng 2","Tháng 3","Tháng 4","Tháng 5","Tháng 6","Tháng 7","Tháng 8","Tháng 9","Tháng 10","Tháng 11","Tháng 12"],dayNames:["Chủ Nhật","Thứ Hai","Thứ Ba","Thứ Tư","Thứ Năm","Thứ Sáu","Thứ Bảy"],dayNamesShort:["CN","T2","T3","T4","T5","T6","T7"],dayNamesMin:["CN","T2","T3","T4","T5","T6","T7"],weekHeader:"Tu",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("vi",{defaultButtonText:{month:"Tháng",week:"Tuần",day:"Ngày",list:"Lịch biểu"},allDayText:"Cả ngày",eventLimitText:function(e){return"+ thêm "+e}})}); \ No newline at end of file diff --git a/dist/lang/zh-cn.js b/dist/lang/zh-cn.js new file mode 100644 index 0000000..ce7d584 --- /dev/null +++ b/dist/lang/zh-cn.js @@ -0,0 +1 @@ +(function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"zh-cn",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"周日_周一_周二_周三_周四_周五_周六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"Ah点mm",L:"YYYY-MM-DD",LL:"YYYY年MMMD日",LLL:"YYYY年MMMD日LT",LLLL:"YYYY年MMMD日ddddLT",l:"YYYY-MM-DD",ll:"YYYY年MMMD日",lll:"YYYY年MMMD日LT",llll:"YYYY年MMMD日ddddLT"},meridiem:function(e,t){var n=100*e+t;return 600>n?"凌晨":900>n?"早上":1130>n?"上午":1230>n?"中午":1800>n?"下午":"晚上"},calendar:{sameDay:function(){return 0===this.minutes()?"[今天]Ah[点整]":"[今天]LT"},nextDay:function(){return 0===this.minutes()?"[明天]Ah[点整]":"[明天]LT"},lastDay:function(){return 0===this.minutes()?"[昨天]Ah[点整]":"[昨天]LT"},nextWeek:function(){var e,n;return e=t().startOf("week"),n=this.unix()-e.unix()>=604800?"[下]":"[本]",0===this.minutes()?n+"dddAh点整":n+"dddAh点mm"},lastWeek:function(){var e,n;return e=t().startOf("week"),n=this.unix()n?"早上":1130>n?"上午":1230>n?"中午":1800>n?"下午":"晚上"},calendar:{sameDay:"[今天]LT",nextDay:"[明天]LT",nextWeek:"[下]ddddLT",lastDay:"[昨天]LT",lastWeek:"[上]ddddLT",sameElse:"L"},ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s內",past:"%s前",s:"幾秒",m:"一分鐘",mm:"%d分鐘",h:"一小時",hh:"%d小時",d:"一天",dd:"%d天",M:"一個月",MM:"%d個月",y:"一年",yy:"%d年"}}),e.fullCalendar.datepickerLang("zh-tw","zh-TW",{closeText:"關閉",prevText:"<上月",nextText:"下月>",currentText:"今天",monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthNamesShort:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周六"],dayNamesMin:["日","一","二","三","四","五","六"],weekHeader:"周",dateFormat:"yy/mm/dd",firstDay:1,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"年"}),e.fullCalendar.lang("zh-tw",{defaultButtonText:{month:"月",week:"週",day:"天",list:"待辦事項"},allDayText:"全天",eventLimitText:"更多"})}); \ No newline at end of file diff --git a/fullcalendar.jquery.json b/fullcalendar.jquery.json index 1648f4d..e600605 100644 --- a/fullcalendar.jquery.json +++ b/fullcalendar.jquery.json @@ -1,7 +1,6 @@ { "name": "fullcalendar", "version": "2.1.1", - "description": "Full-sized drag & drop event calendar with resources", "keywords": [ "calendar", "event", "full-sized", "resources" ], "homepage": "http://arshaw.com/fullcalendar/", @@ -16,7 +15,6 @@ "docs": "http://arshaw.com/fullcalendar/docs/", "download": "", "bugs": "https://github.com/seankenny/fullcalendar/issues", - "licenses": [ { "type": "MIT", diff --git a/lang/ar.js b/lang/ar.js new file mode 100644 index 0000000..ac56ec1 --- /dev/null +++ b/lang/ar.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("ar", { + defaultButtonText: { + month: "شهر", + week: "أسبوع", + day: "يوم", + list: "أجندة" + }, + allDayText: "اليوم كله", + eventLimitText: "أخرى" +}); diff --git a/lang/bg.js b/lang/bg.js new file mode 100644 index 0000000..d30821a --- /dev/null +++ b/lang/bg.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("bg", { + defaultButtonText: { + month: "Месец", + week: "Седмица", + day: "Ден", + list: "График" + }, + allDayText: "Цял ден", + eventLimitText: function(n) { + return "+още " + n; + } +}); diff --git a/lang/ca.js b/lang/ca.js new file mode 100644 index 0000000..292ca2c --- /dev/null +++ b/lang/ca.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("ca", { + defaultButtonText: { + month: "Mes", + week: "Setmana", + day: "Dia", + list: "Agenda" + }, + allDayText: "Tot el dia", + eventLimitText: "més" +}); diff --git a/lang/cs.js b/lang/cs.js new file mode 100644 index 0000000..3492256 --- /dev/null +++ b/lang/cs.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("cs", { + defaultButtonText: { + month: "Měsíc", + week: "Týden", + day: "Den", + list: "Agenda" + }, + allDayText: "Celý den", + eventLimitText: function(n) { + return "+další: " + n; + } +}); diff --git a/lang/da.js b/lang/da.js new file mode 100644 index 0000000..b79d084 --- /dev/null +++ b/lang/da.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("da", { + defaultButtonText: { + month: "Måned", + week: "Uge", + day: "Dag", + list: "Agenda" + }, + allDayText: "Hele dagen", + eventLimitText: "flere" +}); diff --git a/lang/de.js b/lang/de.js new file mode 100644 index 0000000..9514aa2 --- /dev/null +++ b/lang/de.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("de", { + defaultButtonText: { + month: "Monat", + week: "Woche", + day: "Tag", + list: "Terminübersicht" + }, + allDayText: "Ganztägig", + eventLimitText: function(n) { + return "+ weitere " + n; + } +}); diff --git a/lang/el.js b/lang/el.js new file mode 100644 index 0000000..1fa2915 --- /dev/null +++ b/lang/el.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("el", { + defaultButtonText: { + month: "Μήνας", + week: "Εβδομάδα", + day: "Ημέρα", + list: "Ατζέντα" + }, + allDayText: "Ολοήμερο", + eventLimitText: "περισσότερα" +}); diff --git a/lang/en-gb.js b/lang/en-gb.js new file mode 100644 index 0000000..9f1d100 --- /dev/null +++ b/lang/en-gb.js @@ -0,0 +1,6 @@ + +$.fullCalendar.lang("en-gb", { + columnFormat: { + week: 'ddd D/M' + } +}); diff --git a/lang/es.js b/lang/es.js new file mode 100644 index 0000000..a1d7774 --- /dev/null +++ b/lang/es.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("es", { + defaultButtonText: { + month: "Mes", + week: "Semana", + day: "Día", + list: "Agenda" + }, + allDayHtml: "Todo
el día", + eventLimitText: "más" +}); diff --git a/lang/fa.js b/lang/fa.js new file mode 100644 index 0000000..653de98 --- /dev/null +++ b/lang/fa.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("fa", { + defaultButtonText: { + month: "ماه", + week: "هفته", + day: "روز", + list: "برنامه" + }, + allDayText: "تمام روز", + eventLimitText: function(n) { + return "بیش از " + n; + } +}); diff --git a/lang/fi.js b/lang/fi.js new file mode 100644 index 0000000..09b9e7c --- /dev/null +++ b/lang/fi.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("fi", { + defaultButtonText: { + month: "Kuukausi", + week: "Viikko", + day: "Päivä", + list: "Tapahtumat" + }, + allDayText: "Koko päivä", + eventLimitText: "lisää" +}); diff --git a/lang/fr.js b/lang/fr.js new file mode 100644 index 0000000..f402971 --- /dev/null +++ b/lang/fr.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("fr", { + defaultButtonText: { + month: "Mois", + week: "Semaine", + day: "Jour", + list: "Mon planning" + }, + allDayHtml: "Toute la
journée", + eventLimitText: "en plus" +}); diff --git a/lang/hi.js b/lang/hi.js new file mode 100644 index 0000000..d91e160 --- /dev/null +++ b/lang/hi.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("hi", { + defaultButtonText: { + month: "महीना", + week: "सप्ताह", + day: "दिन", + list: "कार्यसूची" + }, + allDayText: "सभी दिन", + eventLimitText: function(n) { + return "+अधिक " + n; + } +}); diff --git a/lang/hr.js b/lang/hr.js new file mode 100644 index 0000000..b530d10 --- /dev/null +++ b/lang/hr.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("hr", { + defaultButtonText: { + month: "Mjesec", + week: "Tjedan", + day: "Dan", + list: "Raspored" + }, + allDayText: "Cijeli dan", + eventLimitText: function(n) { + return "+ još " + n; + } +}); diff --git a/lang/hu.js b/lang/hu.js new file mode 100644 index 0000000..66cd1ad --- /dev/null +++ b/lang/hu.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("hu", { + defaultButtonText: { + month: "Hónap", + week: "Hét", + day: "Nap", + list: "Napló" + }, + allDayText: "Egész nap", + eventLimitText: "további" +}); diff --git a/lang/id.js b/lang/id.js new file mode 100644 index 0000000..38cd9e6 --- /dev/null +++ b/lang/id.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("id", { + defaultButtonText: { + month: "Bulan", + week: "Minggu", + day: "Hari", + list: "Agenda" + }, + allDayHtml: "Sehari
penuh", + eventLimitText: "lebih" +}); diff --git a/lang/in.js b/lang/in.js new file mode 100644 index 0000000..966c7d5 --- /dev/null +++ b/lang/in.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("in", { + defaultButtonText: { + month: "Bulan", + week: "Minggu", + day: "Hari", + list: "Agenda" + }, + allDayText: "Sepanjang hari", + eventLimitText: function(n) { + return "masih ada " + n + " acara"; + } +}); diff --git a/lang/is.js b/lang/is.js new file mode 100644 index 0000000..058dcad --- /dev/null +++ b/lang/is.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("is", { + defaultButtonText: { + month: "Mánuður", + week: "Vika", + day: "Dagur", + list: "Dagskrá" + }, + allDayHtml: "Allan
daginn", + eventLimitText: "meira" +}); diff --git a/lang/it.js b/lang/it.js new file mode 100644 index 0000000..a3dfb59 --- /dev/null +++ b/lang/it.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("it", { + defaultButtonText: { + month: "Mese", + week: "Settimana", + day: "Giorno", + list: "Agenda" + }, + allDayHtml: "Tutto il
giorno", + eventLimitText: function(n) { + return "+altri " + n; + } +}); diff --git a/lang/iw.js b/lang/iw.js new file mode 100644 index 0000000..f4c2cb4 --- /dev/null +++ b/lang/iw.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("iw", { + defaultButtonText: { + month: "חודש", + week: "שבוע", + day: "יום", + list: "סדר יום" + }, + allDayText: "כל היום", + eventLimitText: function(n) { + return "+עוד " + n; + } +}); diff --git a/lang/ja.js b/lang/ja.js new file mode 100644 index 0000000..88bf691 --- /dev/null +++ b/lang/ja.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("ja", { + defaultButtonText: { + month: "月", + week: "週", + day: "日", + list: "予定リスト" + }, + allDayText: "終日", + eventLimitText: function(n) { + return "他 " + n + " 件"; + } +}); diff --git a/lang/ko.js b/lang/ko.js new file mode 100644 index 0000000..4b9051f --- /dev/null +++ b/lang/ko.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("ko", { + defaultButtonText: { + month: "월", + week: "주", + day: "일", + list: "일정목록" + }, + allDayText: "종일", + eventLimitText: "개" +}); diff --git a/lang/lt.js b/lang/lt.js new file mode 100644 index 0000000..bfba830 --- /dev/null +++ b/lang/lt.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("lt", { + defaultButtonText: { + month: "Mėnuo", + week: "Savaitė", + day: "Diena", + list: "Darbotvarkė" + }, + allDayText: "Visą dieną", + eventLimitText: "daugiau" +}); diff --git a/lang/lv.js b/lang/lv.js new file mode 100644 index 0000000..d60e918 --- /dev/null +++ b/lang/lv.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("lv", { + defaultButtonText: { + month: "Mēnesis", + week: "Nedēļa", + day: "Diena", + list: "Dienas kārtība" + }, + allDayText: "Visu dienu", + eventLimitText: function(n) { + return "+vēl " + n; + } +}); diff --git a/lang/nl.js b/lang/nl.js new file mode 100644 index 0000000..6b1abff --- /dev/null +++ b/lang/nl.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("nl", { + defaultButtonText: { + month: "Maand", + week: "Week", + day: "Dag", + list: "Agenda" + }, + allDayText: "Hele dag", + eventLimitText: "extra" +}); diff --git a/lang/no.js b/lang/no.js new file mode 100644 index 0000000..ce197e7 --- /dev/null +++ b/lang/no.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("no", { + defaultButtonText: { + month: "Måned", + week: "Uke", + day: "Dag", + list: "Agenda" + }, + allDayText: "Hele dagen", + eventLimitText: "til" +}); diff --git a/lang/pl.js b/lang/pl.js new file mode 100644 index 0000000..90bbd07 --- /dev/null +++ b/lang/pl.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("pl", { + defaultButtonText: { + month: "Miesiąc", + week: "Tydzień", + day: "Dzień", + list: "Plan dnia" + }, + allDayText: "Cały dzień", + eventLimitText: "więcej" +}); diff --git a/lang/pt-br.js b/lang/pt-br.js new file mode 100644 index 0000000..029d5e2 --- /dev/null +++ b/lang/pt-br.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("pt-br", { + defaultButtonText: { + month: "Mês", + week: "Semana", + day: "Dia", + list: "Compromissos" + }, + allDayText: "dia inteiro", + eventLimitText: function(n) { + return "mais +" + n; + } +}); diff --git a/lang/pt.js b/lang/pt.js new file mode 100644 index 0000000..d5c60e3 --- /dev/null +++ b/lang/pt.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("pt", { + defaultButtonText: { + month: "Mês", + week: "Semana", + day: "Dia", + list: "Agenda" + }, + allDayText: "Todo o dia", + eventLimitText: "mais" +}); diff --git a/lang/ro.js b/lang/ro.js new file mode 100644 index 0000000..a4236d1 --- /dev/null +++ b/lang/ro.js @@ -0,0 +1,15 @@ + +$.fullCalendar.lang("ro", { + defaultButtonText: { + prev: "precedentă", // override JQUI's translations, which contains the word "month" + next: "următoare", // " + month: "Lună", + week: "Săptămână", + day: "Zi", + list: "Agendă" + }, + allDayText: "Toată ziua", + eventLimitText: function(n) { + return "+alte " + n; + } +}); diff --git a/lang/ru.js b/lang/ru.js new file mode 100644 index 0000000..4bad549 --- /dev/null +++ b/lang/ru.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("ru", { + defaultButtonText: { + month: "Месяц", + week: "Неделя", + day: "День", + list: "Повестка дня" + }, + allDayText: "Весь день", + eventLimitText: function(n) { + return "+ ещё " + n; + } +}); diff --git a/lang/sk.js b/lang/sk.js new file mode 100644 index 0000000..20842cb --- /dev/null +++ b/lang/sk.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("sk", { + defaultButtonText: { + month: "Mesiac", + week: "Týždeň", + day: "Deň", + list: "Rozvrh" + }, + allDayText: "Celý deň", + eventLimitText: function(n) { + return "+ďalšie: " + n; + } +}); diff --git a/lang/sl.js b/lang/sl.js new file mode 100644 index 0000000..fbc4828 --- /dev/null +++ b/lang/sl.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("sl", { + defaultButtonText: { + month: "Mesec", + week: "Teden", + day: "Dan", + list: "Dnevni red" + }, + allDayText: "Ves dan", + eventLimitText: "več" +}); diff --git a/lang/sr.js b/lang/sr.js new file mode 100644 index 0000000..512a7e7 --- /dev/null +++ b/lang/sr.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("sr", { + defaultButtonText: { + month: "Месец", + week: "Недеља", + day: "Дан", + list: "Планер" + }, + allDayText: "Цео дан", + eventLimitText: function(n) { + return "+ још " + n; + } +}); diff --git a/lang/sv.js b/lang/sv.js new file mode 100644 index 0000000..71475e1 --- /dev/null +++ b/lang/sv.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("sv", { + defaultButtonText: { + month: "Månad", + week: "Vecka", + day: "Dag", + list: "Program" + }, + allDayText: "Heldag", + eventLimitText: "till" +}); diff --git a/lang/th.js b/lang/th.js new file mode 100644 index 0000000..3f95fbe --- /dev/null +++ b/lang/th.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("th", { + defaultButtonText: { + month: "เดือน", + week: "สัปดาห์", + day: "วัน", + list: "แผนงาน" + }, + allDayText: "ตลอดวัน", + eventLimitText: "เพิ่มเติม" +}); diff --git a/lang/tl.js b/lang/tl.js new file mode 100644 index 0000000..606dd3b --- /dev/null +++ b/lang/tl.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("tl", { + defaultButtonText: { + month: "Buwan", + week: "Linggo", + day: "Araw", + list: "Pakay" + }, + allDayText: "Lahat ng araw", + eventLimitText: "dagdag pa" +}); diff --git a/lang/tr.js b/lang/tr.js new file mode 100644 index 0000000..3c84c6f --- /dev/null +++ b/lang/tr.js @@ -0,0 +1,12 @@ + +$.fullCalendar.lang("tr", { + defaultButtonText: { + next: "ileri", // override JQUI's, which has a non-closing HTML entity in it + month: "Ay", + week: "Hafta", + day: "Gün", + list: "Ajanda" + }, + allDayText: "Tüm gün", + eventLimitText: "daha fazla" +}); diff --git a/lang/uk.js b/lang/uk.js new file mode 100644 index 0000000..0283c0a --- /dev/null +++ b/lang/uk.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("uk", { + defaultButtonText: { + month: "Місяць", + week: "Тиждень", + day: "День", + list: "Порядок денний" + }, + allDayText: "Увесь день", + eventLimitText: function(n) { + return "+ще " + n + "..."; + } +}); diff --git a/lang/vi.js b/lang/vi.js new file mode 100644 index 0000000..5c4af22 --- /dev/null +++ b/lang/vi.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("vi", { + defaultButtonText: { + month: "Tháng", + week: "Tuần", + day: "Ngày", + list: "Lịch biểu" + }, + allDayText: "Cả ngày", + eventLimitText: function(n) { + return "+ thêm " + n; + } +}); diff --git a/lang/zh-cn.js b/lang/zh-cn.js new file mode 100644 index 0000000..198e77a --- /dev/null +++ b/lang/zh-cn.js @@ -0,0 +1,13 @@ + +$.fullCalendar.lang("zh-cn", { + defaultButtonText: { + month: "月", + week: "周", + day: "日", + list: "日程" + }, + allDayText: "全天", + eventLimitText: function(n) { + return "另外 " + n + " 个"; + } +}); diff --git a/lang/zh-tw.js b/lang/zh-tw.js new file mode 100644 index 0000000..ac83d44 --- /dev/null +++ b/lang/zh-tw.js @@ -0,0 +1,11 @@ + +$.fullCalendar.lang("zh-tw", { + defaultButtonText: { + month: "月", + week: "週", + day: "天", + list: "待辦事項" + }, + allDayText: "全天", + eventLimitText: "更多" +}); diff --git a/readme.md b/readme.md index 8a9748d..f272f74 100644 --- a/readme.md +++ b/readme.md @@ -21,4 +21,4 @@ For contributors: - [Ways to contribute](http://arshaw.com/fullcalendar/wiki/Contributing/) - [General coding guidelines](https://github.com/arshaw/fullcalendar/wiki/Contributing-Code) - [Contributing features](https://github.com/arshaw/fullcalendar/wiki/Contributing-Features) -- [Contributing bugfixes](https://github.com/arshaw/fullcalendar/wiki/Contributing-Bugfixes) +- [Contributing bugfixes](https://github.com/arshaw/fullcalendar/wiki/Contributing-Bugfixes) \ No newline at end of file diff --git a/tests/automated/Agenda-render.js b/tests/automated/Agenda-render.js new file mode 100644 index 0000000..fac9c55 --- /dev/null +++ b/tests/automated/Agenda-render.js @@ -0,0 +1,43 @@ + +describe('Agenda view rendering', function() { + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultView: 'agendaWeek' + }; + }); + + describe('when LTR', function() { + + beforeEach(function() { + options.isRTL = false; + }); + + it('renders the axis on the left', function() { + $('#cal').fullCalendar(options); + var header = $('.fc-view > table > thead'); + var firstSlat = $('.fc-slats tr:first'); + expect(header.find('.fc-axis')).toBeLeftOf(header.find('.fc-day-header:first')); + expect($('.fc-day-grid .fc-axis')).toBeLeftOf($('.fc-day-grid .fc-day:first')); + expect(firstSlat.find('.fc-axis')).toBeLeftOf(firstSlat.find('td:not(.fc-axis)')); + }); + }); + + describe('when RTL', function() { + + beforeEach(function() { + options.isRTL = true; + }); + + it('renders the axis on the right', function() { + $('#cal').fullCalendar(options); + var header = $('.fc-view > table > thead'); + var firstSlat = $('.fc-slats tr:first'); + expect(header.find('.fc-axis')).toBeRightOf(header.find('.fc-day-header:first')); + expect($('.fc-day-grid .fc-axis')).toBeRightOf($('.fc-day-grid .fc-day:first')); + expect(firstSlat.find('.fc-axis')).toBeRightOf(firstSlat.find('td:not(.fc-axis)')); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/DayGrid-events.js b/tests/automated/DayGrid-events.js new file mode 100644 index 0000000..5cda9f7 --- /dev/null +++ b/tests/automated/DayGrid-events.js @@ -0,0 +1,58 @@ +describe('DayGrid event rendering', function() { + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-08-01', + defaultView: 'month' + }; + }); + + it('rendering of events across weeks stays consistent', function() { + options.events = [ + { + title: 'event1', + start: '2014-08-01', + end: '2014-08-04', + className: 'event1' + }, + { + title: 'event2', + start: '2014-08-02', + end: '2014-08-05', + className: 'event2' + } + ]; + $('#cal').fullCalendar(options); + var row0 = $('.fc-day-grid .fc-row:eq(0)'); + var row0event1 = row0.find('.event1'); + var row0event2 = row0.find('.event2'); + var row1 = $('.fc-day-grid .fc-row:eq(1)'); + var row1event1 = row1.find('.event1'); + var row1event2 = row1.find('.event2'); + expect(row0event1.offset().top).toBeLessThan(row0event2.offset().top); + expect(row1event1.offset().top).toBeLessThan(row1event2.offset().top); + }); + + it('renders an event with no url with no href', function() { + options.events = [ { + title: 'event1', + start: '2014-08-01' + } ]; + $('#cal').fullCalendar(options); + var seg = $('.fc-event'); + expect(seg).not.toHaveAttr('href'); + }); + + it('renders an event with a url with an href', function() { + options.events = [ { + title: 'event1', + start: '2014-08-01', + url: 'http://google.com/' + } ]; + $('#cal').fullCalendar(options); + var seg = $('.fc-event'); + expect(seg).toHaveAttr('href'); + }); +}); \ No newline at end of file diff --git a/tests/automated/TimeGrid-events.js b/tests/automated/TimeGrid-events.js new file mode 100644 index 0000000..2fb4f93 --- /dev/null +++ b/tests/automated/TimeGrid-events.js @@ -0,0 +1,70 @@ + +describe('TimeGrid event rendering', function() { + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-08-23', + defaultView: 'agendaWeek', + scrollTime: '00:00:00' + }; + }); + + it('renders the start and end time of an event that spans only 1 day', function() { + options.events = [ { + title: 'event1', + start: '2014-08-18T02:00:00', + end: '2014-08-18T22:00:00' + } ]; + $('#cal').fullCalendar(options); + expect('.fc-event .fc-time').toHaveText('2:00 - 10:00'); + }); + + it('renders time to/from midnight for an event that spans two days', function() { + options.events = [ { + title: 'event1', + start: '2014-08-18T02:00:00', + end: '2014-08-19T22:00:00' + } ]; + $('#cal').fullCalendar(options); + var seg1 = $('.fc-event:eq(0)'); + var seg2 = $('.fc-event:eq(1)'); + expect(seg1.find('.fc-time')).toHaveText('2:00 - 12:00'); + expect(seg2.find('.fc-time')).toHaveText('12:00 - 10:00'); + }); + + it('renders no time on an event segment that spans through an entire day', function() { + options.events = [ { + title: 'event1', + start: '2014-08-18T02:00:00', + end: '2014-08-20T22:00:00' + } ]; + $('#cal').fullCalendar(options); + var seg2 = $('.fc-event:eq(1)'); + expect(seg2).toBeInDOM(); + expect(seg2.find('.fc-time')).not.toBeInDOM(); + }); + + it('renders an event with no url with no href', function() { + options.events = [ { + title: 'event1', + start: '2014-08-18T02:00:00' + } ]; + $('#cal').fullCalendar(options); + var seg = $('.fc-event'); + expect(seg).not.toHaveAttr('href'); + }); + + it('renders an event with a url with an href', function() { + options.events = [ { + title: 'event1', + start: '2014-08-18T02:00:00', + url: 'http://google.com/' + } ]; + $('#cal').fullCalendar(options); + var seg = $('.fc-event'); + expect(seg).toHaveAttr('href'); + }); + +}); \ No newline at end of file diff --git a/tests/automated/addEventSource.js b/tests/automated/addEventSource.js new file mode 100644 index 0000000..60d193e --- /dev/null +++ b/tests/automated/addEventSource.js @@ -0,0 +1,108 @@ +describe('addEventSource', function() { + + var options; + var eventArray = [ + { id: 0, title: 'event zero', start: '2014-06-24', className: 'event-zero' }, + { id: 1, title: 'event one', start: '2014-06-24', className: 'event-non-zero event-one' }, + { id: 2, title: 'event two', start: '2014-06-24', className: 'event-non-zero event-two' } + ]; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-06-24', + defaultView: 'month' + }; + }); + + it('correctly adds an array source', function(done) { + go( + function() { + $('#cal').fullCalendar('addEventSource', eventArray); + }, + null, + done + ); + }); + + it('correctly adds a function source', function(done) { + go( + function() { + $('#cal').fullCalendar('addEventSource', function(start, end, timezone, callback) { + callback(eventArray); + }); + }, + null, + done + ); + }); + + it('correctly adds an extended array source', function(done) { + go( + function() { + $('#cal').fullCalendar('addEventSource', { + className: 'arraysource', + events: eventArray + }); + }, + function() { + expect($('.arraysource').length).toEqual(3); + }, + done + ); + }); + + it('correctly adds an extended array source', function(done) { + go( + function() { + $('#cal').fullCalendar('addEventSource', { + className: 'funcsource', + events: function(start, end, timezone, callback) { + callback(eventArray); + } + }); + }, + function() { + expect($('.funcsource').length).toEqual(3); + }, + done + ); + }); + + + function go(addFunc, extraTestFunc, doneFunc) { + var callCnt = 0; + options.eventAfterAllRender = function() { + callCnt++; + if (callCnt == 2) { // once for initial render. second time for addEventSource + called = true; + + checkAllEvents(); + if (extraTestFunc) { + extraTestFunc(); + } + + // move the calendar back out of view, then back in (for issue 2191) + $('#cal').fullCalendar('next'); + $('#cal').fullCalendar('prev'); + + checkAllEvents(); + if (extraTestFunc) { + extraTestFunc(); + } + + doneFunc(); + } + }; + $('#cal').fullCalendar(options); + addFunc(); + } + + // Checks to make sure all events have been rendered and that the calendar + // has internal info on all the events. + function checkAllEvents() { + expect($('#cal').fullCalendar('clientEvents').length).toEqual(3); + expect($('.fc-event').length).toEqual(3); + } + +}); \ No newline at end of file diff --git a/tests/automated/agenda-view.js b/tests/automated/agenda-view.js new file mode 100644 index 0000000..68662fc --- /dev/null +++ b/tests/automated/agenda-view.js @@ -0,0 +1,52 @@ + +describe('agenda view rendering', function() { + + beforeEach(function() { + affix('#cal'); + }); + + describe('when isRTL is false', function() { + + beforeEach(function() { + $('#cal').fullCalendar({ + defaultView: 'agendaWeek', + isRTL: false + }); + }); + + it('should have have days ordered sun to sat', function() { + var fc = $('#cal').find('.fc-view > table > thead th'); + expect(fc[0]).toHaveClass('fc-axis'); + expect(fc[1]).toHaveClass('fc-sun'); + expect(fc[2]).toHaveClass('fc-mon'); + expect(fc[3]).toHaveClass('fc-tue'); + expect(fc[4]).toHaveClass('fc-wed'); + expect(fc[5]).toHaveClass('fc-thu'); + expect(fc[6]).toHaveClass('fc-fri'); + expect(fc[7]).toHaveClass('fc-sat'); + }); + }); + + describe('when isRTL is true', function() { + + beforeEach(function() { + $('#cal').fullCalendar({ + defaultView: 'agendaWeek', + isRTL: true + }); + }); + + it('should have have days ordered sat to sun', function() { + var fc = $('#cal').find('.fc-view > table > thead th'); + expect(fc[0]).toHaveClass('fc-sat'); + expect(fc[1]).toHaveClass('fc-fri'); + expect(fc[2]).toHaveClass('fc-thu'); + expect(fc[3]).toHaveClass('fc-wed'); + expect(fc[4]).toHaveClass('fc-tue'); + expect(fc[5]).toHaveClass('fc-mon'); + expect(fc[6]).toHaveClass('fc-sun'); + expect(fc[7]).toHaveClass('fc-axis'); + }); + }); + +}); \ No newline at end of file diff --git a/tests/automated/allDayDefault.js b/tests/automated/allDayDefault.js new file mode 100644 index 0000000..87f7515 --- /dev/null +++ b/tests/automated/allDayDefault.js @@ -0,0 +1,210 @@ + +describe('allDayDefault', function() { + + beforeEach(function() { + affix('#cal'); + }); + + describe('when undefined', function() { + + it('guesses false if T in ISO8601 start date', function() { + $('#cal').fullCalendar({ + events: [ + { + id: '1', + start: '2014-05-01T06:00:00' + } + ] + }); + var eventObj = $('#cal').fullCalendar('clientEvents', '1')[0]; + expect(eventObj.allDay).toEqual(false); + }); + + it('guesses false if T in ISO8601 end date', function() { + $('#cal').fullCalendar({ + events: [ + { + id: '1', + start: '2014-05-01', + end: '2014-05-01T08:00:00' + } + ] + }); + var eventObj = $('#cal').fullCalendar('clientEvents', '1')[0]; + expect(eventObj.allDay).toEqual(false); + }); + + it('guesses true if ISO8601 start date with no time and unspecified end date', function() { + $('#cal').fullCalendar({ + events: [ + { + id: '1', + start: '2014-05-01' + } + ] + }); + var eventObj = $('#cal').fullCalendar('clientEvents', '1')[0]; + expect(eventObj.allDay).toEqual(true); + }); + + it('guesses true if ISO8601 start and end date with no times', function() { + $('#cal').fullCalendar({ + events: [ + { + id: '1', + start: '2014-05-01', + end: '2014-05-03' + } + ] + }); + var eventObj = $('#cal').fullCalendar('clientEvents', '1')[0]; + expect(eventObj.allDay).toEqual(true); + }); + + it('guesses false if start is a unix timestamp (which implies it has a time)', function() { + $('#cal').fullCalendar({ + events: [ + { + id: '1', + start: 1398902400000, + end: '2014-05-03' + } + ] + }); + var eventObj = $('#cal').fullCalendar('clientEvents', '1')[0]; + expect(eventObj.allDay).toEqual(false); + }); + + it('guesses false if end is a unix timestamp (which implies it has a time)', function() { + $('#cal').fullCalendar({ + events: [ + { + id: '1', + start: '2014-05-01', + end: 1399075200000 + } + ] + }); + var eventObj = $('#cal').fullCalendar('clientEvents', '1')[0]; + expect(eventObj.allDay).toEqual(false); + }); + + }); + + describe('when specified', function() { + + it('has an effect when an event\'s allDay is not specified', function() { + $('#cal').fullCalendar({ + allDayDefault: false, + events: [ + { + id: '1', + start: '2014-05-01' + } + ] + }); + var eventObj = $('#cal').fullCalendar('clientEvents', '1')[0]; + expect(eventObj.allDay).toEqual(false); + }); + + it('has no effect when an event\'s allDay is specified', function() { + $('#cal').fullCalendar({ + allDayDefault: false, + events: [ + { + id: '1', + start: '2014-05-01T00:00:00', + allDay: true + } + ] + }); + var eventObj = $('#cal').fullCalendar('clientEvents', '1')[0]; + expect(eventObj.allDay).toEqual(true); + }); + + }); + +}); + +describe('source.allDayDefault', function() { + + beforeEach(function() { + affix('#cal'); + }); + + it('has an effect when an event\'s allDay is not specified', function() { + $('#cal').fullCalendar({ + eventSources: [ + { + allDayDefault: false, + events: [ + { + id: '1', + start: '2014-05-01' + } + ] + } + ] + }); + var eventObj = $('#cal').fullCalendar('clientEvents', '1')[0]; + expect(eventObj.allDay).toEqual(false); + }); + + it('a true value can override the global allDayDefault', function() { + $('#cal').fullCalendar({ + allDayDefault: false, + eventSources: [ + { + allDayDefault: true, + events: [ + { + id: '1', + start: '2014-05-01T06:00:00' + } + ] + } + ] + }); + var eventObj = $('#cal').fullCalendar('clientEvents', '1')[0]; + expect(eventObj.allDay).toEqual(true); + }); + + it('a false value can override the global allDayDefault', function() { + $('#cal').fullCalendar({ + allDayDefault: true, + eventSources: [ + { + allDayDefault: false, + events: [ + { + id: '1', + start: '2014-05-01' + } + ] + } + ] + }); + var eventObj = $('#cal').fullCalendar('clientEvents', '1')[0]; + expect(eventObj.allDay).toEqual(false); + }); + + it('has no effect when an event\'s allDay is specified', function() { + $('#cal').fullCalendar({ + eventSources: [ + { + allDayDefault: true, + events: [ + { + id: '1', + start: '2014-05-01', + allDay: false + } + ] + } + ] + }); + var eventObj = $('#cal').fullCalendar('clientEvents', '1')[0]; + expect(eventObj.allDay).toEqual(false); + }); + +}); diff --git a/tests/automated/allDaySlot.js b/tests/automated/allDaySlot.js new file mode 100644 index 0000000..21aef33 --- /dev/null +++ b/tests/automated/allDaySlot.js @@ -0,0 +1,80 @@ + +describe('allDaySlots', function() { + + beforeEach(function() { + affix('#cal'); + }); + + describe('when allDaySlots is not set', function() { + describe('in agendaWeek', function() { + it('should default to having an allDaySlots table', function() { + var options = { + defaultView: 'agendaWeek' + }; + $('#cal').fullCalendar(options); + var allDaySlotCount = $('.fc-day-grid').length; + expect(allDaySlotCount).toEqual(1); + }); + }); + describe('in agendaDay', function() { + it('should default to having an allDaySlots table', function() { + var options = { + defaultView: 'agendaDay' + }; + $('#cal').fullCalendar(options); + var allDaySlotCount = $('.fc-day-grid').length; + expect(allDaySlotCount).toEqual(1); + }); + }); + }); + + describe('when allDaySlots is set true', function() { + describe('in agendaWeek', function() { + it('should default to having an allDaySlots table', function() { + var options = { + defaultView: 'agendaWeek', + allDaySlot: true + }; + $('#cal').fullCalendar(options); + var allDaySlotCount = $('.fc-day-grid').length; + expect(allDaySlotCount).toEqual(1); + }); + }); + describe('in agendaDay', function() { + it('should default to having an allDaySlots table', function() { + var options = { + defaultView: 'agendaDay', + allDaySlot: true + }; + $('#cal').fullCalendar(options); + var allDaySlotCount = $('.fc-day-grid').length; + expect(allDaySlotCount).toEqual(1); + }); + }); + }); + + describe('when allDaySlots is set false', function() { + describe('in agendaWeek', function() { + it('should default to having an allDaySlots table', function() { + var options = { + defaultView: 'agendaWeek', + allDaySlot: false + }; + $('#cal').fullCalendar(options); + var allDaySlotCount = $('.fc-day-grid').length; + expect(allDaySlotCount).toEqual(0); + }); + }); + describe('in agendaDay', function() { + it('should default to having an allDaySlots table', function() { + var options = { + defaultView: 'agendaDay', + allDaySlot: false + }; + $('#cal').fullCalendar(options); + var allDaySlotCount = $('.fc-day-grid').length; + expect(allDaySlotCount).toEqual(0); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/allDayText.js b/tests/automated/allDayText.js new file mode 100644 index 0000000..f2711e1 --- /dev/null +++ b/tests/automated/allDayText.js @@ -0,0 +1,108 @@ + +describe('allDayText', function() { + + beforeEach(function() { + affix('#cal'); + }); + + describe('when allDaySlots is not set', function() { + describe('in agendaWeek', function() { + it('should default allDayText to using \'all-day\'', function() { + var options = { + defaultView: 'agendaWeek' + }; + $('#cal').fullCalendar(options); + var allDayText = $('.fc-day-grid > .fc-row > .fc-bg .fc-axis').text(); + expect(allDayText).toEqual('all-day'); + }); + }); + describe('in agendaDay', function() { + it('should default allDayText to using \'all-day\'', function() { + var options = { + defaultView: 'agendaDay' + }; + $('#cal').fullCalendar(options); + var allDayText = $('.fc-day-grid > .fc-row > .fc-bg .fc-axis').text(); + expect(allDayText).toEqual('all-day'); + }); + }); + }); + + describe('when allDaySlots is set true', function() { + describe('in agendaWeek', function() { + it('should default allDayText to using \'all-day\'', function() { + var options = { + defaultView: 'agendaWeek', + allDaySlot: true + }; + $('#cal').fullCalendar(options); + var allDayText = $('.fc-day-grid > .fc-row > .fc-bg .fc-axis').text(); + expect(allDayText).toEqual('all-day'); + }); + }); + describe('in agendaDay', function() { + it('should default allDayText to using \'all-day\'', function() { + var options = { + defaultView: 'agendaDay', + allDaySlot: true + }; + $('#cal').fullCalendar(options); + var allDayText = $('.fc-day-grid > .fc-row > .fc-bg .fc-axis').text(); + expect(allDayText).toEqual('all-day'); + }); + }); + }); + + describe('when allDaySlots is set true and language is not default', function() { + describe('in agendaWeek', function() { + it('should use the language\'s all-day value', function() { + var options = { + defaultView: 'agendaWeek', + allDaySlot: true, + lang: 'pt-br' + }; + $('#cal').fullCalendar(options); + var allDayText = $('.fc-day-grid > .fc-row > .fc-bg .fc-axis').text(); + expect(allDayText).toEqual('dia inteiro'); + }); + }); + describe('in agendaDay', function() { + it('should use the language\'s all-day value', function() { + var options = { + defaultView: 'agendaDay', + allDaySlot: true, + lang: 'pt-br' + }; + $('#cal').fullCalendar(options); + var allDayText = $('.fc-day-grid > .fc-row > .fc-bg .fc-axis').text(); + expect(allDayText).toEqual('dia inteiro'); + }); + }); + }); + + describe('when allDaySlots is set true and allDayText is specified', function() { + describe('in agendaWeek', function() { + it('should show specified all day text', function() { + var options = { + defaultView: 'agendaWeek', + allDaySlot: true, + allDayText: 'axis-phosy' + }; + $('#cal').fullCalendar(options); + var allDayText = $('.fc-day-grid > .fc-row > .fc-bg .fc-axis').text(); + expect(allDayText).toEqual('axis-phosy'); + }); + }); + describe('in agendaDay', function() { + it('should show specified all day text', function() { + var options = { + defaultView: 'agendaDay', + allDayText: 'axis-phosy' + }; + $('#cal').fullCalendar(options); + var allDayText = $('.fc-day-grid > .fc-row > .fc-bg .fc-axis').text(); + expect(allDayText).toEqual('axis-phosy'); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/aspectRatio.js b/tests/automated/aspectRatio.js new file mode 100644 index 0000000..ced7f0a --- /dev/null +++ b/tests/automated/aspectRatio.js @@ -0,0 +1,139 @@ + +describe('aspectRatio', function() { + + beforeEach(function() { + affix('#cal'); + }); + + describe('when default settings are used', function() { + beforeEach(function() { + $('#cal').width(675); + $('#cal').fullCalendar(); + }); + it('fc-content should use the ratio 1:35 to set height', function() { + var height = $('.fc-view-container').height(); + expect(height).toEqual(500); + }); + it('fc-content should have width of div', function() { + var width = $('.fc-view-container').width(); + expect(width).toEqual(675); + }); + }); + + describe('when initializing the aspectRatio', function() { + + describe('to 2', function() { + beforeEach(function() { + $('#cal').width(1000); + $('#cal').fullCalendar({ + aspectRatio: 2 + }); + }); + it('should not change the width', function() { + var width = $('.fc-view-container').width(); + expect(width).toEqual(1000); + }); + it('should set the height to width sizes very close to ratio of 2', function() { + var width = $('.fc-view-container').width(); + var height = $('.fc-view-container').height(); + var ratio = Math.round(width / height * 100); + expect(ratio).toEqual(200); + }); + }); + + describe('to 1', function() { + beforeEach(function() { + $('#cal').width(1000); + $('#cal').fullCalendar({ + aspectRatio: 1 + }); + }); + it('should not change the width', function() { + var width = $('.fc-view-container').width(); + expect(width).toEqual(1000); + }); + it('should set the height to width sizes very close to ratio of 2', function() { + var width = $('.fc-view-container').width(); + var height = $('.fc-view-container').height(); + var ratio = Math.round(width / height * 100); + expect(ratio).toEqual(100); + }); + }); + + describe('to less than 0.5', function() { + beforeEach(function() { + $('#cal').width(1000); + $('#cal').fullCalendar({ + aspectRatio: 0.4 + }); + }); + it('should not change the width', function() { + var width = $('.fc-view-container').width(); + expect(width).toEqual(1000); + }); + it('should set the height to width ratio to 0.5', function() { + var width = $('.fc-view-container').width(); + var height = $('.fc-view-container').height(); + var ratio = Math.round(width / height * 100); + expect(ratio).toEqual(50); + }); + }); + + describe('to negative', function() { + beforeEach(function() { + $('#cal').width(1000); + $('#cal').fullCalendar({ + aspectRatio: -2 + }); + }); + it('should not change the width', function() { + var width = $('.fc-view-container').width(); + expect(width).toEqual(1000); + }); + it('should set the height to width ratio to 0.5', function() { + var width = $('.fc-view-container').width(); + var height = $('.fc-view-container').height(); + var ratio = Math.round(width / height * 100); + expect(ratio).toEqual(50); + }); + }); + + describe('to zero', function() { + beforeEach(function() { + $('#cal').width(1000); + $('#cal').fullCalendar({ + aspectRatio: 0 + }); + }); + it('should not change the width', function() { + var width = $('.fc-view-container').width(); + expect(width).toEqual(1000); + }); + it('should set the height to width ratio to 0.5', function() { + var width = $('.fc-view-container').width(); + var height = $('.fc-view-container').height(); + var ratio = Math.round(width / height * 100); + expect(ratio).toEqual(50); + }); + }); + + describe('to very large', function() { + beforeEach(function() { + $('#cal').width(1000); + $('#cal').fullCalendar({ + aspectRatio: 4000 + }); + }); + it('should not change the width', function() { + var width = $('.fc-view-container').width(); + expect(width).toEqual(1000); + }); + it('should cause rows to be natural height', function() { + var actualHeight = $('.fc-view-container').height(); + $('tr.fc-week td:first-child > div').css('min-height', '').css('background', 'red'); + var naturalHeight = $('.fc-view-container').height(); + expect(actualHeight).toEqual(naturalHeight); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/axisFormat.js b/tests/automated/axisFormat.js new file mode 100644 index 0000000..78b9489 --- /dev/null +++ b/tests/automated/axisFormat.js @@ -0,0 +1,33 @@ +describe('axisFormat', function() { + + var options; + + function getAxisText() { + return $('.fc-slats tr:first-child .fc-time').text(); + } + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-06-04', + defaultView: 'agendaWeek' + }; + }); + + it('renders correctly when default', function() { + $('#cal').fullCalendar(options); + expect(getAxisText()).toBe('12am'); + }); + + it('renders correctly when default and the language is customized', function() { + options.lang = 'en-gb'; + $('#cal').fullCalendar(options); + expect(getAxisText()).toBe('00'); + }); + + it('renders correctly when customized', function() { + options.axisFormat = 'H:mm:mm[!]'; + $('#cal').fullCalendar(options); + expect(getAxisText()).toBe('0:00:00!'); + }); +}); \ No newline at end of file diff --git a/tests/automated/basic-view.js b/tests/automated/basic-view.js new file mode 100644 index 0000000..c2c525a --- /dev/null +++ b/tests/automated/basic-view.js @@ -0,0 +1,50 @@ + +describe('basic view rendering', function() { + + beforeEach(function() { + affix('#cal'); + }); + + describe('when isRTL is false', function() { + + beforeEach(function() { + $('#cal').fullCalendar({ + defaultView: 'month', + isRTL: false + }); + }); + + it('should have have days ordered sun to sat', function() { + var fc = $('#cal').find('.fc-day-header'); + expect(fc[0]).toHaveClass('fc-sun'); + expect(fc[1]).toHaveClass('fc-mon'); + expect(fc[2]).toHaveClass('fc-tue'); + expect(fc[3]).toHaveClass('fc-wed'); + expect(fc[4]).toHaveClass('fc-thu'); + expect(fc[5]).toHaveClass('fc-fri'); + expect(fc[6]).toHaveClass('fc-sat'); + }); + }); + + describe('when isRTL is true', function() { + + beforeEach(function() { + $('#cal').fullCalendar({ + defaultView: 'month', + isRTL: true + }); + }); + + it('should have have days ordered sat to sun', function() { + var fc = $('#cal').find('.fc-day-header'); + expect(fc[0]).toHaveClass('fc-sat'); + expect(fc[1]).toHaveClass('fc-fri'); + expect(fc[2]).toHaveClass('fc-thu'); + expect(fc[3]).toHaveClass('fc-wed'); + expect(fc[4]).toHaveClass('fc-tue'); + expect(fc[5]).toHaveClass('fc-mon'); + expect(fc[6]).toHaveClass('fc-sun'); + }); + }); + +}); \ No newline at end of file diff --git a/tests/automated/buttonIcons.js b/tests/automated/buttonIcons.js new file mode 100644 index 0000000..ff70afa --- /dev/null +++ b/tests/automated/buttonIcons.js @@ -0,0 +1,92 @@ +describe('buttonIcons', function() { + + beforeEach(function() { + affix('#cal'); + }); + + describe('when buttonIcons is not set', function() { + + beforeEach(function() { + $('#cal').fullCalendar({ + header: { + left: 'prev,next today', + center: 'title', + right: 'prevYear, nextYear' + } + }); + }); + + it('should have default values', function() { + var prevBtn = $('#cal').find('.fc-prev-button'); + var nextBtn = $('#cal').find('.fc-next-button'); + var nextYearBtn = $('#cal').find('.fc-nextYear-button'); + var prevYearBtn = $('#cal').find('.fc-prevYear-button'); + + expect(prevBtn.find('span:first')).toHaveClass('fc-icon-left-single-arrow'); + expect(nextBtn.find('span:first')).toHaveClass('fc-icon-right-single-arrow'); + expect(nextYearBtn.find('span:first')).toHaveClass('fc-icon-right-double-arrow'); + expect(prevYearBtn.find('span:first')).toHaveClass('fc-icon-left-double-arrow'); + }); + }); + + describe('when buttonIcons is set and theme is falsy', function() { + + beforeEach(function() { + $('#cal').fullCalendar({ + buttonIcons: { + prev: 'some-icon-left', + next: 'some-icon-right', + prevYear: 'some-icon-leftYear', + nextYear: 'some-icon-rightYear' + }, + header: { + left: 'prev,next today', + center: 'title', + right: 'prevYear, nextYear' + } + }); + }); + + it('should have the set values', function() { + var prevBtn = $('#cal').find('.fc-prev-button'); + var nextBtn = $('#cal').find('.fc-next-button'); + var prevYearBtn = $('#cal').find('.fc-prevYear-button'); + var nextYearBtn = $('#cal').find('.fc-nextYear-button'); + + + expect(prevBtn.find('span:first')).toHaveClass('fc-icon-some-icon-left'); + expect(prevBtn.find('span:first')).toHaveClass('fc-icon-some-icon-left'); + expect(prevYearBtn.find('span:first')).toHaveClass('fc-icon-some-icon-leftYear'); + expect(nextYearBtn.find('span:first')).toHaveClass('fc-icon-some-icon-rightYear'); + }); + }); + + describe('when theme is true', function() { + + beforeEach(function() { + $('#cal').fullCalendar({ + theme: true, + header: { + left: 'prev,next today', + center: 'title', + right: 'prevYear, nextYear' + } + }); + }); + + it('buttonIcons is ignored', function() { + var prevBtn = $('#cal').find('.fc-prev-button'); + var nextBtn = $('#cal').find('.fc-next-button'); + var prevYearBtn = $('#cal').find('.fc-prevYear-button'); + var nextYearBtn = $('#cal').find('.fc-nextYear-button'); + + var classesToSearch = [ '.fc-icon-left-single-arrow', '.fc-icon-right-double-arrow', + '.fc-icon-right-single-arrow', '.fc-icon-left-double-arrow' ]; + + for (var i = 0; i < classesToSearch.length; i++) { + var cls = classesToSearch[i]; + expect($('#cal').find(cls).length).toBe(0); + }; + }); + }); +}); diff --git a/tests/automated/buttonText.js b/tests/automated/buttonText.js new file mode 100644 index 0000000..4835d0c --- /dev/null +++ b/tests/automated/buttonText.js @@ -0,0 +1,225 @@ +describe('button text', function() { + + var settings; + + beforeEach(function() { + affix('#cal'); + settings = { + header: { + left: 'prevYear,prev,today,next,nextYear', + center: '', + right: 'month,basicWeek,basicDay,agendaWeek,agendaDay' + } + }; + }); + + describe('with default language', function() { + + describe('with default buttonIcons', function() { + + it('should contain default text values', function() { + $('#cal').fullCalendar(settings); + + // will have button icons, to text will be empty + expect($('.fc-next-button')).toHaveText(''); + expect($('.fc-nextYear-button')).toHaveText(''); + expect($('.fc-prev-button')).toHaveText(''); + expect($('.fc-prevYear-button')).toHaveText(''); + + expect($('.fc-today-button')).toHaveText('today'); + expect($('.fc-month-button')).toHaveText('month'); + expect($('.fc-basicWeek-button')).toHaveText('week'); + expect($('.fc-agendaWeek-button')).toHaveText('week'); + expect($('.fc-basicDay-button')).toHaveText('day'); + expect($('.fc-agendaDay-button')).toHaveText('day'); + }); + + it('should contain specified text values', function() { + settings.buttonText = { + prev: '<-', + next: '->', + prevYear: '<--', + nextYear: '-->', + today: 'tidei', + month: 'mun', + week: 'wiki', + day: 'dei' + }; + $('#cal').fullCalendar(settings); + + expect($('.fc-next-button')).toHaveText('->'); + expect($('.fc-nextYear-button')).toHaveText('-->'); + expect($('.fc-prev-button')).toHaveText('<-'); + expect($('.fc-prevYear-button')).toHaveText('<--'); + + expect($('.fc-today-button')).toHaveText('tidei'); + expect($('.fc-month-button')).toHaveText('mun'); + expect($('.fc-agendaDay-button')).toHaveText('dei'); + expect($('.fc-agendaWeek-button')).toHaveText('wiki'); + expect($('.fc-basicDay-button')).toHaveText('dei'); + expect($('.fc-basicWeek-button')).toHaveText('wiki'); + }); + + }); + + describe('with buttonIcons turned off', function() { + + beforeEach(function() { + settings.buttonIcons = false; + }); + + it('should contain default text values', function() { + $('#cal').fullCalendar(settings); + + // will have actual text now + expect($('.fc-next-button')).toHaveText('next'); + expect($('.fc-nextYear-button')).toHaveText('next year'); + expect($('.fc-prev-button')).toHaveText('prev'); + expect($('.fc-prevYear-button')).toHaveText('prev year'); + + expect($('.fc-today-button')).toHaveText('today'); + expect($('.fc-month-button')).toHaveText('month'); + expect($('.fc-basicWeek-button')).toHaveText('week'); + expect($('.fc-agendaWeek-button')).toHaveText('week'); + expect($('.fc-basicDay-button')).toHaveText('day'); + expect($('.fc-agendaDay-button')).toHaveText('day'); + }); + + it('should contain specified text values', function() { + settings.buttonText = { + prev: '<-', + next: '->', + prevYear: '<--', + nextYear: '-->', + today: 'tidei', + month: 'mun', + week: 'wiki', + day: 'dei' + }; + $('#cal').fullCalendar(settings); + + expect($('.fc-next-button')).toHaveText('->'); + expect($('.fc-nextYear-button')).toHaveText('-->'); + expect($('.fc-prev-button')).toHaveText('<-'); + expect($('.fc-prevYear-button')).toHaveText('<--'); + + expect($('.fc-today-button')).toHaveText('tidei'); + expect($('.fc-month-button')).toHaveText('mun'); + expect($('.fc-agendaDay-button')).toHaveText('dei'); + expect($('.fc-agendaWeek-button')).toHaveText('wiki'); + expect($('.fc-basicDay-button')).toHaveText('dei'); + expect($('.fc-basicWeek-button')).toHaveText('wiki'); + }); + + }); + + }); + + describe('when lang is not default', function() { + + beforeEach(function() { + settings.lang = 'fr'; + }); + + describe('with default buttonIcons', function() { + + it('should contain default text values', function() { + $('#cal').fullCalendar(settings); + + // will contain icons, so will contain no text + expect($('.fc-next-button')).toHaveText(''); + expect($('.fc-nextYear-button')).toHaveText(''); + expect($('.fc-prev-button')).toHaveText(''); + expect($('.fc-prevYear-button')).toHaveText(''); + + expect($('.fc-today-button')).toHaveText('Aujourd\'hui'); + expect($('.fc-month-button')).toHaveText('Mois'); + expect($('.fc-basicWeek-button')).toHaveText('Semaine'); + expect($('.fc-agendaWeek-button')).toHaveText('Semaine'); + expect($('.fc-basicDay-button')).toHaveText('Jour'); + expect($('.fc-agendaDay-button')).toHaveText('Jour'); + }); + + it('should contain specified text values', function() { + settings.buttonText = { + prev: '<-', + next: '->', + prevYear: '<--', + nextYear: '-->', + today: 'tidei', + month: 'mun', + week: 'wiki', + day: 'dei' + }; + $('#cal').fullCalendar(settings); + + expect($('.fc-next-button')).toHaveText('->'); + expect($('.fc-nextYear-button')).toHaveText('-->'); + expect($('.fc-prev-button')).toHaveText('<-'); + expect($('.fc-prevYear-button')).toHaveText('<--'); + + expect($('.fc-today-button')).toHaveText('tidei'); + expect($('.fc-month-button')).toHaveText('mun'); + expect($('.fc-agendaDay-button')).toHaveText('dei'); + expect($('.fc-agendaWeek-button')).toHaveText('wiki'); + expect($('.fc-basicDay-button')).toHaveText('dei'); + expect($('.fc-basicWeek-button')).toHaveText('wiki'); + }); + + }); + + describe('with buttonIcons turned off', function() { + + beforeEach(function() { + settings.buttonIcons = false; + }); + + it('should contain default text values', function() { + $('#cal').fullCalendar(settings); + + // will have the language's actual text now + expect($('.fc-next-button')).toHaveText('Suivant'); + expect($('.fc-prev-button')).toHaveText('Précédent'); + //// languages files don't have data for prev/next *year* + //expect($('.fc-nextYear-button')).toHaveText('Suivant'); + //expect($('.fc-prevYear-button')).toHaveText('Précédent'); + + expect($('.fc-today-button')).toHaveText('Aujourd\'hui'); + expect($('.fc-month-button')).toHaveText('Mois'); + expect($('.fc-basicWeek-button')).toHaveText('Semaine'); + expect($('.fc-agendaWeek-button')).toHaveText('Semaine'); + expect($('.fc-basicDay-button')).toHaveText('Jour'); + expect($('.fc-agendaDay-button')).toHaveText('Jour'); + }); + + it('should contain specified text values', function() { + settings.buttonText = { + prev: '<-', + next: '->', + prevYear: '<--', + nextYear: '-->', + today: 'tidei', + month: 'mun', + week: 'wiki', + day: 'dei' + }; + $('#cal').fullCalendar(settings); + + expect($('.fc-next-button')).toHaveText('->'); + expect($('.fc-nextYear-button')).toHaveText('-->'); + expect($('.fc-prev-button')).toHaveText('<-'); + expect($('.fc-prevYear-button')).toHaveText('<--'); + + expect($('.fc-today-button')).toHaveText('tidei'); + expect($('.fc-month-button')).toHaveText('mun'); + expect($('.fc-agendaDay-button')).toHaveText('dei'); + expect($('.fc-agendaWeek-button')).toHaveText('wiki'); + expect($('.fc-basicDay-button')).toHaveText('dei'); + expect($('.fc-basicWeek-button')).toHaveText('wiki'); + }); + + }); + + }); + +}); diff --git a/tests/automated/columnFormat.js b/tests/automated/columnFormat.js new file mode 100644 index 0000000..e13d8d5 --- /dev/null +++ b/tests/automated/columnFormat.js @@ -0,0 +1,115 @@ +describe('columnFormat', function() { + + beforeEach(function() { + affix('#cal'); + }); + + describe('when columnFormat is not set', function() { + + var viewWithFormat = [ { view: 'month', expected: 'Sun', selector: 'th.fc-day-header.fc-sun' }, + { view: 'basicWeek', expected: 'Sun 5/11', selector: 'th.fc-day-header.fc-sun' }, + { view: 'agendaWeek', expected: 'Sun 5/11', selector: 'th.fc-widget-header.fc-sun' }, + { view: 'basicDay', expected: 'Sunday', selector: 'th.fc-day-header.fc-sun' }, + { view: 'agendaDay', expected: 'Sunday', selector: 'th.fc-widget-header.fc-sun' } ]; + + beforeEach(function() { + $('#cal').fullCalendar({ + defaultDate: '2014-05-11' + }); + }); + + it('should have default values', function() { + var cal = $('#cal'); + + for (var i = 0; i < viewWithFormat.length; i++) { + var crtView = viewWithFormat[i]; + cal.fullCalendar('changeView', crtView.view); + expect(cal.find(crtView.selector).text()).toBe(crtView.expected); + }; + }); + }); + + describe('when columnFormat is set on a per-view basis', function() { + + var viewWithFormat = [ { view: 'month', expected: 'Sunday', selector: 'th.fc-day-header.fc-sun' }, + { view: 'basicWeek', expected: 'Sunday 11 - 5', selector: 'th.fc-day-header.fc-sun' }, + { view: 'agendaWeek', expected: 'Sunday 11 , 5', selector: 'th.fc-widget-header.fc-sun' }, + { view: 'basicDay', expected: 'Sunday 11 | 5', selector: 'th.fc-day-header.fc-sun' }, + { view: 'agendaDay', expected: 'Sunday 5/11', selector: 'th.fc-widget-header.fc-sun' } ]; + + beforeEach(function() { + $('#cal').fullCalendar({ + defaultDate: '2014-05-11', + columnFormat: { + month: 'dddd', + agendaDay: 'dddd M/D', + agendaWeek: 'dddd D , M', + basicDay: 'dddd D | M', + basicWeek: 'dddd D - M' + } + }); + }); + + it('should have the correct values', function() { + var cal = $('#cal'); + + for (var i = 0; i < viewWithFormat.length; i++) { + var crtView = viewWithFormat[i]; + cal.fullCalendar('changeView', crtView.view); + expect(cal.find(crtView.selector).text()).toBe(crtView.expected); + }; + }); + }); + + describe('when lang is French', function() { + + var viewWithFormat = [ { view: 'month', expected: 'dim.', selector: 'th.fc-day-header.fc-sun' }, + { view: 'basicWeek', expected: 'dim. 11/05', selector: 'th.fc-day-header.fc-sun' }, + { view: 'agendaWeek', expected: 'dim. 11/05', selector: 'th.fc-widget-header.fc-sun' }, + { view: 'basicDay', expected: 'dimanche', selector: 'th.fc-day-header.fc-sun' }, + { view: 'agendaDay', expected: 'dimanche', selector: 'th.fc-widget-header.fc-sun' } ]; + + beforeEach(function() { + $('#cal').fullCalendar({ + defaultDate: '2014-05-11', + lang: 'fr' + }); + }); + + it('should have the translated dates', function() { + var cal = $('#cal'); + + for (var i = 0; i < viewWithFormat.length; i++) { + var crtView = viewWithFormat[i]; + cal.fullCalendar('changeView', crtView.view); + expect(cal.find(crtView.selector).text()).toBe(crtView.expected); + }; + }); + }); + + describe('when lang is Korean', function() { + + var viewWithFormat = [ { view: 'month', expected: '일', selector: 'th.fc-day-header.fc-sun' }, + { view: 'basicWeek', expected: '일 05.11', selector: 'th.fc-day-header.fc-sun' }, + { view: 'agendaWeek', expected: '일 05.11', selector: 'th.fc-widget-header.fc-sun' }, + { view: 'basicDay', expected: '일요일', selector: 'th.fc-day-header.fc-sun' }, + { view: 'agendaDay', expected: '일요일', selector: 'th.fc-widget-header.fc-sun' } ]; + + beforeEach(function() { + $('#cal').fullCalendar({ + defaultDate: '2014-05-11', + lang: 'ko' + }); + }); + + it('should have the translated dates and columnFormat should be computed differently', function() { + var cal = $('#cal'); + + for (var i = 0; i < viewWithFormat.length; i++) { + var crtView = viewWithFormat[i]; + cal.fullCalendar('changeView', crtView.view); + expect(cal.find(crtView.selector).text()).toBe(crtView.expected); + }; + }); + }); +}); diff --git a/tests/automated/constructor.js b/tests/automated/constructor.js new file mode 100644 index 0000000..552e48d --- /dev/null +++ b/tests/automated/constructor.js @@ -0,0 +1,87 @@ + +describe('constructor', function() { + + beforeEach(function() { + affix('#calendar'); + }); + + it('should return a jQuery object for chaining', function() { + var res = $('#calendar').fullCalendar(); + expect(res instanceof jQuery).toBe(true); + }); + + it('should not modify the options object', function() { + var options = { + defaultView: 'agendaWeek', + scrollTime: '09:00:00', + slotDuration: { minutes: 45 } + }; + var optionsCopy = $.extend({}, options, true); + $('#calendar').fullCalendar(options); + expect(options).toEqual(optionsCopy); + }); + + it('should not modify the events array', function() { + var options = { + defaultView: 'month', + defaultDate: '2014-05-27', + events: [ + { + title: 'mytitle', + start: '2014-05-27' + } + ] + }; + var optionsCopy = $.extend(true, {}, options); // recursive copy + $('#calendar').fullCalendar(options); + expect(options).toEqual(optionsCopy); + }); + + it('should not modify the eventSources array', function() { + var options = { + defaultView: 'month', + defaultDate: '2014-05-27', + eventSources: [ + { events: [ + { + title: 'mytitle', + start: '2014-05-27' + } + ] } + ] + }; + var optionsCopy = $.extend(true, {}, options); // recursive copy + $('#calendar').fullCalendar(options); + expect(options).toEqual(optionsCopy); + }); + + describe('when called on a div', function() { + + beforeEach(function() { + $('#calendar').fullCalendar(); + }); + + it('should contain a table fc-toolbar', function() { + var header = $('#calendar > .fc-toolbar'); + expect(header[0]).not.toBeUndefined(); + }); + + it('should contain a div fc-view-container', function() { + var content = ($('#calendar > .fc-view-container')); + expect(content[0]).not.toBeUndefined(); + }); + + it('should only contain 2 elements', function() { + var calenderNodeCount = $('#calendar >').length; + expect(calenderNodeCount).toEqual(2); + }); + + describe('and then called again', function() { + it('should still only have a single set of calendar [header,content]', function() { + $('#calendar').fullCalendar(); + var count = $('#calendar >').length; + expect(count).toEqual(2); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/current-date.js b/tests/automated/current-date.js new file mode 100644 index 0000000..a6e9f29 --- /dev/null +++ b/tests/automated/current-date.js @@ -0,0 +1,290 @@ +describe('current date', function() { + + var TITLE_FORMAT = 'MMMM D YYYY'; + var options; + + beforeEach(function() { + affix('#cal'); + options = { + titleFormat: TITLE_FORMAT, + defaultDate: '2014-06-01' + }; + }); + + describe('defaultDate & getDate', function() { + describeWhenInMonth(function() { + it('should initialize at the date', function() { + options.defaultDate = '2011-03-10'; + $('#cal').fullCalendar(options); + expectViewDates('2011-02-27', '2011-04-10', '2011-03-01'); + var currentDate = $('#cal').fullCalendar('getDate'); + expect(moment.isMoment(currentDate)).toEqual(true); // test the type, but only here + expect(currentDate).toEqualMoment('2011-03-10'); + }); + }); + describeWhenInWeek(function() { + it('should initialize at the date, given a date string', function() { + options.defaultDate = '2011-03-10'; + $('#cal').fullCalendar(options); + expectViewDates('2011-03-06', '2011-03-13'); + expect($('#cal').fullCalendar('getDate')).toEqualMoment('2011-03-10'); + }); + it('should initialize at the date, given a Moment object', function() { + options.defaultDate = $.fullCalendar.moment('2011-03-10'); + $('#cal').fullCalendar(options); + expectViewDates('2011-03-06', '2011-03-13'); + expect($('#cal').fullCalendar('getDate')).toEqualMoment('2011-03-10'); + }); + }); + describeWhenInDay(function() { + it('should initialize at the date', function() { + options.defaultDate = '2011-03-10'; + $('#cal').fullCalendar(options); + expectViewDates('2011-03-10'); + expect($('#cal').fullCalendar('getDate')).toEqualMoment('2011-03-10'); + }); + }); + }); + + describe('gotoDate', function() { + describeWhenInMonth(function() { + it('should go to a date when given a date string', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('gotoDate', '2015-04-01'); + expectViewDates('2015-03-29', '2015-05-10', '2015-04-01'); + }); + }); + describeWhenInWeek(function() { + it('should go to a date when given a date string', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('gotoDate', '2015-04-01'); + expectViewDates('2015-03-29', '2015-04-05'); + }); + it('should go to a date when given a date string with a time', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('gotoDate', '2015-04-01T12:00:00'); + expectViewDates('2015-03-29', '2015-04-05'); + }); + it('should go to a date when given a moment object', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('gotoDate', $.fullCalendar.moment('2015-04-01')); + expectViewDates('2015-03-29', '2015-04-05'); + }); + }); + describeWhenInDay(function() { + it('should go to a date when given a date string', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('gotoDate', '2015-04-01'); + expectViewDates('2015-04-01'); + }); + }); + }); + + describe('incrementDate', function() { + describeWhenInMonth(function() { + it('should increment the date when given a Duration object', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('incrementDate', { months: -1 }); + expectViewDates('2014-04-27', '2014-06-08', '2014-05-01'); + }); + }); + describeWhenInWeek(function() { + it('should increment the date when given a Duration object', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('incrementDate', { weeks: -2 }); + expectViewDates('2014-05-18', '2014-05-25'); + }); + }); + describeWhenInDay(function() { + it('should increment the date when given a Duration object', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('incrementDate', { days: 2 }); + expectViewDates('2014-06-03'); + }); + it('should increment the date when given a Duration string', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('incrementDate', '2.00:00:00'); + expectViewDates('2014-06-03'); + }); + it('should increment the date when given a Duration string with a time', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('incrementDate', '2.05:30:00'); + expectViewDates('2014-06-03'); + }); + }); + }); + + describe('prev', function() { + describeWhenInMonth(function() { + it('should move the calendar back a month', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('prev'); + expectViewDates('2014-04-27', '2014-06-08', '2014-05-01'); + }); + }); + describeWhenInWeek(function() { + it('should move the calendar back a week', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('prev'); + expectViewDates('2014-05-25', '2014-06-01'); + }); + }); + describeWhenInDay(function() { + it('should move the calendar back a week', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('prev'); + expectViewDates('2014-05-31'); + }); + }); + }); + + describe('next', function() { + describeWhenInMonth(function() { + it('should move the calendar forward a month', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('next'); + expectViewDates('2014-06-29', '2014-08-10', '2014-07-01'); + }); + }); + describeWhenInWeek(function() { + it('should move the calendar forward a week', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('next'); + expectViewDates('2014-06-08', '2014-06-15'); + }); + }); + describeWhenInDay(function() { + it('should move the calendar forward a week', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('next'); + expectViewDates('2014-06-02'); + }); + }); + }); + + describe('prevYear', function() { + describeWhenInMonth(function() { + it('should move the calendar back a year', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('prevYear'); + expectViewDates('2013-05-26', '2013-07-07', '2013-06-01'); + }); + }); + describeWhenInWeek(function() { + it('should move the calendar back a year', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('prevYear'); + expectViewDates('2013-05-26', '2013-06-02'); + }); + }); + describeWhenInDay(function() { + it('should move the calendar back a year', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('prevYear'); + expectViewDates('2013-06-01'); + }); + }); + }); + + describe('nextYear', function() { + describeWhenInMonth(function() { + it('should move the calendar forward a year', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('nextYear'); + expectViewDates('2015-05-31', '2015-07-12', '2015-06-01'); + }); + }); + describeWhenInWeek(function() { + it('should move the calendar forward a year', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('nextYear'); + expectViewDates('2015-05-31', '2015-06-07'); + }); + }); + describeWhenInDay(function() { + it('should move the calendar forward a year', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('nextYear'); + expectViewDates('2015-06-01'); + }); + }); + }); + + describe('today', function() { + beforeEach(function() { + options.now = '2016-03-15'; // the "today" date + }); + describeWhenInMonth(function() { + it('should move the calendar to now', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('today'); + expectViewDates('2016-02-28', '2016-04-10', '2016-03-01'); + }); + }); + describeWhenInWeek(function() { + it('should move the calendar to now', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('today'); + expectViewDates('2016-03-13', '2016-03-20'); + }); + }); + describeWhenInDay(function() { + it('should move the calendar to now', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('today'); + expectViewDates('2016-03-15'); + }); + }); + }); + + + // UTILS + // ----- + + function describeWhenInMonth(func) { + describeWhenIn('month', func); + } + + function describeWhenInWeek(func) { + describeWhenIn('basicWeek', func); + describeWhenIn('agendaWeek', func); + } + + function describeWhenInDay(func) { + describeWhenIn('basicDay', func); + describeWhenIn('agendaDay', func); + } + + function describeWhenIn(viewName, func) { + describe('when in ' + viewName, function() { + beforeEach(function() { + options.defaultView = viewName; + }); + func(); + }); + } + + function expectViewDates(start, end, titleDate) { + var view = $('#cal').fullCalendar('getView'); + var calculatedEnd; + var title; + + start = $.fullCalendar.moment(start); + calculatedEnd = end ? $.fullCalendar.moment(end) : start.clone().add(1, 'days'); + expect(start).toEqualMoment(view.start); + expect(calculatedEnd).toEqualMoment(view.end); + + if (titleDate || !end) { + title = $.fullCalendar.moment(titleDate || start).format(TITLE_FORMAT); + } + else { + title = $.fullCalendar.formatRange( + start, + calculatedEnd.clone().add(-1, 'ms'), + TITLE_FORMAT + ); + } + expect($('.fc-toolbar h2')).toContainText(title); + } + +}); diff --git a/tests/automated/dayClick.js b/tests/automated/dayClick.js new file mode 100644 index 0000000..99d3c3e --- /dev/null +++ b/tests/automated/dayClick.js @@ -0,0 +1,144 @@ +describe('dayClick', function() { + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-05-27', + selectable: false + }; + }); + + afterEach(function() { + $('#cal').fullCalendar('destroy'); + }); + + [ false, true ].forEach(function(isRTL) { + describe('when isRTL is ' + isRTL, function() { + beforeEach(function() { + options.isRTL = isRTL; + }); + [ false, true ].forEach(function(selectable) { + describe('when selectable is ' + selectable, function() { + beforeEach(function() { + options.selectable = selectable; + }); + + describe('when in month view', function() { + beforeEach(function() { + options.defaultView = 'month'; + }); + it('fires correctly when clicking on a cell', function(done) { + options.dayClick = function(date, jsEvent, view) { + expect(moment.isMoment(date)).toEqual(true); + expect(typeof jsEvent).toEqual('object'); // TODO: more descrimination + expect(typeof view).toEqual('object'); // " + expect(date.hasTime()).toEqual(false); + expect(date).toEqualMoment('2014-05-07'); + }; + spyOn(options, 'dayClick').and.callThrough(); + $('#cal').fullCalendar(options); + + var dayCell = $('.fc-day:eq(10)'); // 2014-05-07 (regardless of isRTL) + + // for simulating the mousedown/mouseup/click (relevant for selectable) + dayCell.simulate('drag-n-drop', { + callback: function() { + dayCell.simulate('click'); + expect(options.dayClick).toHaveBeenCalled(); + done(); + } + }); + }); + }); + + describe('when in agendaWeek view', function() { + beforeEach(function() { + options.defaultView = 'agendaWeek'; + }); + it('fires correctly when clicking on an all-day slot', function(done) { + options.dayClick = function(date, jsEvent, view) { + expect(moment.isMoment(date)).toEqual(true); + expect(typeof jsEvent).toEqual('object'); // TODO: more descrimination + expect(typeof view).toEqual('object'); // " + expect(date.hasTime()).toEqual(false); + expect(date).toEqualMoment('2014-05-28'); + }; + spyOn(options, 'dayClick').and.callThrough(); + $('#cal').fullCalendar(options); + + // 2014-05-28 (regardless of isRTL) + var dayContent = $('.fc-agenda-view .fc-day-grid .fc-day:eq(3)'); + + // for simulating the mousedown/mouseup/click (relevant for selectable) + dayContent.simulate('drag-n-drop', { + callback: function() { + dayContent.simulate('click'); + expect(options.dayClick).toHaveBeenCalled(); + done(); + } + }); + }); + it('fires correctly when clicking on a timed slot', function(done) { + + // make sure the click slot will be in scroll view + options.contentHeight = 500; + options.scrollTime = '07:00:00'; + + options.dayClick = function(date, jsEvent, view) { + expect(moment.isMoment(date)).toEqual(true); + expect(typeof jsEvent).toEqual('object'); // TODO: more descrimination + expect(typeof view).toEqual('object'); // " + expect(date.hasTime()).toEqual(true); + expect(date).toEqualMoment('2014-05-28T09:00:00'); + }; + spyOn(options, 'dayClick').and.callThrough(); + $('#cal').fullCalendar(options); + + // the middle is 2014-05-28T09:00:00 (regardless of isRTL) + var slotRow = $('.fc-slats tr:eq(18) td:not(.fc-time)'); + + // for simulating the mousedown/mouseup/click (relevant for selectable) + slotRow.simulate('drag-n-drop', { + callback: function() { + expect(options.dayClick).toHaveBeenCalled(); + done(); + } + }); + }); + + // issue 2217 + it('fires correctly when clicking on a timed slot, with minTime set', function(done) { + + // make sure the click slot will be in scroll view + options.contentHeight = 500; + options.scrollTime = '07:00:00'; + options.minTime = '02:00:00'; + + options.dayClick = function(date, jsEvent, view) { + expect(moment.isMoment(date)).toEqual(true); + expect(typeof jsEvent).toEqual('object'); // TODO: more descrimination + expect(typeof view).toEqual('object'); // " + expect(date.hasTime()).toEqual(true); + expect(date).toEqualMoment('2014-05-28T11:00:00'); + }; + spyOn(options, 'dayClick').and.callThrough(); + $('#cal').fullCalendar(options); + + // the middle is 2014-05-28T11:00:00 (regardless of isRTL) + var slotRow = $('.fc-slats tr:eq(18) td:not(.fc-time)'); + + // for simulating the mousedown/mouseup/click (relevant for selectable) + slotRow.simulate('drag-n-drop', { + callback: function() { + expect(options.dayClick).toHaveBeenCalled(); + done(); + } + }); + }); + }); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/dayNames.js b/tests/automated/dayNames.js new file mode 100644 index 0000000..b68d439 --- /dev/null +++ b/tests/automated/dayNames.js @@ -0,0 +1,94 @@ +describe('day names', function() { + var settings = {}; + var testableClasses = [ + 'basicDay', + 'agendaDay' + ]; + var dayClasses = [ + '.fc-sun', + '.fc-mon', + '.fc-tue', + '.fc-wed', + '.fc-thu', + '.fc-fri', + '.fc-sat' + ]; + var referenceDate = '2014-05-25 06:00'; // A sunday + var languages = [ 'es', 'fr', 'de', 'zh-cn', 'nl' ]; + + beforeEach(function() { + affix('#cal'); + settings = { + now: moment(referenceDate).toISOString() + }; + }); + + afterEach(function() { + moment.lang('en'); // reset moment's global language + }); + + testableClasses.forEach(function(viewClass, index, viewClasses) { + describe('when view is basicDay', function() { + beforeEach(function() { + settings.defaultView = 'basicDay'; + }); + + describe('when lang is default', function() { + beforeEach(function() { + settings.lang = 'en'; + }); + + dayClasses.forEach(function(cls, index, classes) { + var weekdays = moment.weekdays(); + it('should be ' + weekdays[index], function() { + settings.now = moment(referenceDate).add(index, 'days'); + $('#cal').fullCalendar(settings); + + expect($('.fc-view thead ' + dayClasses[index])).toHaveText(weekdays[index]); + }); + }); + }); + + $.each(languages, function(index, language) { + describe('when lang is ' + language, function() { + beforeEach(function() { + moment.lang(language); + }); + + dayClasses.forEach(function(cls, index, classes) { + it('should be the translation for ' + moment.weekdays()[index], function() { + settings.lang = language; + settings.now = moment(referenceDate).add(index, 'days'); + $('#cal').fullCalendar(settings); + + expect($('.fc-view thead ' + dayClasses[index])).toHaveText(moment.weekdays()[index]); + }); + }); + }); + }); + + describe('when daynames are specified', function() { + var weekdays = [ + 'Hovjaj', + 'maSjaj', + 'veSjaj', + 'mechjaj', + 'jevjaj', + 'parmaqjaj', + 'HoSjaj' + ]; + + dayClasses.forEach(function(cls, idx, classes) { + it('should be ' + weekdays[idx], function() { + settings.dayNames = [].slice.call(weekdays); // copy. in case there is a mutation + settings.now = moment(referenceDate).add(idx, 'days'); + + $('#cal').fullCalendar(settings); + + expect($('.fc-view thead ' + cls)).toHaveText(weekdays[idx]); + }); + }); + }); + }); + }); +}); diff --git a/tests/automated/dayNamesShort.js b/tests/automated/dayNamesShort.js new file mode 100644 index 0000000..a17805d --- /dev/null +++ b/tests/automated/dayNamesShort.js @@ -0,0 +1,78 @@ +describe('short day names', function() { + var settings = {}; + var testableClasses = [ + 'month', + 'agendaWeek', + 'basicWeek' + ]; + var dayClasses = [ + '.fc-sun', + '.fc-mon', + '.fc-tue', + '.fc-wed', + '.fc-thu', + '.fc-fri', + '.fc-sat' + ]; + var languages = [ 'es', 'fr', 'de', 'zh-cn', 'es' ]; + + beforeEach(function() { + affix('#cal'); + settings = { }; + }); + + afterEach(function() { + moment.lang('en'); // reset moment's global language + }); + + testableClasses.forEach(function(viewClass, index, viewClasses) { + describe('when view is ' + viewClass, function() { + beforeEach(function() { + settings.defaultView = viewClass; + }); + + describe('when lang is default', function() { + it('should be in English', function() { + moment.lang('en'); + $('#cal').fullCalendar(settings); + var weekdays = moment.weekdaysShort(); + + dayClasses.forEach(function(cls, index, classes) { + expect($('.fc-view thead ' + cls)[0]).toContainText(weekdays[index]); + }); + }); + }); + + describe('when lang is not default', function() { + languages.forEach(function(language, index, languages) { + it('should be in the selected language', function() { + settings.lang = language; + $('#cal').fullCalendar(settings); + + moment.lang(language); + var dow = moment.langData(language)._week.dow; + var weekdays = moment.weekdaysShort(); + + dayClasses.forEach(function(cls, index, classes) { + expect($('.fc-view thead ' + cls)[0]).toContainText(weekdays[index]); + }); + }); + }); + }); + + describe('when specified', function() { + it('should contain the specified names in the given order', function() { + var days = [ + 'Hov.', 'maS.', 'veS.', 'mech.', 'parmaq.', 'HoS.' + ]; + settings.dayNamesShort = days; + $('#cal').fullCalendar(settings); + + dayClasses.forEach(function(cls, index, classes) { + expect($('.fc-view thead ' + cls)[0]).toContainText(days[index]); + }); + }); + }); + }); + }); +}); diff --git a/tests/automated/dayPopoverFormat.js b/tests/automated/dayPopoverFormat.js new file mode 100644 index 0000000..dfefac5 --- /dev/null +++ b/tests/automated/dayPopoverFormat.js @@ -0,0 +1,43 @@ + +describe('dayPopoverFormat', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-08-01', + eventLimit: 3, + events: [ + { title: 'event1', start: '2014-07-28', end: '2014-07-30', className: 'event1' }, + { title: 'event2', start: '2014-07-29', end: '2014-07-31', className: 'event2' }, + { title: 'event3', start: '2014-07-29', className: 'event3' }, + { title: 'event4', start: '2014-07-29', className: 'event4' } + ] + }; + }); + + function init() { + $('#cal').fullCalendar(options); + $('.fc-more').simulate('click'); + } + + it('can be set to a custom value', function() { + options.dayPopoverFormat = 'ddd, MMMM'; + init(); + expect($('.fc-more-popover > .fc-header .fc-title')).toHaveText('Tue, July'); + }); + + it('is affected by the current locale when the value is default', function() { + options.lang = 'fr'; + init(); + expect($('.fc-more-popover > .fc-header .fc-title')).toHaveText('29 juillet 2014'); + }); + + it('still maintains the same format when explicitly set, and there is a lang', function() { + options.lang = 'fr'; + options.dayPopoverFormat = 'YYYY'; + init(); + expect($('.fc-more-popover > .fc-header .fc-title')).toHaveText('2014'); + }); +}); diff --git a/tests/automated/dayRender.js b/tests/automated/dayRender.js new file mode 100644 index 0000000..35b4371 --- /dev/null +++ b/tests/automated/dayRender.js @@ -0,0 +1,86 @@ + +describe('dayRender', function() { + + beforeEach(function() { + affix('#cal'); + }); + + it('is triggered upon initialization of a view, with correct parameters', function() { + var options = { + defaultView: 'month', + weekMode: 'fixed', + defaultDate: '2014-05-01', + dayRender: function(date, cell) { + expect(moment.isMoment(date)).toEqual(true); + expect(date.hasTime()).toEqual(false); + expect(date.format()).toEqual(cell.data('date')); + expect(cell).toBeInDOM(); + } + }; + + spyOn(options, 'dayRender').and.callThrough(); + $('#cal').fullCalendar(options); + expect(options.dayRender.calls.count()).toEqual(42); + }); + + it('is called when view is changed', function() { + var options = { + defaultView: 'month', + weekMode: 'fixed', + defaultDate: '2014-05-01', + dayRender: function(date, cell) { } + }; + + spyOn(options, 'dayRender').and.callThrough(); + $('#cal').fullCalendar(options); + options.dayRender.calls.reset(); + $('#cal').fullCalendar('changeView', 'basicWeek'); + expect(options.dayRender.calls.count()).toEqual(7); + }); + + // called if the date is navigated to a different visible range + it('is called when view is changed', function() { + var options = { + defaultView: 'basicWeek', + defaultDate: '2014-05-01', + dayRender: function(date, cell) { } + }; + + spyOn(options, 'dayRender').and.callThrough(); + $('#cal').fullCalendar(options); + options.dayRender.calls.reset(); + $('#cal').fullCalendar('gotoDate', '2014-05-04'); // a day in the next week + expect(options.dayRender.calls.count()).toEqual(7); + }); + + it('won\'t be called when date is navigated but remains in the current visible range', function() { + var options = { + defaultView: 'basicWeek', + defaultDate: '2014-05-01', + dayRender: function(date, cell) { } + }; + + spyOn(options, 'dayRender').and.callThrough(); + $('#cal').fullCalendar(options); + options.dayRender.calls.reset(); + $('#cal').fullCalendar('gotoDate', '2014-05-02'); // a day in the same week + expect(options.dayRender.calls.count()).toEqual(0); + }); + + it('allows you to modify the element', function() { + var options = { + defaultView: 'month', + weekMode: 'fixed', + defaultDate: '2014-05-01', + dayRender: function(date, cell) { + if (date.isSame('2014-05-01')) { + cell.addClass('mycustomclass'); + } + } + }; + + $('#cal').fullCalendar(options); + expect($('#cal td[data-date="2014-05-01"]')).toHaveClass('mycustomclass'); + }); + +}); \ No newline at end of file diff --git a/tests/automated/defaultAllDayEventDuration.js b/tests/automated/defaultAllDayEventDuration.js new file mode 100644 index 0000000..c2914e9 --- /dev/null +++ b/tests/automated/defaultAllDayEventDuration.js @@ -0,0 +1,87 @@ +describe('defaultAllDayEventDuration', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + + options = { + defaultDate: '2014-05-01', + defaultView: 'month' + }; + }); + + describe('when forceEventDuration is on', function() { + + beforeEach(function() { + options.forceEventDuration = true; + }); + + it('correctly calculates an unspecified end when using a Duration object input', function() { + options.defaultAllDayEventDuration = { days: 2 }; + options.events = [ + { + allDay: true, + start: '2014-05-05' + } + ]; + $('#cal').fullCalendar(options); + var event = $('#cal').fullCalendar('clientEvents')[0]; + expect(event.end).toEqualMoment('2014-05-07'); + }); + + it('correctly calculates an unspecified end when using a string Duration input', function() { + options.defaultAllDayEventDuration = '3.00:00:00'; + options.events = [ + { + allDay: true, + start: '2014-05-05' + } + ]; + $('#cal').fullCalendar(options); + var event = $('#cal').fullCalendar('clientEvents')[0]; + expect(event.end).toEqualMoment('2014-05-08'); + }); + }); + + describe('when forceEventDuration is off', function() { + + beforeEach(function() { + options.forceEventDuration = false; + }); + + [ 'basicWeek', 'agendaWeek' ].forEach(function(viewName) { // because they render all-day events in similar ways + describe('with ' + viewName + ' view', function() { + beforeEach(function() { + options.defaultView = viewName; + }); + it('renders an all-day event with no `end` to appear to have the default duration', function(done) { + options.defaultAllDayEventDuration = { days: 2 }; + options.events = [ + { + // a control. so we know how wide it should be + title: 'control event', + allDay: true, + start: '2014-04-28', + end: '2014-04-30' + }, + { + // one day after the control. no specified end + title: 'test event', + allDay: true, + start: '2014-04-28' + } + ]; + options.eventAfterAllRender = function() { + var eventElms = $('#cal .fc-event'); + var width0 = eventElms.eq(0).outerWidth(); + var width1 = eventElms.eq(1).outerWidth(); + expect(width0).toEqual(width1); + done(); + }; + $('#cal').fullCalendar(options); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/defaultTimedEventDuration.js b/tests/automated/defaultTimedEventDuration.js new file mode 100644 index 0000000..5c2484c --- /dev/null +++ b/tests/automated/defaultTimedEventDuration.js @@ -0,0 +1,117 @@ +describe('defaultTimedEventDuration', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + + options = { + defaultDate: '2014-05-01', + defaultView: 'month' + }; + }); + + describe('when forceEventDuration is on', function() { + + beforeEach(function() { + options.forceEventDuration = true; + }); + + it('correctly calculates an unspecified end when using a Duration object input', function() { + options.defaultTimedEventDuration = { hours: 2, minutes: 30 }; + options.events = [ + { + allDay: false, + start: '2014-05-05T04:00:00' + } + ]; + $('#cal').fullCalendar(options); + var event = $('#cal').fullCalendar('clientEvents')[0]; + expect(event.end).toEqualMoment('2014-05-05T06:30:00'); + }); + + it('correctly calculates an unspecified end when using a string Duration input', function() { + options.defaultTimedEventDuration = '03:15:00'; + options.events = [ + { + allDay: false, + start: '2014-05-05T04:00:00' + } + ]; + $('#cal').fullCalendar(options); + var event = $('#cal').fullCalendar('clientEvents')[0]; + expect(event.end).toEqualMoment('2014-05-05T07:15:00'); + }); + }); + + describe('when forceEventDuration is off', function() { + + beforeEach(function() { + options.forceEventDuration = false; + }); + + describe('with agendaWeek view', function() { + beforeEach(function() { + options.defaultView = 'agendaWeek'; + }); + it('renders a timed event with no `end` to appear to have the default duration', function(done) { + options.defaultTimedEventDuration = '01:15:00'; + options.events = [ + { + // a control. so we know how tall it should be + title: 'control event', + allDay: false, + start: '2014-05-01T04:00:00', + end: '2014-05-01T05:15:00' + }, + { + // one day after the control. no specified end + title: 'test event', + allDay: false, + start: '2014-05-02T04:00:00' + } + ]; + options.eventAfterAllRender = function() { + var eventElms = $('#cal .fc-event'); + var height0 = eventElms.eq(0).outerHeight(); + var height1 = eventElms.eq(1).outerHeight(); + expect(height0).toEqual(height1); + done(); + }; + $('#cal').fullCalendar(options); + }); + }); + + describe('with basicWeek view', function() { + beforeEach(function() { + options.defaultView = 'basicWeek'; + }); + it('renders a timed event with no `end` to appear to have the default duration', function(done) { + options.defaultTimedEventDuration = { days: 2 }; + options.events = [ + { + // a control. so we know how wide it should be + title: 'control event', + allDay: false, + start: '2014-04-28T04:00:00', + end: '2014-04-30T00:00:00' + }, + { + // one day after the control. no specified end + title: 'test event', + allDay: false, + start: '2014-04-28T04:00:00' + } + ]; + options.eventAfterAllRender = function() { + var eventElms = $('#cal .fc-event'); + var width0 = eventElms.eq(0).outerWidth(); + var width1 = eventElms.eq(1).outerWidth(); + expect(width0).toEqual(width1); + done(); + }; + $('#cal').fullCalendar(options); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/destroy.js b/tests/automated/destroy.js new file mode 100644 index 0000000..046e101 --- /dev/null +++ b/tests/automated/destroy.js @@ -0,0 +1,27 @@ +describe('destroy', function() { + + beforeEach(function() { + affix('#cal'); + }); + + describe('when calendar is LTR', function() { + it('cleans up all classNames on the root element', function() { + $('#cal').fullCalendar({ + isRTL: false + }); + $('#cal').fullCalendar('destroy'); + expect($('#cal')[0].className).toBe(''); + }); + }); + + describe('when calendar is RTL', function() { + it('cleans up all classNames on the root element', function() { + $('#cal').fullCalendar({ + isRTL: true + }); + $('#cal').fullCalendar('destroy'); + expect($('#cal')[0].className).toBe(''); + }); + }); + +}); \ No newline at end of file diff --git a/tests/automated/displayEventEnd.js b/tests/automated/displayEventEnd.js new file mode 100644 index 0000000..80c090a --- /dev/null +++ b/tests/automated/displayEventEnd.js @@ -0,0 +1,142 @@ +describe('displayEventEnd', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-06-13', + timeFormat: 'H:mm' + }; + }); + + afterEach(function() { + $('#cal').fullCalendar('destroy'); + }); + + [ 'month', 'agendaWeek' ].forEach(function(viewName) { + describe('when in ' + viewName + ' view', function() { + beforeEach(function() { + options.defaultView = viewName; + }); + + describe('when off', function() { + beforeEach(function() { + options.displayEventEnd = false; + }); + + describe('with an all-day event', function() { + beforeEach(function() { + options.events = [ { + title: 'timed event', + start: '2014-06-13', + end: '2014-06-13', + allDay: true + } ]; + }); + it('displays no time text', function(done) { + options.eventAfterAllRender = function() { + expect($('.fc-event .fc-time').length).toBe(0); + done(); + }; + $('#cal').fullCalendar(options); + }); + }); + + describe('with a timed event with no end time', function(done) { + beforeEach(function() { + options.events = [ { + title: 'timed event', + start: '2014-06-13T01:00:00', + allDay: false + } ]; + }); + it('displays only the start time text', function(done) { + options.eventAfterAllRender = function() { + expect($('.fc-event .fc-time')).toHaveText('1:00'); + done(); + }; + $('#cal').fullCalendar(options); + }); + }); + + describe('with a timed event with an end time', function() { + beforeEach(function() { + options.events = [ { + title: 'timed event', + start: '2014-06-13T01:00:00', + end: '2014-06-13T02:00:00', + allDay: false + } ]; + }); + it('displays only the start time text', function(done) { + options.eventAfterAllRender = function() { + expect($('.fc-event .fc-time')).toHaveText('1:00'); + done(); + }; + $('#cal').fullCalendar(options); + }); + }); + }); + + describe('when on', function() { + beforeEach(function() { + options.displayEventEnd = true; + }); + + describe('with an all-day event', function() { + beforeEach(function() { + options.events = [ { + title: 'timed event', + start: '2014-06-13', + end: '2014-06-13', + allDay: true + } ]; + }); + it('displays no time text', function(done) { + options.eventAfterAllRender = function() { + expect($('.fc-event .fc-time').length).toBe(0); + done(); + }; + $('#cal').fullCalendar(options); + }); + }); + + describe('with a timed event with no end time', function(done) { + beforeEach(function() { + options.events = [ { + title: 'timed event', + start: '2014-06-13T01:00:00', + allDay: false + } ]; + }); + it('displays only the start time text', function(done) { + options.eventAfterAllRender = function() { + expect($('.fc-event .fc-time')).toHaveText('1:00'); + done(); + }; + $('#cal').fullCalendar(options); + }); + }); + + describe('with a timed event with an end time', function() { + beforeEach(function() { + options.events = [ { + title: 'timed event', + start: '2014-06-13T01:00:00', + end: '2014-06-13T02:00:00', + allDay: false + } ]; + }); + it('displays both the start and end time text', function(done) { + options.eventAfterAllRender = function() { + expect($('.fc-event .fc-time')).toHaveText('1:00 - 2:00'); + done(); + }; + $('#cal').fullCalendar(options); + }); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/event-dnd.js b/tests/automated/event-dnd.js new file mode 100644 index 0000000..8e3c56f --- /dev/null +++ b/tests/automated/event-dnd.js @@ -0,0 +1,423 @@ +describe('eventDrop', function() { + var options; + + beforeEach(function() { + options = { + defaultDate: '2014-06-11', + editable: true, + dragScroll: false + }; + affix('#cal'); + }); + + afterEach(function() { + $('#cal').fullCalendar('destroy'); + }); + + describe('when in month view', function() { + beforeEach(function() { + options.defaultView = 'month'; + }); + + describe('when dragging an all-day event to another day', function() { + it('should be given correct arguments, with whole-day delta', function(done) { + options.events = [ { + title: 'all-day event', + start: '2014-06-11', + allDay: true + } ]; + + init( + function() { + $('.fc-event').simulate('drag-n-drop', { + dx: $('.fc-day').width() * 2, + dy: $('.fc-day').height() + }); + }, + function(event, delta, revertFunc) { + expect(delta.asDays()).toBe(9); + expect(delta.hours()).toBe(0); + expect(delta.minutes()).toBe(0); + expect(delta.seconds()).toBe(0); + expect(delta.milliseconds()).toBe(0); + + expect(event.start).toEqualMoment('2014-06-20'); + expect(event.end).toBeNull(); + revertFunc(); + expect(event.start).toEqualMoment('2014-06-11'); + expect(event.end).toBeNull(); + + done(); + } + ); + }); + }); + + describe('when gragging a timed event to another day', function() { + it('should be given correct arguments, with whole-day delta', function(done) { + options.events = [ { + title: 'timed event', + start: '2014-06-11T06:00:00', + allDay: false + } ]; + + init( + function() { + $('.fc-event').simulate('drag-n-drop', { + dx: $('.fc-day').width() * -2, + dy: $('.fc-day').height() + }); + }, + function(event, delta, revertFunc) { + expect(delta.asDays()).toBe(5); + expect(delta.hours()).toBe(0); + expect(delta.minutes()).toBe(0); + expect(delta.seconds()).toBe(0); + expect(delta.milliseconds()).toBe(0); + + expect(event.start).toEqualMoment('2014-06-16T06:00:00'); + expect(event.end).toBeNull(); + revertFunc(); + expect(event.start).toEqualMoment('2014-06-11T06:00:00'); + expect(event.end).toBeNull(); + + done(); + } + ); + }); + }); + + // TODO: tests for eventMouseover/eventMouseout firing correctly when no dragging + it('should not fire any eventMouseover/eventMouseout events while dragging', function(done) { // issue 1297 + options.events = [ + { + title: 'all-day event', + start: '2014-06-11', + allDay: true, + className: 'event1' + }, + { + title: 'event2', + start: '2014-06-10', + allDay: true, + className: 'event2' + } + ]; + options.eventMouseover = function() { }; + options.eventMouseout = function() { }; + spyOn(options, 'eventMouseover'); + spyOn(options, 'eventMouseout'); + + init( + function() { + $('.event1').simulate('drag-n-drop', { + dx: $('.fc-day').width() * 2, + dy: $('.fc-day').height(), + interpolation: { + stepCount: 10, + duration: 1000 + } + }); + setTimeout(function() { // wait until half way through drag + $('.event2') + .simulate('mouseover') + .simulate('mouseenter') + .simulate('mouseout') + .simulate('mouseleave'); + }, 500); + }, + function(event, delta, revertFunc) { + expect(options.eventMouseover).not.toHaveBeenCalled(); + expect(options.eventMouseout).not.toHaveBeenCalled(); + done(); + } + ); + }); + }); + + describe('when in agenda view', function() { + beforeEach(function() { + options.defaultView = 'agendaWeek'; + }); + + describe('when dragging a timed event to another time on a different day', function() { + it('should be given correct arguments and delta with days/time', function(done) { + options.events = [ { + title: 'timed event', + start: '2014-06-11T06:00:00', + allDay: false + } ]; + + init( + function() { + $('.fc-event .fc-time').simulate('drag-n-drop', { + dx: $('th.fc-wed').width(), // 1 day + dy: $('.fc-slats tr:eq(1)').outerHeight() * 2.9 // 1.5 hours + }); + }, + function(event, delta, revertFunc) { + expect(delta.days()).toBe(1); + expect(delta.hours()).toBe(1); + expect(delta.minutes()).toBe(30); + expect(delta.seconds()).toBe(0); + expect(delta.milliseconds()).toBe(0); + + expect(event.start).toEqualMoment('2014-06-12T07:30:00'); + expect(event.end).toBeNull(); + revertFunc(); + expect(event.start).toEqualMoment('2014-06-11T06:00:00'); + expect(event.end).toBeNull(); + + done(); + } + ); + }); + }); + + describe('when dragging an all-day event to another all-day', function() { + it('should be given correct arguments, with whole-day delta', function(done) { + options.events = [ { + title: 'all-day event', + start: '2014-06-11', + allDay: true + } ]; + + init( + function() { + $('.fc-event').simulate('drag-n-drop', { + dx: $('th.fc-wed').width() * 2 // 2 days + }); + }, + function(event, delta, revertFunc) { + expect(delta.days()).toBe(2); + expect(delta.hours()).toBe(0); + expect(delta.minutes()).toBe(0); + expect(delta.seconds()).toBe(0); + expect(delta.milliseconds()).toBe(0); + + expect(event.start).toEqualMoment('2014-06-13'); + expect(event.end).toBeNull(); + revertFunc(); + expect(event.start).toEqualMoment('2014-06-11'); + expect(event.end).toBeNull(); + + done(); + } + ); + }); + }); + + describe('when dragging an all-day event to a time slot on a different day', function() { + it('should be given correct arguments and delta with days/time', function(done) { + options.scrollTime = '01:00:00'; + options.height = 400; // short enough to make scrolling happen + options.events = [ { + title: 'all-day event', + start: '2014-06-11', + allDay: true + } ]; + + init( + function() { + var allDayGrid = $('.fc-agenda-view .fc-day-grid'); + var hr = allDayGrid.next('hr'); + $('.fc-event').simulate('drag-n-drop', { + dx: $('th.fc-wed').width() * -1, + dy: allDayGrid.outerHeight() + hr.outerHeight() + }); + }, + function(event, delta, revertFunc) { + expect(delta.days()).toBe(-1); + expect(delta.hours()).toBe(1); + expect(delta.minutes()).toBe(0); + expect(delta.seconds()).toBe(0); + expect(delta.milliseconds()).toBe(0); + + expect(event.start).toEqualMoment('2014-06-10T01:00:00'); + expect(event.end).toBeNull(); + expect(event.allDay).toBe(false); + revertFunc(); + expect(event.start).toEqualMoment('2014-06-11'); + expect(event.end).toBeNull(); + expect(event.allDay).toBe(true); + + done(); + } + ); + }); + }); + + describe('when dragging a timed event to an all-day slot on a different day', function() { + it('should be given correct arguments, with whole-day delta', function(done) { + var eventElm; + + options.scrollTime = '01:00:00'; + options.height = 400; // short enough to make scrolling happen + options.events = [ { + title: 'timed event', + start: '2014-06-11T01:00:00', + allDay: false + } ]; + + init( + function() { + eventElm = $('.fc-event .fc-time').simulate('drag', { // grabs the top of the event + dx: $('th.fc-wed').width() * -1, + dy: -$('.fc-agenda-view .fc-day-grid').outerHeight(), + callback: function() { + // the all day slot works off of mouse-moving coordinates + var offset = eventElm.offset(); + $('.fc-agenda-allday .fc-day-content') + .simulate('mouseover', { + clientX: offset.left + 10, + clientY: offset.top + 10 + }) + .simulate('mousemove', { + clientX: offset.left + 10, + clientY: offset.top + 10 + }); + setTimeout(function() { + eventElm.simulate('drop'); + }, 100); + } + }); + }, + function(event, delta, revertFunc) { + expect(delta.days()).toBe(-1); + expect(delta.hours()).toBe(0); + expect(delta.minutes()).toBe(0); + expect(delta.seconds()).toBe(0); + expect(delta.milliseconds()).toBe(0); + + expect(event.start).toEqualMoment('2014-06-10'); + expect(event.end).toBeNull(); + expect(event.allDay).toBe(true); + revertFunc(); + expect(event.start).toEqualMoment('2014-06-11T01:00:00'); + expect(event.end).toBeNull(); + expect(event.allDay).toBe(false); + + done(); + } + ); + }); + }); + + describe('when dragging a timed event with no end time', function() { + it('should continue to only show the updated start time', function(done) { + var dragged = false; + var eventElm; + + options.scrollTime = '01:00:00'; + options.height = 400; // short enough to make scrolling happen + options.events = [ { + title: 'timed event', + start: '2014-06-11T01:00:00', + allDay: false + } ]; + + init( + function() { + eventElm = $('.fc-event .fc-time').simulate('drag', { + dy: $('.fc-slats tr:eq(1)').height() * 2.9, // 1.5 hours + callback: function() { + dragged = true; + expect($('.fc-event.fc-helper .fc-time')).toHaveText('2:30'); + eventElm.simulate('drop'); + } + }); + }, + function() { + expect(dragged).toBe(true); + done(); + } + ); + }); + }); + + describe('when dragging a timed event with an end time', function() { + it('should continue to show the updated start and end time', function(done) { + var dragged = false; + var eventElm; + + options.scrollTime = '01:00:00'; + options.height = 400; // short enough to make scrolling happen + options.events = [ { + title: 'timed event', + start: '2014-06-11T01:00:00', + end: '2014-06-11T02:00:00', + allDay: false + } ]; + + init( + function() { + eventElm = $('.fc-event .fc-time').simulate('drag', { + dy: $('.fc-slats tr:eq(1)').height() * 2.9, // 1.5 hours + callback: function() { + dragged = true; + expect($('.fc-event.fc-helper .fc-time')).toHaveText('2:30 - 3:30'); + eventElm.simulate('drop'); + } + }); + }, + function() { + expect(dragged).toBe(true); + done(); + } + ); + }); + }); + }); + + // Initialize a calendar, run a drag, and do type-checking of all arguments for all handlers. + // TODO: more descrimination instead of just checking for 'object' + function init(dragFunc, dropHandler) { + var eventsRendered = false; + + options.eventAfterAllRender = function() { + if (!eventsRendered) { // because event rerendering will happen upon drop + dragFunc(); + eventsRendered = true; + } + }; + options.eventDragStart = function(event, jsEvent, uiEvent, view) { + expect(this instanceof Element).toBe(true); + expect(this).toHaveClass('fc-event'); + expect(typeof event).toBe('object'); + expect(typeof jsEvent).toBe('object'); + expect(typeof uiEvent).toBe('object'); + expect(typeof view).toBe('object'); + }; + options.eventDragStop = function(event, jsEvent, uiEvent, view) { + expect(options.eventDragStart).toHaveBeenCalled(); + + expect(this instanceof Element).toBe(true); + expect(this).toHaveClass('fc-event'); + expect(typeof event).toBe('object'); + expect(typeof jsEvent).toBe('object'); + expect(typeof uiEvent).toBe('object'); + expect(typeof view).toBe('object'); + }; + options.eventDrop = function(event, delta, revertFunc, jsEvent, uiEvent, view) { + expect(options.eventDragStop).toHaveBeenCalled(); + + expect(this instanceof Element).toBe(true); + expect(this).toHaveClass('fc-event'); + expect(typeof event).toBe('object'); + expect(moment.isDuration(delta)).toBe(true); + expect(typeof revertFunc).toBe('function'); + expect(typeof jsEvent).toBe('object'); + expect(typeof uiEvent).toBe('object'); + expect(typeof view).toBe('object'); + + dropHandler.apply(this, arguments); + }; + + spyOn(options, 'eventDragStart').and.callThrough(); + spyOn(options, 'eventDragStop').and.callThrough(); + + setTimeout(function() { // hack. agenda view scroll state would get messed up between tests + $('#cal').fullCalendar(options); + }, 0); + } +}); \ No newline at end of file diff --git a/tests/automated/event-feed-param.js b/tests/automated/event-feed-param.js new file mode 100644 index 0000000..f04b76a --- /dev/null +++ b/tests/automated/event-feed-param.js @@ -0,0 +1,72 @@ +describe('event feed params', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + + options = { + defaultDate: '2014-05-01', + defaultView: 'month' + }; + + $.mockjax({ + url: '*', + contentType: 'text/json', + responseText: [ + { + title: 'my event', + start: '2014-05-21' + } + ] + }); + $.mockjaxSettings.log = function() { }; // don't console.log + }); + + afterEach(function() { + $.mockjaxClear(); + }); + + it('utilizes custom startParam, endParam, and timezoneParam names', function() { + options.events = 'my-feed.php'; + options.timezone = 'America/Los_Angeles'; + options.startParam = 'mystart'; + options.endParam = 'myend'; + options.timezoneParam = 'currtz'; + $('#cal').fullCalendar(options); + var request = $.mockjax.mockedAjaxCalls()[0]; + expect(request.data.start).toBeUndefined(); + expect(request.data.end).toBeUndefined(); + expect(request.data.timezone).toBeUndefined(); + expect(request.data.mystart).toEqual('2014-04-27'); + expect(request.data.myend).toEqual('2014-06-08'); + expect(request.data.currtz).toEqual('America/Los_Angeles'); + }); + + it('utilizes event-source-specific startParam, endParam, and timezoneParam names', function() { + options.timezone = 'America/Los_Angeles'; + options.startParam = 'mystart'; + options.endParam = 'myend'; + options.timezoneParam = 'currtz'; + options.eventSources = [ + { + url: 'my-feed.php', + startParam: 'feedstart', + endParam: 'feedend', + timezoneParam: 'feedctz' + } + ]; + $('#cal').fullCalendar(options); + var request = $.mockjax.mockedAjaxCalls()[0]; + expect(request.data.start).toBeUndefined(); + expect(request.data.end).toBeUndefined(); + expect(request.data.timezone).toBeUndefined(); + expect(request.data.mystart).toBeUndefined(); + expect(request.data.myend).toBeUndefined(); + expect(request.data.currtz).toBeUndefined(); + expect(request.data.feedstart).toEqual('2014-04-27'); + expect(request.data.feedend).toEqual('2014-06-08'); + expect(request.data.feedctz).toEqual('America/Los_Angeles'); + }); + +}); \ No newline at end of file diff --git a/tests/automated/event-obj.js b/tests/automated/event-obj.js new file mode 100644 index 0000000..7715aef --- /dev/null +++ b/tests/automated/event-obj.js @@ -0,0 +1,109 @@ +describe('event object creation', function() { + + /* + + NOTE: Where possible, if there is a specific option that affects event object creation + behavior, write your tests in the individual file for that option, instead of here. + Examples of this: + allDayDefault (tests allDay guessing behavior too) + eventDataTransform + forceEventDuration + + */ + + var event; + + beforeEach(function() { + affix('#cal'); + event = null; + }); + + function init(singleEventData) { + $('#cal').fullCalendar({ + events: [ singleEventData ] + }); + event = $('#cal').fullCalendar('clientEvents')[0]; + } + + it('accepts `date` property as alias for `start`', function() { + init({ + date: '2014-05-05' + }); + expect(moment.isMoment(event.start)).toEqual(true); + expect(event.start).toEqualMoment('2014-05-05'); + }); + + it('doesn\'t produce an event when an invalid start', function() { + init({ + start: new Date('asdf') // we use Date constructor to avoid annoying momentjs warning + }); + expect(event).toBeUndefined(); + }); + + it('doesn\'t produce an event when an invalid end', function() { + init({ + end: new Date('asdf') // we use Date constructor to avoid annoying momentjs warning + }); + expect(event).toBeUndefined(); + }); + + it('strips times of dates when event is all-day', function() { + init({ + start: '2014-05-01T01:00:00-12:00', + end: '2014-05-02T01:00:00+12:00', + allDay: true + }); + expect(event.start.hasTime()).toEqual(false); + expect(event.start).toEqualMoment('2014-05-01'); + expect(event.end.hasTime()).toEqual(false); + expect(event.end).toEqualMoment('2014-05-02'); + }); + + it('gives 00:00 times to ambiguously-timed dates when event is timed', function() { + init({ + start: '2014-05-01', + end: '2014-05-03', + allDay: false + }); + expect(event.start.hasTime()).toEqual(true); + expect(event.start).toEqualMoment('2014-05-01T00:00:00'); + expect(event.end.hasTime()).toEqual(true); + expect(event.end).toEqualMoment('2014-05-03T00:00:00'); + }); + + it('sets the source', function() { + init({ + start: '2014-05-01' + }); + expect(typeof event.source).toEqual('object'); + }); + + it('accepts an array `className`', function() { + init({ + start: '2014-05-01', + className: [ 'class1', 'class2' ] + }); + expect($.isArray(event.className)).toEqual(true); + expect(event.className).toEqual([ 'class1', 'class2' ]); + }); + + it('accepts a string `className`', function() { + init({ + start: '2014-05-01', + className: 'class1 class2' + }); + expect($.isArray(event.className)).toEqual(true); + expect(event.className).toEqual([ 'class1', 'class2' ]); + }); + + it('copies over custom properties', function() { + init({ + start: '2014-05-01', + prop1: 'prop1val', + prop2: [ 'a', 'b' ] + }); + expect(event.prop1).toEqual('prop1val'); + expect(event.prop2).toEqual([ 'a', 'b' ]); + }); + +}); \ No newline at end of file diff --git a/tests/automated/event-resize.js b/tests/automated/event-resize.js new file mode 100644 index 0000000..85a3edd --- /dev/null +++ b/tests/automated/event-resize.js @@ -0,0 +1,431 @@ +describe('eventResize', function() { + var options; + + beforeEach(function() { + options = { + defaultDate: '2014-06-11', + editable: true + }; + affix('#cal'); + }); + + afterEach(function() { + $('#cal').fullCalendar('destroy'); + }); + + describe('when in month view', function() { + beforeEach(function() { + options.defaultView = 'month'; + }); + + describe('when resizing an all-day event', function() { + it('should have correct arguments with a whole-day delta', function(done) { + options.events = [ { + title: 'all-day event', + start: '2014-06-11', + allDay: true + } ]; + + init( + function() { + $('.fc-event .fc-resizer').simulate('drag-n-drop', { + dx: $('.fc-day').width() * -2.5, // guarantee 2 days to left + dy: $('.fc-day').height() + }); + }, + function(event, delta, revertFunc) { + expect(delta.asDays()).toBe(5); + expect(delta.hours()).toBe(0); + expect(delta.minutes()).toBe(0); + expect(delta.seconds()).toBe(0); + expect(delta.milliseconds()).toBe(0); + + expect(event.start).toEqualMoment('2014-06-11'); + expect(event.end).toEqualMoment('2014-06-17'); + revertFunc(); + expect(event.start).toEqualMoment('2014-06-11'); + expect(event.end).toBeNull(); + + done(); + } + ); + }); + }); + + describe('when rendering a timed event', function() { + it('should not have resize capabilities', function(done) { + options.events = [ { + title: 'timed event', + start: '2014-06-11T08:00:00', + allDay: false + } ]; + options.eventAfterAllRender = function() { + expect($('.fc-event .fc-resizer').length).toBe(0); + done(); + }; + $('#cal').fullCalendar(options); + }); + }); + }); + + describe('when in agenda view', function() { + beforeEach(function() { + options.defaultView = 'agendaWeek'; + }); + + describe('when resizing an all-day event', function() { + it('should have correct arguments with a whole-day delta', function(done) { + options.events = [ { + title: 'all-day event', + start: '2014-06-11', + allDay: true + } ]; + + init( + function() { + $('.fc-event .fc-resizer').simulate('drag-n-drop', { + dx: $('th.fc-wed').width() * 1.5 // two days + }); + }, + function(event, delta, revertFunc) { + expect(delta.asDays()).toBe(2); + expect(delta.hours()).toBe(0); + expect(delta.minutes()).toBe(0); + expect(delta.seconds()).toBe(0); + expect(delta.milliseconds()).toBe(0); + + expect(event.start).toEqualMoment('2014-06-11'); + expect(event.end).toEqualMoment('2014-06-14'); + revertFunc(); + expect(event.start).toEqualMoment('2014-06-11'); + expect(event.end).toBeNull(); + + done(); + } + ); + }); + }); + + describe('when resizing a timed event with an end', function() { + beforeEach(function() { + options.events = [ { + title: 'timed event event', + start: '2014-06-11T05:00:00', + end: '2014-06-11T07:00:00', + allDay: false + } ]; + }); + + it('should have correct arguments with a timed delta', function(done) { + init( + function() { + $('.fc-event .fc-resizer').simulate('drag-n-drop', { + dy: $('.fc-slats tr:eq(1)').height() * 4.5 // 5 slots, so 2.5 hours + }); + }, + function(event, delta, revertFunc) { + expect(delta.days()).toBe(0); + expect(delta.hours()).toBe(2); + expect(delta.minutes()).toBe(30); + expect(delta.seconds()).toBe(0); + expect(delta.milliseconds()).toBe(0); + + expect(event.start).toEqualMoment('2014-06-11T05:00:00'); + expect(event.end).toEqualMoment('2014-06-11T09:30:00'); + revertFunc(); + expect(event.start).toEqualMoment('2014-06-11T05:00:00'); + expect(event.end).toEqualMoment('2014-06-11T07:00:00'); + + done(); + } + ); + }); + + // TODO: test RTL + it('should have correct arguments with a timed delta when resized to a different day', function(done) { + init( + function() { + $('.fc-event .fc-resizer').simulate('drag-n-drop', { + dx: $('.fc-day-header:first').width() * .9, // one day + dy: $('.fc-slats tr:eq(1)').height() * 4.5 // 5 slots, so 2.5 hours + }); + }, + function(event, delta, revertFunc) { + expect(delta.days()).toBe(1); + expect(delta.hours()).toBe(2); + expect(delta.minutes()).toBe(30); + expect(delta.seconds()).toBe(0); + expect(delta.milliseconds()).toBe(0); + + expect(event.start).toEqualMoment('2014-06-11T05:00:00'); + expect(event.end).toEqualMoment('2014-06-12T09:30:00'); + revertFunc(); + expect(event.start).toEqualMoment('2014-06-11T05:00:00'); + expect(event.end).toEqualMoment('2014-06-11T07:00:00'); + + done(); + } + ); + }); + + it('should have correct arguments with a timed delta, when timezone is local', function(done) { + options.timezone = 'local'; + init( + function() { + $('.fc-event .fc-resizer').simulate('drag-n-drop', { + dy: $('.fc-slats tr:eq(1)').height() * 4.5 // 5 slots, so 2.5 hours + }); + }, + function(event, delta, revertFunc) { + expect(delta.days()).toBe(0); + expect(delta.hours()).toBe(2); + expect(delta.minutes()).toBe(30); + expect(delta.seconds()).toBe(0); + expect(delta.milliseconds()).toBe(0); + + expect(event.start).toEqualMoment(moment('2014-06-11T05:00:00')); // compate to local moment + expect(event.end).toEqualMoment(moment('2014-06-11T09:30:00')); + revertFunc(); + expect(event.start).toEqualMoment(moment('2014-06-11T05:00:00')); + expect(event.end).toEqualMoment(moment('2014-06-11T07:00:00')); + + done(); + } + ); + }); + + it('should have correct arguments with a timed delta, when timezone is UTC', function(done) { + options.timezone = 'UTC'; + init( + function() { + $('.fc-event .fc-resizer').simulate('drag-n-drop', { + dy: $('.fc-slats tr:eq(1)').height() * 4.5 // 5 slots, so 2.5 hours + }); + }, + function(event, delta, revertFunc) { + expect(delta.days()).toBe(0); + expect(delta.hours()).toBe(2); + expect(delta.minutes()).toBe(30); + expect(delta.seconds()).toBe(0); + expect(delta.milliseconds()).toBe(0); + + expect(event.start).toEqualMoment('2014-06-11T05:00:00+00:00'); + expect(event.end).toEqualMoment('2014-06-11T09:30:00+00:00'); + revertFunc(); + expect(event.start).toEqualMoment('2014-06-11T05:00:00+00:00'); + expect(event.end).toEqualMoment('2014-06-11T07:00:00+00:00'); + + done(); + } + ); + }); + + it('should display the correct time text while resizing', function(done) { + var dy; + var handle; + + init( + function() { + dy = $('.fc-slats tr:eq(1)').height() * 4.5; // 5 slots, so 2.5 hours + handle = $('.fc-event .fc-resizer').simulate('drag', { + dy: dy, + callback: function() { + expect($('.fc-event.fc-helper .fc-time')).toHaveText('5:00 - 9:30'); + handle.simulate('drag', { + // BUG with jquery-simulate-ext + // I guess the delta is still relative to the original position, so should be zero. + // But zero causes nothing to happen, so make it a tiny non-zero delta. + dy: -1, + + callback: function() { + expect($('.fc-event.fc-helper')).not.toExist(); + expect($('.fc-event')).toBeVisible(); + expect($('.fc-event .fc-time')).toHaveText('5:00 - 7:00'); + handle.simulate('drop', { + callback: function() { + done(); + } + }); + } + }); + } + }); + }, + function() { + // this wasn't firing for some reason. do it in the drop callback instead + //done(); + } + ); + }); + + it('should run the temporarily rendered event through eventRender', function(done) { + var dy; + var handle; + + options.eventRender = function(event, element) { + element.addClass('didEventRender'); + }; + + init( + function() { + dy = $('.fc-slats tr:eq(1)').height() * 4.5; // 5 slots, so 2.5 hours + handle = $('.fc-event .fc-resizer').simulate('drag', { + dy: dy, + callback: function() { + expect($('.fc-event.fc-helper')).toHaveClass('didEventRender'); + handle.simulate('drag', { + // BUG with jquery-simulate-ext + // I guess the delta is still relative to the original position, so should be zero. + // But zero causes nothing to happen, so make it a tiny non-zero delta. + dy: -1, + + callback: function() { + expect($('.fc-event.fc-helper')).not.toExist(); + handle.simulate('drop', { + callback: function() { + done(); + } + }); + } + }); + } + }); + }, + function() { + // this wasn't firing for some reason. do it in the drop callback instead + //done(); + } + ); + }); + + it('should not fire the windowResize handler', function(done) { // bug 1116 + + // has to do this crap because PhantomJS was trigger false window resizes unrelated to the fc-event resize + var isDragging = false; + var calledWhileDragging = false; + + options.windowResizeDelay = 0; + options.windowResize = function(ev) { + if (isDragging) { + calledWhileDragging = true; + } + }; + + init( + function() { + setTimeout(function() { + isDragging = true; + $('.fc-event .fc-resizer').simulate('drag-n-drop', { + dy: 100 + }); + }, 100); // hack for PhantomJS. after any initial false window resizes + }, + function() { + expect(calledWhileDragging).toBe(false); + done(); + } + ); + }); + }); + + describe('when resizing a timed event without an end', function() { + beforeEach(function() { + options.events = [ { + title: 'timed event event', + start: '2014-06-11T05:00:00', + allDay: false + } ]; + }); + + it('should display the correct time text while resizing', function(done) { + var dy; + var handle; + + init( + function() { + dy = $('.fc-slats tr:eq(1)').height() * 4.5; // 5 slots, so 2.5 hours + handle = $('.fc-event .fc-resizer').simulate('drag', { + dy: dy, + callback: function() { + expect($('.fc-event.fc-helper .fc-time')).toHaveText('5:00 - 9:30'); + handle.simulate('drag', { + // BUG with jquery-simulate-ext + // I guess the delta is still relative to the original position, so should be zero. + // But zero causes nothing to happen, so make it a tiny non-zero delta. + dy: -1, + + callback: function() { + expect($('.fc-event.fc-helper')).not.toExist(); + expect($('.fc-event')).toBeVisible(); + expect($('.fc-event .fc-time')).toHaveText('5:00'); + handle.simulate('drop', { + callback: function() { + done(); + } + }); + } + }); + } + }); + }, + function() { + // this wasn't firing for some reason. do it in the drop callback instead + //done(); + } + ); + }); + }); + }); + + // Initialize a calendar, run a resize, and do type-checking of all arguments for all handlers. + // TODO: more descrimination instead of just checking for 'object' + function init(resizeStartFunc, resizeDoneFunc) { + var eventsRendered = false; + + options.eventAfterAllRender = function() { + if (!eventsRendered) { // because event rerendering will happen when resize is over + resizeStartFunc(); + eventsRendered = true; + } + }; + options.eventResizeStart = function(event, jsEvent, uiEvent, view) { + expect(this instanceof Element).toBe(true); + expect(this).toHaveClass('fc-event'); + expect(typeof event).toBe('object'); + expect(typeof jsEvent).toBe('object'); + expect(typeof uiEvent).toBe('object'); + expect(typeof view).toBe('object'); + }; + options.eventResizeStop = function(event, jsEvent, uiEvent, view) { + expect(options.eventResizeStart).toHaveBeenCalled(); + + expect(this instanceof Element).toBe(true); + expect(this).toHaveClass('fc-event'); + expect(typeof event).toBe('object'); + expect(typeof jsEvent).toBe('object'); + expect(typeof uiEvent).toBe('object'); + expect(typeof view).toBe('object'); + }; + options.eventResize = function(event, delta, revertFunc, jsEvent, uiEvent, view) { + expect(options.eventResizeStop).toHaveBeenCalled(); + + expect(this instanceof Element).toBe(true); + expect(this).toHaveClass('fc-event'); + expect(typeof event).toBe('object'); + expect(moment.isDuration(delta)).toBe(true); + expect(typeof revertFunc).toBe('function'); + expect(typeof jsEvent).toBe('object'); + expect(typeof uiEvent).toBe('object'); // might be a non-jqui dummy object + expect(typeof view).toBe('object'); + + resizeDoneFunc.apply(this, arguments); + }; + + spyOn(options, 'eventResizeStart').and.callThrough(); + spyOn(options, 'eventResizeStop').and.callThrough(); + + setTimeout(function() { // hack. agenda view scroll state would get messed up between tests + $('#cal').fullCalendar(options); + }, 0); + } +}); \ No newline at end of file diff --git a/tests/automated/eventDestroy.js b/tests/automated/eventDestroy.js new file mode 100644 index 0000000..ffc43aa --- /dev/null +++ b/tests/automated/eventDestroy.js @@ -0,0 +1,59 @@ + +describe('eventDestroy', function() { + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-08-01' + }; + }); + + function testSingleEvent(singleEventData, done) { + var callCnt = 0; + + expect(singleEventData.id).toBeTruthy(); + options.events = [ singleEventData ]; + options.eventDestroy = function(event, element) { + if (callCnt++ === 0) { // only care about the first call. gets called again when calendar is destroyed + expect(event.id).toBe(singleEventData.id); + done(); + } + }; + + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('removeEvents', singleEventData.id); + } + + describe('when in month view', function() { // for issue 2017 + + beforeEach(function() { + options.defaultView = 'month'; + }); + + it('gets called with removeEvents method', function(done) { + testSingleEvent({ + id: 1, + title: 'event1', + date: '2014-08-02' + }, done); + }); + }); + + describe('when in agendaWeek view', function() { // for issue 2017 + + beforeEach(function() { + options.defaultView = 'agendaWeek'; + options.scrollTime = '00:00:00'; + }); + + it('gets called with removeEvents method', function(done) { + testSingleEvent({ + id: 1, + title: 'event1', + date: '2014-08-02T02:00:00' + }, done); + }); + }); + +}); \ No newline at end of file diff --git a/tests/automated/eventLimit-popover.js b/tests/automated/eventLimit-popover.js new file mode 100644 index 0000000..603852d --- /dev/null +++ b/tests/automated/eventLimit-popover.js @@ -0,0 +1,219 @@ + +describe('eventLimit popover', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultView: 'month', + defaultDate: '2014-08-01', + eventLimit: 3, + events: [ + { title: 'event1', start: '2014-07-28', end: '2014-07-30', className: 'event1' }, + { title: 'event2', start: '2014-07-29', end: '2014-07-31', className: 'event2' }, + { title: 'event3', start: '2014-07-29', className: 'event3' }, + { title: 'event4', start: '2014-07-29', className: 'event4' } + ], + dragScroll: false, // don't do autoscrolling while dragging. close quarters in PhantomJS + popoverViewportConstrain: false, // because PhantomJS window is small, don't do smart repositioning + handleWindowResize: false // because showing the popover causes scrollbars and fires resize + }; + }); + + function init() { + $('#cal').fullCalendar(options); + $('.fc-more').simulate('click'); + } + + [ 'month', 'basicWeek', 'agendaWeek' ].forEach(function(viewName) { + + describe('when in ' + viewName + ' view', function() { + + beforeEach(function() { + options.defaultView = viewName; + }); + + it('aligns horizontally with left edge of cell if LTR', function() { + options.isRTL = false; + init(); + var cellLeft = $('.fc-day-grid .fc-row:eq(0) .fc-bg td:not(.fc-axis):eq(2)').offset().left; + var popoverLeft = $('.fc-more-popover').offset().left; + var diff = Math.abs(cellLeft - popoverLeft); + expect(diff).toBeLessThan(2); + }); + + it('aligns horizontally with left edge of cell if RTL', function() { + options.isRTL = true; + init(); + var cell = $('.fc-day-grid .fc-row:eq(0) .fc-bg td:not(.fc-axis):eq(4)'); + var cellRight = cell.offset().left + cell.outerWidth(); + var popover = $('.fc-more-popover'); + var popoverRight = popover.offset().left + popover.outerWidth(); + var diff = Math.abs(cellRight - popoverRight); + expect(diff).toBeLessThan(2); + }); + }); + }); + + describe('when in month view', function() { + + beforeEach(function() { + options.defaultView = 'month'; + }); + + it('aligns with top of cell', function() { + init(); + var popoverTop = $('.fc-more-popover').offset().top; + var rowTop = $('.fc-day-grid .fc-row:eq(0)').offset().top; + var diff = Math.abs(popoverTop - rowTop); + expect(diff).toBeLessThan(2); + }); + }); + + [ 'basicWeek', 'agendaWeek' ].forEach(function(viewName) { + + describe('when in ' + viewName + ' view', function() { + + beforeEach(function() { + options.defaultView = viewName; + }); + + it('aligns with top of header', function() { + init(); + var popoverTop = $('.fc-more-popover').offset().top; + var headTop = $('.fc-view > table > thead .fc-row').offset().top; + var diff = Math.abs(popoverTop - headTop); + expect(diff).toBeLessThan(2); + }); + }); + }); + + // TODO: somehow test how the popover does to the edge of any scroll container + + it('closes when user clicks the X', function() { + init(); + expect($('.fc-more-popover')).toBeVisible(); + $('.fc-more-popover .fc-close') + .simulate('click') + .trigger('click'); // needed this for IE8 for some reason + expect($('.fc-more-popover')).not.toBeVisible(); + }); + + it('doesn\'t close when user clicks somewhere inside of the popover', function() { + init(); + expect($('.fc-more-popover')).toBeVisible(); + expect($('.fc-more-popover .fc-header')).toBeInDOM(); + $('.fc-more-popover .fc-header').simulate('mousedown').simulate('click'); + expect($('.fc-more-popover')).toBeVisible(); + }); + + it('closes when user clicks outside of the popover', function() { + init(); + expect($('.fc-more-popover')).toBeVisible(); + $('body').simulate('mousedown').simulate('click'); + expect($('.fc-more-popover')).not.toBeVisible(); + }); + + it('has the correct event contents', function() { + init(); + expect($('.fc-more-popover .event1')).toBeMatchedBy('.fc-not-start.fc-end'); + expect($('.fc-more-popover .event2')).toBeMatchedBy('.fc-start.fc-not-end'); + expect($('.fc-more-popover .event3')).toBeMatchedBy('.fc-start.fc-end'); + expect($('.fc-more-popover .event4')).toBeMatchedBy('.fc-start.fc-end'); + }); + + + describe('when dragging events out', function() { + + beforeEach(function() { + options.editable = true; + }); + + describe('when dragging an all-day event to a different day', function() { + + it('should have the new day and remain all-day', function(done) { + + options.eventDrop = function(event) { + expect(event.start).toEqualMoment('2014-07-28'); + expect(event.allDay).toBe(true); + done(); + }; + init(); + + setTimeout(function() { // simulate was getting confused about which thing was being clicked :( + $('.fc-more-popover .event4').simulate('drag-n-drop', { + dragTarget: $('.fc-day-grid .fc-row:eq(0) .fc-bg td:not(.fc-axis):eq(1)') // one day before + }); + }, 0); + }); + }); + + describe('when dragging a timed event to a whole day', function() { + + it('should move to new day but maintain its time', function(done) { + + options.events.push({ // add timed event + title: 'event5', + start: '2014-07-29T13:00:00', + className: 'event5' + }); + options.eventDrop = function(event) { + expect(event.start).toEqualMoment('2014-07-28T13:00:00'); + expect(event.allDay).toBe(false); + done(); + }; + init(); + + setTimeout(function() { // simulate was getting confused about which thing was being clicked :( + $('.fc-more-popover .event5').simulate('drag-n-drop', { + dragTarget: $('.fc-day-grid .fc-row:eq(0) .fc-bg td:not(.fc-axis):eq(1)') // one day before + }); + }, 0); + }); + }); + + describe('when dragging a whole day event to a timed slot', function() { + + it('should assume the new time, with a cleared end', function(done) { + + options.defaultView = 'agendaWeek'; + options.scrollTime = '00:00:00'; + options.eventDrop = function(event) { + expect(event.start).toEqualMoment('2014-07-30T03:00:00'); + expect(event.allDay).toBe(false); + done(); + }; + init(); + + setTimeout(function() { // simulate was getting confused about which thing was being clicked :( + $('.fc-more-popover .event4').simulate('drag-n-drop', { + dragTarget: $('.fc-slats tr:eq(6)') // the middle will be 7/30, 3:00am + }); + }, 0); + }); + }); + + describe('when a single-day event isn\'t dragged out all the way', function() { + + it('shouldn\'t do anything', function(done) { + + options.eventDragStop = function() { + setTimeout(function() { // try to wait until drag is over. eventDrop won't fire BTW + expect($('.fc-more-popover')).toBeInDOM(); + done(); + },0); + }; + init(); + + setTimeout(function() { // simulate was getting confused about which thing was being clicked :( + $('.fc-more-popover .event1 .fc-title').simulate('drag-n-drop', { + dx: 20 + }); + }, 0); + }); + }); + + }); + +}); diff --git a/tests/automated/eventLimit.js b/tests/automated/eventLimit.js new file mode 100644 index 0000000..470b0c1 --- /dev/null +++ b/tests/automated/eventLimit.js @@ -0,0 +1,223 @@ + +describe('eventLimit', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-08-01' // important that it is the first week, so works w/ month + week views + }; + }); + + describe('as a number', function() { + + beforeEach(function() { + options.eventLimit = 3; + }); + + [ 'month', 'basicWeek', 'agendaWeek' ].forEach(function(viewName) { + + describe('when in ' + viewName + ' view', function() { + + beforeEach(function() { + options.defaultView = viewName; + }); + + it('doesn\'t display a more link when limit is more than the # of events', function() { + options.events = [ + { title: 'event1', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' } + ]; + $('#cal').fullCalendar(options); + expect($('.fc-more').length).toBe(0); + }); + + it('doesn\'t display a more link when limit equal to the # of events', function() { + options.events = [ + { title: 'event1', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' } + ]; + $('#cal').fullCalendar(options); + expect($('.fc-more').length).toBe(0); + }); + + it('displays a more link when limit is less than the # of events', function() { + options.events = [ + { title: 'event1', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' } + ]; + $('#cal').fullCalendar(options); + expect($('.fc-more').length).toBe(1); + expect($('.fc-more')).toHaveText('+2 more'); + }); + + it('displays one more per day, when a multi-day event is above', function() { + options.events = [ + { title: 'event1', start: '2014-07-29', end: '2014-07-31' }, + { title: 'event2', start: '2014-07-29', end: '2014-07-31' }, + { title: 'event2', start: '2014-07-29', end: '2014-07-31' }, + { title: 'event2', start: '2014-07-29', end: '2014-07-31' } + ]; + $('#cal').fullCalendar(options); + var cells = $('.fc-day-grid .fc-row:eq(0) .fc-bg td:not(.fc-axis)'); + expect($('.fc-more').length).toBe(2); + expect($('.fc-more').eq(0)).toHaveText('+2 more'); + expect($('.fc-more').eq(0)).toBeBoundedBy(cells.eq(2)); + expect($('.fc-more').eq(1)).toHaveText('+2 more'); + expect($('.fc-more').eq(1)).toBeBoundedBy(cells.eq(3)); + }); + + it('will render a link in a multi-day event\'s second column ' + + 'if it has already been hidden in the first', + function() { + options.events = [ + { title: 'event1', start: '2014-07-29', end: '2014-07-31' }, + { title: 'event2', start: '2014-07-29', end: '2014-07-31' }, + { title: 'event2', start: '2014-07-29', end: '2014-07-31' }, + { title: 'event2', start: '2014-07-29' } + ]; + $('#cal').fullCalendar(options); + var cells = $('.fc-day-grid .fc-row:eq(0) .fc-bg td:not(.fc-axis)'); + expect($('.fc-more').length).toBe(2); + expect($('.fc-more').eq(0)).toHaveText('+2 more'); + expect($('.fc-more').eq(0)).toBeBoundedBy(cells.eq(2)); + expect($('.fc-more').eq(1)).toHaveText('+1 more'); + expect($('.fc-more').eq(1)).toBeBoundedBy(cells.eq(3)); + }); + + it('will render a link in a multi-day event\'s second column ' + + 'if it has already been hidden in the first even if he second column hardly has any events', + function() { + options.events = [ + { title: 'event1', start: '2014-07-28', end: '2014-07-30' }, + { title: 'event2', start: '2014-07-28', end: '2014-07-30' }, + { title: 'event2', start: '2014-07-28', end: '2014-07-30' }, + { title: 'event2', start: '2014-07-29', end: '2014-07-31' } + ]; + $('#cal').fullCalendar(options); + var cells = $('.fc-day-grid .fc-row:eq(0) .fc-bg td:not(.fc-axis)'); + var link = $('.fc-more').eq(0); // will appear to be the third link, but will be in first row, so 0dom + expect(link.length).toBe(1); + expect(link).toHaveText('+1 more'); + expect(link).toBeBoundedBy(cells.eq(3)); + }); + + it('will render a link in place of a hidden single day event, if covered by a multi-day', function() { + options.events = [ + { title: 'event1', start: '2014-07-28', end: '2014-07-30' }, + { title: 'event2', start: '2014-07-28', end: '2014-07-30' }, + { title: 'event2', start: '2014-07-28' }, + { title: 'event2', start: '2014-07-28' } + ]; + $('#cal').fullCalendar(options); + var cells = $('.fc-day-grid .fc-row:eq(0) .fc-bg td:not(.fc-axis)'); + var link = $('.fc-more').eq(0); + expect(link.length).toBe(1); + expect(link).toHaveText('+2 more'); + expect(link).toBeBoundedBy(cells.eq(1)); + }); + + it('will render a link in place of a hidden single day event, if covered by a multi-day ' + + 'and in its second column', + function() { + options.events = [ + { title: 'event1', start: '2014-07-28', end: '2014-07-30' }, + { title: 'event2', start: '2014-07-28', end: '2014-07-30' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' } + ]; + $('#cal').fullCalendar(options); + var cells = $('.fc-day-grid .fc-row:eq(0) .fc-bg td:not(.fc-axis)'); + var link = $('.fc-more').eq(0); + expect(link.length).toBe(1); + expect(link).toHaveText('+2 more'); + expect(link).toBeBoundedBy(cells.eq(2)); + }); + }); + }); + }); + + describe('when auto', function() { + + beforeEach(function() { + options.eventLimit = true; + }); + + describe('in month view', function() { + + beforeEach(function() { + options.defaultView = 'month'; + }); + + it('renders the heights of all the rows the same, regardless of # of events', function() { + options.events = [ + { title: 'event1', start: '2014-07-28', end: '2014-07-30' }, + { title: 'event2', start: '2014-07-28', end: '2014-07-30' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' } + ]; + $('#cal').fullCalendar(options); + var rowEls = $('.fc-day-grid .fc-row').slice(0, -1); // remove last b/c it will be a different height + expect(rowEls.length).toBeGreaterThan(0); + var height = rowEls.height(); + rowEls.each(function(i, node) { + expect($(node).height()).toBe(height); + }); + }); + }); + + [ 'month', 'basicWeek' ].forEach(function(viewName) { + + describe('in ' + viewName + ' view', function() { + + beforeEach(function() { + options.defaultView = viewName; + }); + + it('doesn\'t render a more link where there should obviously not be a limit', function() { + options.events = [ + { title: 'event1', start: '2014-07-28', end: '2014-07-30' } + ]; + $('#cal').fullCalendar(options); + expect($('.fc-more').length).toBe(0); + }); + }); + }); + + describe('in agendaWeek view', function() { + + beforeEach(function() { + options.defaultView = 'agendaWeek'; + }); + + it('behaves as if limit is 5', function() { + options.events = [ + { title: 'event1', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' } + ]; + $('#cal').fullCalendar(options); + expect($('.fc-event:visible').length).toBe(4); + expect($('.fc-more').length).toBe(1); + expect($('.fc-more')).toHaveText('+3 more'); + }); + }); + }); +}); diff --git a/tests/automated/eventLimitClick.js b/tests/automated/eventLimitClick.js new file mode 100644 index 0000000..98e800e --- /dev/null +++ b/tests/automated/eventLimitClick.js @@ -0,0 +1,135 @@ + +describe('eventLimitClick', function() { // simulate a click + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-08-01', // important that it is the first week, so works w/ month + week views + defaultView: 'month', + eventLimit: 3, + events: [ + { title: 'event1', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' } + ] + }; + }); + + describe('when set to "popover"', function() { + + beforeEach(function() { + options.eventLimitClick = 'popover'; + }); + + it('renders a popover upon click', function() { + $('#cal').fullCalendar(options); + $('.fc-more').simulate('click'); + expect($('.fc-more-popover')).toBeVisible(); + }); + + // more popover tests are done in eventLimit-popover + }); + + describe('when set to "week"', function() { + + beforeEach(function() { + options.eventLimitClick = 'week'; + }); + + it('should go to basicWeek if it is one of the available views', function() { + options.header = { + left: 'prev,next today', + center: 'title', + right: 'month,basicWeek,basicDay' + }; + $('#cal').fullCalendar(options); + $('.fc-more').simulate('click'); + var view = $('#cal').fullCalendar('getView'); + expect(view.name).toBe('basicWeek'); + }); + + it('should go to agendaWeek if it is one of the available views', function() { + options.header = { + left: 'prev,next today', + center: 'title', + right: 'month,agendaWeek,agendaDay' + }; + $('#cal').fullCalendar(options); + $('.fc-more').simulate('click'); + var view = $('#cal').fullCalendar('getView'); + expect(view.name).toBe('agendaWeek'); + }); + }); + + describe('when set to "day"', function() { + + beforeEach(function() { + options.eventLimitClick = 'day'; + }); + + it('should go to basicDay if it is one of the available views', function() { + options.header = { + left: 'prev,next today', + center: 'title', + right: 'month,basicWeek,basicDay' + }; + $('#cal').fullCalendar(options); + $('.fc-more').simulate('click'); + var view = $('#cal').fullCalendar('getView'); + expect(view.name).toBe('basicDay'); + }); + + it('should go to agendaDay if it is one of the available views', function() { + options.header = { + left: 'prev,next today', + center: 'title', + right: 'month,agendaWeek,agendaDay' + }; + $('#cal').fullCalendar(options); + $('.fc-more').simulate('click'); + var view = $('#cal').fullCalendar('getView'); + expect(view.name).toBe('agendaDay'); + }); + }); + + it('works with an explicit view name', function() { + options.eventLimitClick = 'agendaWeek'; + options.header = { + left: 'prev,next today', + center: 'title', + right: 'month,basicWeek,basicDay' + }; + $('#cal').fullCalendar(options); + $('.fc-more').simulate('click'); + var view = $('#cal').fullCalendar('getView'); + expect(view.name).toBe('agendaWeek'); + }); + + it('works with custom function and all the arguments are correct', function() { + options.eventLimitClick = function(cellInfo, jsEvent) { + expect(typeof cellInfo).toBe('object'); + expect(typeof jsEvent).toBe('object'); + expect(cellInfo.date).toEqualMoment('2014-07-29'); + expect(cellInfo.dayEl.data('date')).toBe('2014-07-29'); + expect(cellInfo.hiddenSegs.length).toBe(2); + expect(cellInfo.segs.length).toBe(4); + expect(cellInfo.moreEl).toHaveClass('fc-more'); + }; + $('#cal').fullCalendar(options); + $('.fc-more').simulate('click'); + }); + + it('works with custom function, and can return a view name', function() { + options.eventLimitClick = function(cellInfo, jsEvent) { + return 'agendaDay'; + }; + $('#cal').fullCalendar(options); + $('.fc-more').simulate('click'); + var view = $('#cal').fullCalendar('getView'); + expect(view.name).toBe('agendaDay'); + }); + +}); diff --git a/tests/automated/eventLimitText.js b/tests/automated/eventLimitText.js new file mode 100644 index 0000000..5c3ab2d --- /dev/null +++ b/tests/automated/eventLimitText.js @@ -0,0 +1,48 @@ + +describe('eventLimitText', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-08-01', // important that it is the first week, so works w/ month + week views + defaultView: 'month', + eventLimit: 3, + events: [ + { title: 'event1', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' }, + { title: 'event2', start: '2014-07-29' } + ] + }; + }); + + it('allows a string', function() { + options.eventLimitText = 'extra'; + $('#cal').fullCalendar(options); + expect($('.fc-more')).toHaveText('+2 extra'); + }); + + it('allows a function', function() { + options.eventLimitText = function(n) { + expect(typeof n).toBe('number'); + return 'there are ' + n + ' more events!'; + }; + $('#cal').fullCalendar(options); + expect($('.fc-more')).toHaveText('there are 2 more events!'); + }); + + it('has a default value that is affected by the custom locale', function() { + options.lang = 'fr'; + $('#cal').fullCalendar(options); + expect($('.fc-more')).toHaveText('+2 en plus'); + }); + + it('is not affected by a custom locale when the value is explicitly specified', function() { + options.lang = 'fr'; + options.eventLimitText = 'extra'; + $('#cal').fullCalendar(options); + expect($('.fc-more')).toHaveText('+2 extra'); + }); +}); diff --git a/tests/automated/eventMouseover.js b/tests/automated/eventMouseover.js new file mode 100644 index 0000000..5287d46 --- /dev/null +++ b/tests/automated/eventMouseover.js @@ -0,0 +1,40 @@ +describe('eventMouseover', function() { + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-08-01', + scrollTime: '00:00:00' + }; + }); + + [ 'month', 'agendaWeek' ].forEach(function(viewName) { + describe('for ' + viewName + ' view', function() { + beforeEach(function() { + options.defaultView = viewName; + }); + it('will trigger a eventMouseout with updateEvent', function(done) { + options.events = [ { + title: 'event', + start: '2014-08-02T01:00:00', + className: 'event' + } ]; + options.eventMouseover = function(event, ev) { + expect(typeof event).toBe('object'); + expect(typeof ev).toBe('object'); + event.title = "YO"; + $('#cal').fullCalendar('updateEvent', event); + }; + options.eventMouseout = function(event, ev) { + expect(typeof event).toBe('object'); + expect(typeof ev).toBe('object'); + done(); + }; + spyOn(options, 'eventMouseout').and.callThrough(); + $('#cal').fullCalendar(options); + $('.event').simulate('mouseover'); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/events-array.js b/tests/automated/events-array.js new file mode 100644 index 0000000..be4b933 --- /dev/null +++ b/tests/automated/events-array.js @@ -0,0 +1,57 @@ + +describe('events as an array', function() { + + var options; + var eventArray; + + beforeEach(function() { + affix('#cal'); + options = { + defaultView: 'month', + defaultDate: '2014-05-01' + }; + eventArray = [ + { + title: 'my event', + start: '2014-05-21' + } + ]; + }); + + it('accepts an event using basic form', function(done) { + options.events = eventArray; + options.eventRender = function(eventObj, eventElm) { + expect(eventObj.title).toEqual('my event'); + done(); + }; + $('#cal').fullCalendar(options); + }); + + it('accepts an event using extended form', function(done) { + options.eventSources = [ + { + className: 'customeventclass', + events: eventArray + } + ]; + options.eventRender = function(eventObj, eventElm) { + expect(eventObj.title).toEqual('my event'); + expect(eventElm).toHaveClass('customeventclass'); + done(); + }; + $('#cal').fullCalendar(options); + }); + + it('doesn\'t mutate the original array', function(done) { + var origArray = eventArray; + var origEvent = eventArray[0]; + options.events = eventArray; + options.eventRender = function(eventObj, eventElm) { + expect(origArray).toEqual(eventArray); + expect(origEvent).toEqual(eventArray[0]); + done(); + }; + $('#cal').fullCalendar(options); + }); + +}); diff --git a/tests/automated/events-function.js b/tests/automated/events-function.js new file mode 100644 index 0000000..b1cc459 --- /dev/null +++ b/tests/automated/events-function.js @@ -0,0 +1,134 @@ + +describe('events as a function', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultView: 'month', + defaultDate: '2014-05-01' + }; + }); + + it('requests correctly when no timezone', function(done) { + options.events = function(start, end, timezone, callback) { + expect(moment.isMoment(start)).toEqual(true); + expect(start.hasTime()).toEqual(false); + expect(start.hasZone()).toEqual(false); + expect(start.format()).toEqual('2014-04-27'); + expect(moment.isMoment(end)).toEqual(true); + expect(end.hasTime()).toEqual(false); + expect(end.hasZone()).toEqual(false); + expect(end.format()).toEqual('2014-06-08'); + expect(timezone).toEqual(false); + expect(typeof callback).toEqual('function'); + callback([]); + done(); + }; + $('#cal').fullCalendar(options); + }); + + it('requests correctly when local timezone', function(done) { + options.timezone = 'local'; + options.events = function(start, end, timezone, callback) { + expect(moment.isMoment(start)).toEqual(true); + expect(start.hasTime()).toEqual(false); + expect(start.hasZone()).toEqual(false); + expect(start.format()).toEqual('2014-04-27'); + expect(moment.isMoment(end)).toEqual(true); + expect(end.hasTime()).toEqual(false); + expect(end.hasZone()).toEqual(false); + expect(end.format()).toEqual('2014-06-08'); + expect(timezone).toEqual('local'); + expect(typeof callback).toEqual('function'); + callback([]); + done(); + }; + $('#cal').fullCalendar(options); + }); + + it('requests correctly when UTC timezone', function(done) { + options.timezone = 'UTC'; + options.events = function(start, end, timezone, callback) { + expect(moment.isMoment(start)).toEqual(true); + expect(start.hasTime()).toEqual(false); + expect(start.hasZone()).toEqual(false); + expect(start.format()).toEqual('2014-04-27'); + expect(moment.isMoment(end)).toEqual(true); + expect(end.hasTime()).toEqual(false); + expect(end.hasZone()).toEqual(false); + expect(end.format()).toEqual('2014-06-08'); + expect(timezone).toEqual('UTC'); + expect(typeof callback).toEqual('function'); + callback([]); + done(); + }; + $('#cal').fullCalendar(options); + }); + + it('requests correctly when custom timezone', function(done) { + options.timezone = 'America/Chicago'; + options.events = function(start, end, timezone, callback) { + expect(moment.isMoment(start)).toEqual(true); + expect(start.hasTime()).toEqual(false); + expect(start.hasZone()).toEqual(false); + expect(start.format()).toEqual('2014-04-27'); + expect(moment.isMoment(end)).toEqual(true); + expect(end.hasTime()).toEqual(false); + expect(end.hasZone()).toEqual(false); + expect(end.format()).toEqual('2014-06-08'); + expect(timezone).toEqual('America/Chicago'); + expect(typeof callback).toEqual('function'); + callback([]); + done(); + }; + $('#cal').fullCalendar(options); + }); + + it('requests correctly with event source extended form', function(done) { + var eventSource = { + className: 'customeventclass', + events: function(start, end, timezone, callback) { + expect(moment.isMoment(start)).toEqual(true); + expect(start.hasTime()).toEqual(false); + expect(start.hasZone()).toEqual(false); + expect(start.format()).toEqual('2014-04-27'); + expect(moment.isMoment(end)).toEqual(true); + expect(end.hasTime()).toEqual(false); + expect(end.hasZone()).toEqual(false); + expect(end.format()).toEqual('2014-06-08'); + expect(timezone).toEqual(false); + expect(typeof callback).toEqual('function'); + callback([ + { + title: 'event1', + start: '2014-05-10' + } + ]); + } + }; + spyOn(eventSource, 'events').and.callThrough(); + options.eventSources = [ eventSource ]; + options.eventRender = function(eventObj, eventElm) { + expect(eventSource.events.calls.count()).toEqual(1); + expect(eventElm).toHaveClass('customeventclass'); + done(); + }; + $('#cal').fullCalendar(options); + }); + + it('requests the correct dates when days at the start/end of the month are hidden', function(done) { + options.currentView = 'month'; + options.defaultDate = '2013-06-01'; // June 2013 has first day as Saturday, and last as Sunday! + options.weekends = false; + options.weekMode = 'variable'; + options.events = function(start, end, timezone, callback) { + expect(start).toEqualMoment('2013-06-03'); + expect(end).toEqualMoment('2013-06-29'); + done(); + }; + $('#cal').fullCalendar(options); + }); + +}); diff --git a/tests/automated/events-gcal.js b/tests/automated/events-gcal.js new file mode 100644 index 0000000..ea2b329 --- /dev/null +++ b/tests/automated/events-gcal.js @@ -0,0 +1,117 @@ + +describe('Google Calendar plugin', function() { + + var options; + var currentRequest; + + beforeEach(function() { + affix('#cal'); + + options = { + defaultView: 'month', + defaultDate: '2014-05-01', + events: 'http://www.google.com/calendar/feeds/notarealfeed/public/basic' + }; + + // workaround. wanted to use mockedAjaxCalls(), but JSONP requests get mangled later on + currentRequest = null; + $.mockjaxSettings.log = function(mockHandler, request) { + currentRequest = currentRequest || $.extend({}, request); // copy + }; + + // fake the JSONP call (which actually calls to /full) + $.mockjax({ + url: 'http://www.google.com/calendar/feeds/notarealfeed/public/*', + responseText: JSON.parse( + '{"version":"1.0","encoding":"UTF-8","feed":{"xmlns":"http://www.w3.org/2005/Atom","xmlns$openSearch"' + + ':"http://a9.com/-/spec/opensearchrss/1.0/","xmlns$gCal":"http://schemas.google.com/gCal/2005","xmlns' + + '$gd":"http://schemas.google.com/g/2005","id":{"$t":"http://www.google.com/calendar/feeds/usa__en%40h' + + 'oliday.calendar.google.com/public/full"},"updated":{"$t":"2014-05-22T13:00:40.000Z"},"category":[{"s' + + 'cheme":"http://schemas.google.com/g/2005#kind","term":"http://schemas.google.com/g/2005#event"}],"ti' + + 'tle":{"$t":"Holidays in United States","type":"text"},"subtitle":{"$t":"Holidays and Observances in ' + + 'United States","type":"text"},"link":[{"rel":"alternate","type":"text/html","href":"http://www.googl' + + 'e.com/calendar/embed?src=usa__en%40holiday.calendar.google.com"},{"rel":"http://schemas.google.com/g' + + '/2005#feed","type":"application/atom+xml","href":"http://www.google.com/calendar/feeds/usa__en%40hol' + + 'iday.calendar.google.com/public/full"},{"rel":"http://schemas.google.com/g/2005#batch","type":"appli' + + 'cation/atom+xml","href":"http://www.google.com/calendar/feeds/usa__en%40holiday.calendar.google.com/' + + 'public/full/batch"},{"rel":"self","type":"application/atom+xml","href":"http://www.google.com/calend' + + 'ar/feeds/usa__en%40holiday.calendar.google.com/public/full?alt=json-in-script&max-results=9999&start' + + '-min=2014-04-27T00%3A00%3A00Z&singleevents=true&start-max=2014-06-08T00%3A00%3A00Z"}],"author":[{"na' + + 'me":{"$t":"Holidays in United States"}}],"generator":{"$t":"Google Calendar","version":"1.0","uri":"' + + 'http://www.google.com/calendar"},"openSearch$totalResults":{"$t":2},"openSearch$startIndex":{"$t":1}' + + ',"openSearch$itemsPerPage":{"$t":9999},"gCal$timezone":{"value":"UTC"},"gCal$timesCleaned":{"value":' + + '0},"entry":[{"id":{"$t":"http://www.google.com/calendar/feeds/usa__en%40holiday.calendar.google.com/' + + 'public/full/20140511_60o30dr560o30e1g60o30dr4ck"},"published":{"$t":"2014-05-22T13:00:40.000Z"},"upd' + + 'ated":{"$t":"2014-05-22T13:00:40.000Z"},"category":[{"scheme":"http://schemas.google.com/g/2005#kind' + + '","term":"http://schemas.google.com/g/2005#event"}],"title":{"$t":"Mothers Day","type":"text"},"cont' + + 'ent":{"$t":"","type":"text"},"link":[{"rel":"alternate","type":"text/html","href":"http://www.google' + + '.com/calendar/event?eid=MjAxNDA1MTFfNjBvMzBkcjU2MG8zMGUxZzYwbzMwZHI0Y2sgdXNhX19lbkBo","title":"alter' + + 'nate"},{"rel":"self","type":"application/atom+xml","href":"http://www.google.com/calendar/feeds/usa_' + + '_en%40holiday.calendar.google.com/public/full/20140511_60o30dr560o30e1g60o30dr4ck"}],"author":[{"nam' + + 'e":{"$t":"Holidays in United States"}}],"gd$comments":{"gd$feedLink":{"href":"http://www.google.com/' + + 'calendar/feeds/usa__en%40holiday.calendar.google.com/public/full/20140511_60o30dr560o30e1g60o30dr4ck' + + '/comments"}},"gd$eventStatus":{"value":"http://schemas.google.com/g/2005#event.confirmed"},"gd$where' + + '":[{}],"gd$who":[{"email":"usa__en@holiday.calendar.google.com","rel":"http://schemas.google.com/g/2' + + '005#event.organizer","valueString":"Holidays in United States"}],"gd$when":[{"endTime":"2014-05-12",' + + '"startTime":"2014-05-11"}],"gd$transparency":{"value":"http://schemas.google.com/g/2005#event.transp' + + 'arent"},"gCal$anyoneCanAddSelf":{"value":"false"},"gCal$guestsCanInviteOthers":{"value":"true"},"gCa' + + 'l$guestsCanModify":{"value":"false"},"gCal$guestsCanSeeGuests":{"value":"true"},"gCal$sequence":{"va' + + 'lue":0},"gCal$uid":{"value":"20140511_60o30dr560o30e1g60o30dr4ck@google.com"}},{"id":{"$t":"http://w' + + 'ww.google.com/calendar/feeds/usa__en%40holiday.calendar.google.com/public/full/20140526_60o30dr56co3' + + '0e1g60o30dr4ck"},"published":{"$t":"2014-05-22T13:00:40.000Z"},"updated":{"$t":"2014-05-22T13:00:40.' + + '000Z"},"category":[{"scheme":"http://schemas.google.com/g/2005#kind","term":"http://schemas.google.c' + + 'om/g/2005#event"}],"title":{"$t":"Memorial Day","type":"text"},"content":{"$t":"","type":"text"},"li' + + 'nk":[{"rel":"alternate","type":"text/html","href":"http://www.google.com/calendar/event?eid=MjAxNDA1' + + 'MjZfNjBvMzBkcjU2Y28zMGUxZzYwbzMwZHI0Y2sgdXNhX19lbkBo","title":"alternate"},{"rel":"self","type":"app' + + 'lication/atom+xml","href":"http://www.google.com/calendar/feeds/usa__en%40holiday.calendar.google.co' + + 'm/public/full/20140526_60o30dr56co30e1g60o30dr4ck"}],"author":[{"name":{"$t":"Holidays in United Sta' + + 'tes"}}],"gd$comments":{"gd$feedLink":{"href":"http://www.google.com/calendar/feeds/usa__en%40holiday' + + '.calendar.google.com/public/full/20140526_60o30dr56co30e1g60o30dr4ck/comments"}},"gd$eventStatus":{"' + + 'value":"http://schemas.google.com/g/2005#event.confirmed"},"gd$where":[{}],"gd$who":[{"email":"usa__' + + 'en@holiday.calendar.google.com","rel":"http://schemas.google.com/g/2005#event.organizer","valueStrin' + + 'g":"Holidays in United States"}],"gd$when":[{"endTime":"2014-05-27","startTime":"2014-05-26"}],"gd$t' + + 'ransparency":{"value":"http://schemas.google.com/g/2005#event.transparent"},"gCal$anyoneCanAddSelf":' + + '{"value":"false"},"gCal$guestsCanInviteOthers":{"value":"true"},"gCal$guestsCanModify":{"value":"fal' + + 'se"},"gCal$guestsCanSeeGuests":{"value":"true"},"gCal$sequence":{"value":0},"gCal$uid":{"value":"201' + + '40526_60o30dr56co30e1g60o30dr4ck@google.com"}}]}}' + ) + }); + }); + + afterEach(function() { + $.mockjaxClear(); + $.mockjaxSettings.log = function() { }; + }); + + it('sends request correctly when no timezone', function() { + $('#cal').fullCalendar(options); + expect(currentRequest.data['start-min']).toEqual('2014-04-27'); + expect(currentRequest.data['start-max']).toEqual('2014-06-08'); + expect(currentRequest.data.ctz).toBeUndefined(); + }); + + it('sends request correctly when local timezone', function() { + options.timezone = 'local'; + $('#cal').fullCalendar(options); + expect(currentRequest.data['start-min']).toEqual('2014-04-27'); + expect(currentRequest.data['start-max']).toEqual('2014-06-08'); + expect(currentRequest.data.ctz).toBeUndefined(); + }); + + it('sends request correctly when UTC timezone', function() { + options.timezone = 'UTC'; + $('#cal').fullCalendar(options); + expect(currentRequest.data['start-min']).toEqual('2014-04-27'); + expect(currentRequest.data['start-max']).toEqual('2014-06-08'); + expect(currentRequest.data.ctz).toEqual('UTC'); + }); + + it('sends request correctly when custom timezone', function() { + options.timezone = 'America/Chicago'; + $('#cal').fullCalendar(options); + expect(currentRequest.data['start-min']).toEqual('2014-04-27'); + expect(currentRequest.data['start-max']).toEqual('2014-06-08'); + expect(currentRequest.data.ctz).toEqual('America/Chicago'); + }); + +}); \ No newline at end of file diff --git a/tests/automated/events-json-feed.js b/tests/automated/events-json-feed.js new file mode 100644 index 0000000..86e056a --- /dev/null +++ b/tests/automated/events-json-feed.js @@ -0,0 +1,122 @@ + +describe('events as a json feed', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + + options = { + defaultDate: '2014-05-01', + defaultView: 'month' + }; + + $.mockjax({ + url: '*', + contentType: 'text/json', + responseText: [ + { + title: 'my event', + start: '2014-05-21' + } + ] + }); + $.mockjaxSettings.log = function() { }; // don't console.log + }); + + afterEach(function() { + $.mockjaxClear(); + }); + + it('requests correctly when no timezone', function() { + options.events = 'my-feed.php'; + $('#cal').fullCalendar(options); + var request = $.mockjax.mockedAjaxCalls()[0]; + expect(request.data.start).toEqual('2014-04-27'); + expect(request.data.end).toEqual('2014-06-08'); + expect(request.data.timezone).toBeUndefined(); + }); + + it('requests correctly when local timezone', function() { + options.events = 'my-feed.php'; + options.timezone = 'local'; + $('#cal').fullCalendar(options); + var request = $.mockjax.mockedAjaxCalls()[0]; + expect(request.data.start).toEqual('2014-04-27'); + expect(request.data.end).toEqual('2014-06-08'); + expect(request.data.timezone).toBeUndefined(); + }); + + it('requests correctly when UTC timezone', function() { + options.events = 'my-feed.php'; + options.timezone = 'UTC'; + $('#cal').fullCalendar(options); + var request = $.mockjax.mockedAjaxCalls()[0]; + expect(request.data.start).toEqual('2014-04-27'); + expect(request.data.end).toEqual('2014-06-08'); + expect(request.data.timezone).toEqual('UTC'); + }); + + it('requests correctly when custom timezone', function() { + options.events = 'my-feed.php'; + options.timezone = 'America/Chicago'; + $('#cal').fullCalendar(options); + var request = $.mockjax.mockedAjaxCalls()[0]; + expect(request.data.start).toEqual('2014-04-27'); + expect(request.data.end).toEqual('2014-06-08'); + expect(request.data.timezone).toEqual('America/Chicago'); + }); + + it('requests correctly with event source extended form', function(done) { + var eventSource = { + url: 'my-feed.php', + className: 'customeventclass' + }; + options.eventSources = [ eventSource ]; + options.timezone = 'America/Chicago'; + options.eventRender = function(eventObj, eventElm) { + var request = $.mockjax.mockedAjaxCalls()[0]; + expect(request.data.start).toEqual('2014-04-27'); + expect(request.data.end).toEqual('2014-06-08'); + expect(request.data.timezone).toEqual('America/Chicago'); + expect(eventElm).toHaveClass('customeventclass'); + done(); + }; + $('#cal').fullCalendar(options); + }); + + it('accepts jQuery.ajax params', function(done) { + var eventSource = { + url: 'my-feed.php', + data: { + customParam: 'yes' + }, + success: function() { + var request = $.mockjax.mockedAjaxCalls()[0]; + expect(request.data.customParam).toMatch('yes'); + done(); + } + }; + options.eventSources = [ eventSource ]; + $('#cal').fullCalendar(options); + }); + + it('accepts a dynamic data function', function(done) { + var eventSource = { + url: 'my-feed.php', + data: function() { + return { + customParam: 'heckyeah' + }; + } + }; + options.eventSources = [ eventSource ]; + options.eventAfterAllRender = function() { + var request = $.mockjax.mockedAjaxCalls()[0]; + expect(request.data.customParam).toMatch('heckyeah'); + done(); + }; + $('#cal').fullCalendar(options); + }); + +}); diff --git a/tests/automated/external-dnd.js b/tests/automated/external-dnd.js new file mode 100644 index 0000000..6dcaf2e --- /dev/null +++ b/tests/automated/external-dnd.js @@ -0,0 +1,138 @@ + +describe('external drag and drop', function() { + + // TODO: fill out tests for droppable/drop/dropAccept, with RTL + + var options; + + beforeEach(function() { + options = { + defaultDate: '2014-08-23', + droppable: true + }; + + $('body').append( + '' + + '
' + + '
' + ); + $('#sidebar a').draggable(); + }); + + afterEach(function() { + $('#cal').remove(); + $('#sidebar').remove(); + }); + + function getMonthCell(row, col) { + return $('.fc-day-grid .fc-row:eq(' + row + ') .fc-bg td:not(.fc-axis):eq(' + col + ')'); + } + + describe('in month view', function() { + + beforeEach(function() { + options.defaultView = 'month'; + }); + + it('works after the view is changed', function(done) { // issue 2240 + var callCnt = 0; + + options.drop = function(date, jsEvent, ui) { + if (callCnt === 0) { + expect(date).toEqualMoment('2014-08-06'); + $('#sidebar .event1').remove(); + $('#cal').fullCalendar('next'); + $('#cal').fullCalendar('prev'); + setTimeout(function() { // needed for IE8 + $('#sidebar .event2').simulate('drag-n-drop', { + dropTarget: getMonthCell(1, 3) + }); + }, 0); + } + else if (callCnt === 1) { + expect(date).toEqualMoment('2014-08-06'); + done(); + } + callCnt++; + }; + + $('#cal').fullCalendar(options); + setTimeout(function() { // needed for IE8 + $('#sidebar .event1').simulate('drag-n-drop', { + dropTarget: getMonthCell(1, 3) + }); + }, 0); + }); + }); + + describe('in agenda view', function() { + + beforeEach(function() { + options.defaultView = 'agendaWeek'; + options.dragScroll = false; + options.scrollTime = '00:00:00'; + }); + + it('works after the view is changed', function(done) { + var callCnt = 0; + + options.drop = function(date, jsEvent, ui) { + if (callCnt === 0) { + expect(date).toEqualMoment('2014-08-20T01:00:00'); + $('#sidebar .event1').remove(); + $('#cal').fullCalendar('next'); + $('#cal').fullCalendar('prev'); + setTimeout(function() { // needed for IE8, for firing the second time, for some reason + $('#sidebar .event2').simulate('drag-n-drop', { + dropTarget: $('.fc-slats tr:eq(2)') // middle is 1:00am on 2014-08-20 + }); + }, 0); + } + else if (callCnt === 1) { + expect(date).toEqualMoment('2014-08-20T01:00:00'); + done(); + } + callCnt++; + }; + + $('#cal').fullCalendar(options); + setTimeout(function() { // needed for IE8 + $('#sidebar .event1').simulate('drag-n-drop', { + dropTarget: $('.fc-slats tr:eq(2)') // middle is 1:00am on 2014-08-20 + }); + }, 0); + }); + + it('works with timezone as "local"', function(done) { // for issue 2225 + options.timezone = 'local'; + options.drop = function(date, jsEvent) { + expect(date).toEqualMoment(moment('2014-08-20T01:00:00')); // compate it to a local moment + done(); + }; + $('#cal').fullCalendar(options); + setTimeout(function() { // needed for IE8 + $('#sidebar .event1').simulate('drag-n-drop', { + dropTarget: $('.fc-slats tr:eq(2)') // middle is 1:00am on 2014-08-20, LOCAL TIME + }); + }, 0); + }); + + it('works with timezone as "UTC"', function(done) { // for issue 2225 + options.timezone = 'UTC'; + options.drop = function(date, jsEvent) { + expect(date).toEqualMoment('2014-08-20T01:00:00+00:00'); + done(); + }; + $('#cal').fullCalendar(options); + setTimeout(function() { // needed for IE8 + $('#sidebar .event1').simulate('drag-n-drop', { + dropTarget: $('.fc-slats tr:eq(2)') // middle is 1:00am on 2014-08-20, LOCAL TIME + }); + }, 0); + }); + }); + +}); \ No newline at end of file diff --git a/tests/automated/firstDay.js b/tests/automated/firstDay.js new file mode 100644 index 0000000..0773fe7 --- /dev/null +++ b/tests/automated/firstDay.js @@ -0,0 +1,201 @@ + +describe('First Day', function() { + + beforeEach(function() { + affix('#cal'); + }); + + describe('when using default settings', function() { + beforeEach(function() { + $('#cal').fullCalendar(); + }); + it('should make Sunday the first day of the week', function() { + var dayFirst = $('.fc-day-header')[0]; + expect(dayFirst).toHaveClass('fc-sun'); + }); + }); + + describe('when setting firstDay to 0', function() { + beforeEach(function() { + var options = { + firstDay: 0 + }; + $('#cal').fullCalendar(options); + }); + it('should make Sunday the first day of the week', function() { + var daysOfWeek = $('.fc-day-header'); + expect(daysOfWeek[0]).toHaveClass('fc-sun'); + expect(daysOfWeek[1]).toHaveClass('fc-mon'); + expect(daysOfWeek[2]).toHaveClass('fc-tue'); + expect(daysOfWeek[3]).toHaveClass('fc-wed'); + expect(daysOfWeek[4]).toHaveClass('fc-thu'); + expect(daysOfWeek[5]).toHaveClass('fc-fri'); + expect(daysOfWeek[6]).toHaveClass('fc-sat'); + }); + }); + + describe('when setting firstDay to 1', function() { + beforeEach(function() { + var options = { + firstDay: 1 + }; + $('#cal').fullCalendar(options); + }); + it('should make Monday the first day of the week', function() { + var daysOfWeek = $('.fc-day-header'); + expect(daysOfWeek[0]).toHaveClass('fc-mon'); + expect(daysOfWeek[1]).toHaveClass('fc-tue'); + expect(daysOfWeek[2]).toHaveClass('fc-wed'); + expect(daysOfWeek[3]).toHaveClass('fc-thu'); + expect(daysOfWeek[4]).toHaveClass('fc-fri'); + expect(daysOfWeek[5]).toHaveClass('fc-sat'); + expect(daysOfWeek[6]).toHaveClass('fc-sun'); + }); + }); + + describe('when setting firstDay to 2', function() { + beforeEach(function() { + var options = { + firstDay: 2 + }; + $('#cal').fullCalendar(options); + }); + it('should make Tuesday the first day of the week', function() { + var daysOfWeek = $('.fc-day-header'); + expect(daysOfWeek[0]).toHaveClass('fc-tue'); + expect(daysOfWeek[1]).toHaveClass('fc-wed'); + expect(daysOfWeek[2]).toHaveClass('fc-thu'); + expect(daysOfWeek[3]).toHaveClass('fc-fri'); + expect(daysOfWeek[4]).toHaveClass('fc-sat'); + expect(daysOfWeek[5]).toHaveClass('fc-sun'); + expect(daysOfWeek[6]).toHaveClass('fc-mon'); + }); + }); + + describe('when setting firstDay to 3', function() { + beforeEach(function() { + var options = { + firstDay: 3 + }; + $('#cal').fullCalendar(options); + }); + it('should make Wednesday the first day of the week', function() { + var daysOfWeek = $('.fc-day-header'); + expect(daysOfWeek[0]).toHaveClass('fc-wed'); + expect(daysOfWeek[1]).toHaveClass('fc-thu'); + expect(daysOfWeek[2]).toHaveClass('fc-fri'); + expect(daysOfWeek[3]).toHaveClass('fc-sat'); + expect(daysOfWeek[4]).toHaveClass('fc-sun'); + expect(daysOfWeek[5]).toHaveClass('fc-mon'); + expect(daysOfWeek[6]).toHaveClass('fc-tue'); + }); + }); + + describe('when setting firstDay to 4', function() { + beforeEach(function() { + var options = { + firstDay: 4 + }; + $('#cal').fullCalendar(options); + }); + it('should make Thursday the first day of the week', function() { + var daysOfWeek = $('.fc-day-header'); + expect(daysOfWeek[0]).toHaveClass('fc-thu'); + expect(daysOfWeek[1]).toHaveClass('fc-fri'); + expect(daysOfWeek[2]).toHaveClass('fc-sat'); + expect(daysOfWeek[3]).toHaveClass('fc-sun'); + expect(daysOfWeek[4]).toHaveClass('fc-mon'); + expect(daysOfWeek[5]).toHaveClass('fc-tue'); + expect(daysOfWeek[6]).toHaveClass('fc-wed'); + }); + }); + + describe('when setting firstDay to 5', function() { + beforeEach(function() { + var options = { + firstDay: 5 + }; + $('#cal').fullCalendar(options); + }); + it('should make Friday the first day of the week', function() { + var daysOfWeek = $('.fc-day-header'); + expect(daysOfWeek[0]).toHaveClass('fc-fri'); + expect(daysOfWeek[1]).toHaveClass('fc-sat'); + expect(daysOfWeek[2]).toHaveClass('fc-sun'); + expect(daysOfWeek[3]).toHaveClass('fc-mon'); + expect(daysOfWeek[4]).toHaveClass('fc-tue'); + expect(daysOfWeek[5]).toHaveClass('fc-wed'); + expect(daysOfWeek[6]).toHaveClass('fc-thu'); + }); + }); + + describe('when setting firstDay to 6', function() { + beforeEach(function() { + var options = { + firstDay: 6 + }; + $('#cal').fullCalendar(options); + }); + it('should make Saturday the first day of the week', function() { + var daysOfWeek = $('.fc-day-header'); + expect(daysOfWeek[0]).toHaveClass('fc-sat'); + expect(daysOfWeek[1]).toHaveClass('fc-sun'); + expect(daysOfWeek[2]).toHaveClass('fc-mon'); + expect(daysOfWeek[3]).toHaveClass('fc-tue'); + expect(daysOfWeek[4]).toHaveClass('fc-wed'); + expect(daysOfWeek[5]).toHaveClass('fc-thu'); + expect(daysOfWeek[6]).toHaveClass('fc-fri'); + }); + }); + + describe('when new firstDay options are set', function() { + it('should change the first day of week to Monday', function() { + $('#cal').fullCalendar({ + firstDay: 1 + }); + expect($('.fc-day-header')[0]).toHaveClass('fc-mon'); + }); + it('shoule change the first day of week to Thursday', function() { + $('#cal').fullCalendar({ + firstDay: 4 + }); + expect($('.fc-day-header')[0]).toHaveClass('fc-thu'); + }); + }); + + describe('when first day is set to Tuesday and isRTL is true', function() { + beforeEach(function() { + var options = { + firstDay: 2, + isRTL: true + }; + $('#cal').fullCalendar(options); + }); + it('should put days mon, sun, sat ...', function() { + var daysOfWeek = $('.fc-day-header'); + expect(daysOfWeek[0]).toHaveClass('fc-mon'); + expect(daysOfWeek[1]).toHaveClass('fc-sun'); + expect(daysOfWeek[2]).toHaveClass('fc-sat'); + expect(daysOfWeek[3]).toHaveClass('fc-fri'); + expect(daysOfWeek[4]).toHaveClass('fc-thu'); + expect(daysOfWeek[5]).toHaveClass('fc-wed'); + expect(daysOfWeek[6]).toHaveClass('fc-tue'); + }); + }); + + it('should have a different default value based on the language', function() { + $('#cal').fullCalendar({ + lang: 'en-gb' + }); + // firstDay will be 1 (Monday) in Great Britain + var daysOfWeek = $('.fc-day-header'); + expect(daysOfWeek[0]).toHaveClass('fc-mon'); + expect(daysOfWeek[1]).toHaveClass('fc-tue'); + expect(daysOfWeek[2]).toHaveClass('fc-wed'); + expect(daysOfWeek[3]).toHaveClass('fc-thu'); + expect(daysOfWeek[4]).toHaveClass('fc-fri'); + expect(daysOfWeek[5]).toHaveClass('fc-sat'); + expect(daysOfWeek[6]).toHaveClass('fc-sun'); + }); + +}); \ No newline at end of file diff --git a/tests/automated/fixedWeekCount.js b/tests/automated/fixedWeekCount.js new file mode 100644 index 0000000..984f5cb --- /dev/null +++ b/tests/automated/fixedWeekCount.js @@ -0,0 +1,48 @@ + +describe('fixedWeekCount', function() { + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultView: 'month', + defaultDate: '2014-07-01' // has 5 weeks + }; + }); + + describe('when true', function() { + beforeEach(function() { + options.fixedWeekCount = true; + }); + it('renders a 5-week month with 6 rows', function() { + $('#cal').fullCalendar(options); + var weeks = $('.fc-week'); + expect(weeks.length).toBe(6); + }); + }); + + describe('when false', function() { + beforeEach(function() { + options.fixedWeekCount = false; + }); + it('renders a 5-week month with 5 rows', function() { + $('#cal').fullCalendar(options); + var weeks = $('.fc-week'); + expect(weeks.length).toBe(5); + }); + }); + + [ true, false ].forEach(function(bool) { + describe('regardless of value (' + bool + ')', function() { + beforeEach(function() { + options.fixedWeekCount = bool; + options.defaultDate = '2014-08-01'; // has 6 weeks + }); + it('should render a 6-week month consistently', function() { + $('#cal').fullCalendar(options); + var weeks = $('.fc-week'); + expect(weeks.length).toBe(6); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/forceEventDuration.js b/tests/automated/forceEventDuration.js new file mode 100644 index 0000000..ea52e62 --- /dev/null +++ b/tests/automated/forceEventDuration.js @@ -0,0 +1,64 @@ +describe('forceEventDuration', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + + options = { + defaultDate: '2014-05-01', + defaultView: 'month' + }; + }); + + describe('when turned off', function() { + beforeEach(function() { + options.forceEventDuration = false; + }); + it('allows a null end date for all-day and timed events', function() { + options.events = [ + { + id: '1', + start: '2014-05-10' + }, + { + id: '2', + start: '2014-05-10T14:00:00' + } + ]; + $('#cal').fullCalendar(options); + var events = $('#cal').fullCalendar('clientEvents'); + expect(events[0].end).toBeNull(); + expect(events[1].end).toBeNull(); + }); + }); + + describe('when turned on', function() { + beforeEach(function() { + options.forceEventDuration = true; + }); + it('allows a null end date for all-day and timed events', function() { + options.events = [ + { + id: '1', + start: '2014-05-10' + }, + { + id: '2', + start: '2014-05-10T14:00:00' + } + ]; + $('#cal').fullCalendar(options); + var events = $('#cal').fullCalendar('clientEvents'); + expect(events[0].id).toEqual('1'); + expect(moment.isMoment(events[0].end)).toEqual(true); + expect(events[1].id).toEqual('2'); + expect(moment.isMoment(events[1].end)).toEqual(true); + }); + }); + + // NOTE: the actual verification of the correct calculation of the end + // (using defaultTimedEventDuration and defaultAllDayEventDuration) + // is done in those test files. + +}); \ No newline at end of file diff --git a/tests/automated/formatRange.js b/tests/automated/formatRange.js new file mode 100644 index 0000000..2796566 --- /dev/null +++ b/tests/automated/formatRange.js @@ -0,0 +1,148 @@ + +describe('formatRange', function() { + + it('doesn\'t do any splitting when dates have different years', function() { + var s = $.fullCalendar.formatRange('2014-01-01', '2015-01-01', 'MMMM Do YYYY'); + expect(s).toEqual('January 1st 2014 - January 1st 2015'); + }); + + it('splits correctly on day when dates have same month', function() { + var s = $.fullCalendar.formatRange('2014-01-01', '2014-01-05', 'MMMM Do YYYY'); + expect(s).toEqual('January 1st - 5th 2014'); + }); + + it('splits correctly on day when dates have same month and smaller unit in front', function() { + var s = $.fullCalendar.formatRange('2014-01-01', '2014-01-05', 'dddd MMMM Do YYYY'); + expect(s).toEqual('Wednesday January 1st - Sunday January 5th 2014'); + }); + + it('splits correctly on the time when dates have the same day', function() { + var s = $.fullCalendar.formatRange('2014-01-01T06:00:00', '2014-01-01T08:00:00', 'MMMM Do YYYY h:mma'); + expect(s).toEqual('January 1st 2014 6:00am - 8:00am'); + }); + + it('splits correctly on the time when dates have the same day and hour but different am/pm', function() { + var s = $.fullCalendar.formatRange('2014-01-01T06:00:00', '2014-01-01T18:00:00', 'MMMM Do YYYY h:mma'); + expect(s).toEqual('January 1st 2014 6:00am - 6:00pm'); + }); + + it('splits correctly on the time when the dates have the same hour', function() { + var s = $.fullCalendar.formatRange('2014-01-01T06:00:00', '2014-01-01T06:30:00', 'MMMM Do YYYY h:mma'); + expect(s).toEqual('January 1st 2014 6:00am - 6:30am'); + }); + + it('outputs the single date when the dates have the same day and time', function() { + var s = $.fullCalendar.formatRange('2014-01-01T06:00:00', '2014-01-01T06:00:00', 'MMMM Do YYYY h:mma'); + expect(s).toEqual('January 1st 2014 6:00am'); + }); + + it('outputs the single date when the dates have the same day and the format string is vague', function() { + var s = $.fullCalendar.formatRange('2014-01-01', '2014-01-01', 'MMMM Do YYYY'); + expect(s).toEqual('January 1st 2014'); + }); + + it('uses a custom separator', function() { + var s = $.fullCalendar.formatRange( + '2014-01-01T06:00:00', + '2014-01-01T06:30:00', + 'MMMM Do YYYY h:mma', + '<...>' + ); + expect(s).toEqual('January 1st 2014 6:00am<...>6:30am'); + }); + + describe('when called with isRTL', function() { + + it('doesn\'t do any splitting when dates have different years', function() { + var s = $.fullCalendar.formatRange('2014-01-01', '2015-01-01', 'MMMM Do YYYY', null, true); + expect(s).toEqual('January 1st 2015 - January 1st 2014'); + }); + + it('splits correctly on day when dates have same month', function() { + var s = $.fullCalendar.formatRange('2014-01-01', '2014-01-05', 'MMMM Do YYYY', null, true); + expect(s).toEqual('January 5th - 1st 2014'); + }); + + it('splits correctly on day when dates have same month and smaller unit in front', function() { + var s = $.fullCalendar.formatRange('2014-01-01', '2014-01-05', 'dddd MMMM Do YYYY', null, true); + expect(s).toEqual('Sunday January 5th - Wednesday January 1st 2014'); + }); + + it('splits correctly on the time when dates have the same day', function() { + var s = $.fullCalendar.formatRange('2014-01-01T06:00:00', '2014-01-01T08:00:00', 'MMMM Do YYYY h:mma', null, true); + expect(s).toEqual('January 1st 2014 8:00am - 6:00am'); + }); + + it('splits correctly on the time when dates have the same day and hour but different am/pm', function() { + var s = $.fullCalendar.formatRange('2014-01-01T06:00:00', '2014-01-01T18:00:00', 'MMMM Do YYYY h:mma', null, true); + expect(s).toEqual('January 1st 2014 6:00pm - 6:00am'); + }); + + it('splits correctly on the time when the dates have the same hour', function() { + var s = $.fullCalendar.formatRange('2014-01-01T06:00:00', '2014-01-01T06:30:00', 'MMMM Do YYYY h:mma', null, true); + expect(s).toEqual('January 1st 2014 6:30am - 6:00am'); + }); + + it('outputs the single date when the dates have the same day and time', function() { + var s = $.fullCalendar.formatRange('2014-01-01T06:00:00', '2014-01-01T06:00:00', 'MMMM Do YYYY h:mma', null, true); + expect(s).toEqual('January 1st 2014 6:00am'); + }); + + it('outputs the single date when the dates have the same day and the format string is vague', function() { + var s = $.fullCalendar.formatRange('2014-01-01', '2014-01-01', 'MMMM Do YYYY', null, true); + expect(s).toEqual('January 1st 2014'); + }); + + it('uses a custom separator', function() { + var s = $.fullCalendar.formatRange( + '2014-01-01T06:00:00', + '2014-01-01T06:30:00', + 'MMMM Do YYYY h:mma', + '<...>', + true + ); + expect(s).toEqual('January 1st 2014 6:30am<...>6:00am'); + }); + + }); + + describe('when calendar has isRTL', function() { + + it('splits correctly on day when dates have same month', function() { + affix('#cal'); + $('#cal').fullCalendar({ + defaultView: 'basicWeek', + defaultDate: '2014-05-20', + isRTL: true, + titleFormat: 'MMMM Do YYYY' + }); + expect($('.fc-toolbar h2')).toHaveText('May 24th - 18th 2014'); + }); + + }); + + describe('when calendar has a customized lang', function() { + + it('uses language and splits correctly on day when dates have same month', function() { + affix('#cal'); + $('#cal').fullCalendar({ + defaultView: 'basicWeek', + defaultDate: '2014-05-20', + lang: 'fr', + titleFormat: 'dddd MMMM D YYYY' + }); + expect($('.fc-toolbar h2')).toHaveText('lundi mai 19 - dimanche mai 25 2014'); + }); + + }); + + it('splits correctly on day when dates have same month, when given real moments', function() { + var s = $.fullCalendar.formatRange( + moment.utc('2014-01-01'), + moment.utc('2015-01-01'), + 'MMMM Do YYYY' + ); + expect(s).toEqual('January 1st 2014 - January 1st 2015'); + }); + +}); diff --git a/tests/automated/handleWindowResize.js b/tests/automated/handleWindowResize.js new file mode 100644 index 0000000..8059d1a --- /dev/null +++ b/tests/automated/handleWindowResize.js @@ -0,0 +1,22 @@ + +describe('handleWindowResize', function() { + + beforeEach(function() { + affix('#cal'); + }); + + // not true + /* + xdescribe('When default is used, should fire resize event', function() { + it('should fire resize', function() { + var resized = 0; + $('#cal').fullCalendar({ + windowResize: function(view) { + resized++; + } + }); + expect(resized).toEqual(1); + }); + }); + */ +}); \ No newline at end of file diff --git a/tests/automated/header-navigation.js b/tests/automated/header-navigation.js new file mode 100644 index 0000000..5e8fc79 --- /dev/null +++ b/tests/automated/header-navigation.js @@ -0,0 +1,60 @@ + +describe('header navigation', function() { + + beforeEach(function() { + affix('#calendar'); + var options = { + header: { + left: 'next,prev,prevYear,nextYear today', + center: '', + right: 'title' + } + }; + $('#calendar').fullCalendar(options); + }); + + describe('and click next', function() { + it('should change view to next month', function() { + $('#calendar').fullCalendar('gotoDate', '2010-02-01'); + $('.fc-next-button').simulate('click'); + var newDate = $('#calendar').fullCalendar('getDate'); + expect(newDate).toEqualMoment('2010-03-01'); + }); + }); + + describe('and click prev', function() { + it('should change view to prev month', function() { + $('#calendar').fullCalendar('gotoDate', '2010-02-01'); + $('.fc-prev-button').simulate('click'); + var newDate = $('#calendar').fullCalendar('getDate'); + expect(newDate).toEqualMoment('2010-01-01'); + }); + }); + + describe('and click prevYear', function() { + it('should change view to prev month', function() { + $('#calendar').fullCalendar('gotoDate', '2010-02-01'); + $('.fc-prevYear-button').simulate('click'); + var newDate = $('#calendar').fullCalendar('getDate'); + expect(newDate).toEqualMoment('2009-02-01'); + }); + }); + + describe('and click nextYear', function() { + it('should change view to prev month', function() { + $('#calendar').fullCalendar('gotoDate', '2010-02-01'); + $('.fc-nextYear-button').simulate('click'); + var newDate = $('#calendar').fullCalendar('getDate'); + expect(newDate).toEqualMoment('2011-02-01'); + }); + }); + + describe('and click today', function() { + it('should change view to prev month', function() { + $('#calendar').fullCalendar('gotoDate', '2010-02-01'); + $('.fc-today-button').simulate('click'); + var newDate = $('#calendar').fullCalendar('getDate'); + expect(newDate).toEqualNow(); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/header-rendering.js b/tests/automated/header-rendering.js new file mode 100644 index 0000000..827eb5d --- /dev/null +++ b/tests/automated/header-rendering.js @@ -0,0 +1,136 @@ + +describe('header rendering', function() { + + beforeEach(function() { + affix('#calendar'); + }); + + describe('when using default header options', function() { + it('should have title as default on left', function() { + $('#calendar').fullCalendar(); + expect($('#calendar > .fc-toolbar > .fc-left > *')).toBeMatchedBy('h2'); + }); + it('should have empty center', function() { + $('#calendar').fullCalendar(); + var center = $('#calendar > .fc-toolbar > .fc-center'); + expect(center).toBeEmpty(); + }); + it('should have right with today|space|left|right', function() { + $('#calendar').fullCalendar(); + var rightChildren = $('#calendar > .fc-toolbar > .fc-right > *'); + var todayButton = rightChildren.eq(0); + var buttonGroup = rightChildren.eq(1); + var prevNextButtons = buttonGroup.children(); + expect(todayButton).toHaveClass('fc-today-button'); + expect(buttonGroup).toHaveClass('fc-button-group'); + expect(prevNextButtons.eq(0)).toHaveClass('fc-prev-button'); + expect(prevNextButtons.eq(1)).toHaveClass('fc-next-button'); + }); + }); + + describe('when supplying header options', function() { + beforeEach(function() { + var options = { + header: { + left: 'next,prev', + center: 'prevYear today nextYear agendaView,dayView', + right: 'title' + } + }; + $('#calendar').fullCalendar(options); + }); + it('should have title on the right', function() { + expect($('#calendar > .fc-toolbar > .fc-right > *')).toBeMatchedBy('h2'); + }); + it('should have next|prev on left', function() { + var buttonGroup = $('#calendar > .fc-toolbar > .fc-left > *'); + var prevNextButtons = buttonGroup.children(); + expect(prevNextButtons.eq(0)).toHaveClass('fc-next-button'); + expect(prevNextButtons.eq(1)).toHaveClass('fc-prev-button'); + }); + it('should have prevYear|space|today|space|nextYear in center', function() { + var items = $('#calendar > .fc-toolbar > .fc-center > *'); + expect(items.eq(0)).toHaveClass('fc-prevYear-button'); + expect(items.eq(1)).toHaveClass('fc-today-button'); + expect(items.eq(2)).toHaveClass('fc-nextYear-button'); + }); + }); + + describe('when setting header to false', function() { + beforeEach(function() { + var options = { + header: false + }; + $('#calendar').fullCalendar(options); + }); + it('should not have header table', function() { + expect($('.fc-toolbar')).not.toBeInDOM(); + }); + }); + + describe('renders left and right literally', function() { + [ true, false ].forEach(function(isRTL) { + describe('when isRTL is ' + isRTL, function() { + beforeEach(function() { + var options = {}; + $('#calendar').fullCalendar({ + header: { + left: 'prev', + center: 'today', + right: 'next' + }, + isRTL: isRTL + }); + }); + it('should have prev in left', function() { + var fcHeaderLeft = $('#calendar .fc-toolbar > .fc-left'); + expect(fcHeaderLeft).toContainElement('.fc-prev-button'); + }); + it('should have today in center', function() { + var fcHeaderCenter = $('#calendar .fc-toolbar > .fc-center'); + expect(fcHeaderCenter).toContainElement('.fc-today-button'); + }); + it('should have next in right', function() { + var fcHeaderRight = $('#calendar .fc-toolbar > .fc-right'); + expect(fcHeaderRight).toContainElement('.fc-next-button'); + }); + }); + }); + }); + + describe('when calendar is within a form', function() { + beforeEach(function() { + $('#calendar').wrap('
'); + }); + it('should not submit the form when clicking the button', function(done) { + var options = { + header: { + left: 'prev,next', + right: 'title' + } + }; + var unloadCalled = false; + + function beforeUnloadHandler() { + console.log('when calendar is within a form, it submits!!!'); + unloadCalled = true; + cleanup(); + return 'click stay on this page'; + } + $(window).on('beforeunload', beforeUnloadHandler); + + function cleanup() { + $(window).off('beforeunload', beforeUnloadHandler); + } + + $('#calendar').fullCalendar(options); + $('.fc-next-button').simulate('click'); + + setTimeout(function() { // wait to see if handler was called + expect(unloadCalled).toBe(false); + cleanup(); + done(); + }, 100); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/height-and-contentHeight.js b/tests/automated/height-and-contentHeight.js new file mode 100644 index 0000000..0fd1300 --- /dev/null +++ b/tests/automated/height-and-contentHeight.js @@ -0,0 +1,253 @@ +(function() { + +[ 'height', 'contentHeight' ].forEach(function(heightProp) { describe(heightProp, function() { + + var options; + var heightElm; + var asAMethod; + + beforeEach(function() { + affix('#cal'); + $('#cal').width(900); + options = { + defaultDate: '2014-08-01' + }; + }); + + function init(heightVal) { + if (asAMethod) { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('option', heightProp, heightVal); + } + else { + options[heightProp] = heightVal; + $('#cal').fullCalendar(options); + } + + if (heightProp === 'height') { + heightElm = $('.fc'); + } + else { + heightElm = $('.fc-view'); + } + } + + function expectHeight(heightVal) { + var diff; + if (heightProp === 'height') { + // Firefox is reporting off-by-one difference sometimes, even when things are good :( + diff = Math.abs(heightElm.outerHeight() - heightVal); + expect(diff).toBeLessThan(2); // off-by-one or exactly the same + } + else { + expect(heightElm.outerHeight()).toBe(heightVal); + } + } + + $.each({ + 'as an init option': false, + 'as a method': true + }, function(desc, bool) { describe(desc, function() { + + beforeEach(function() { + asAMethod = bool; + }); + + describe('when in month view', function() { + beforeEach(function() { + options.defaultView = 'month'; + }); + + describe('as a number, when there are no events', function() { + it('should be the specified height, with no scrollbars', function() { + init(600); + expect(heightElm.outerHeight()).toBe(600); + expect('.fc-day-grid-container').not.toHaveScrollbars(); + }); + }); + + describe('as a number, when there is one tall row of events', function() { + beforeEach(function() { + options.events = repeatClone({ title: 'event', start: '2014-08-04' }, 9); + }); + it('should take away height from other rows, but not do scrollbars', function() { + init(600); + var rows = $('.fc-day-grid .fc-row'); + var tallRow = rows.eq(1); + var shortRows = rows.not(tallRow); // 0, 2, 3, 4, 5 + var shortHeight = shortRows.eq(0).outerHeight(); + + expectHeight(600); + + shortRows.each(function(i, node) { + var rowHeight = $(node).outerHeight(); + var diff = Math.abs(rowHeight - shortHeight); + expect(diff).toBeLessThan(10); // all roughly the same + }); + + expect(tallRow.outerHeight()).toBeGreaterThan(shortHeight * 2); // much taller + expect('.fc-day-grid-container').not.toHaveScrollbars(); + }); + }); + + describe('as a number, when there are many tall rows of events', function() { + beforeEach(function() { + options.events = [].concat( + repeatClone({ title: 'event0', start: '2014-07-28' }, 9), + repeatClone({ title: 'event1', start: '2014-08-04' }, 9), + repeatClone({ title: 'event2', start: '2014-08-11' }, 9), + repeatClone({ title: 'event3', start: '2014-08-18' }, 9), + repeatClone({ title: 'event4', start: '2014-08-25' }, 9), + repeatClone({ title: 'event5', start: '2014-09-01' }, 9) + ); + }); + it('height is correct and scrollbars show up', function() { + init(600); + expectHeight(600); + expect($('.fc-day-grid-container')).toHaveScrollbars(); + }); + }); + + describe('as "auto", when there are many tall rows of events', function() { + beforeEach(function() { + options.events = [].concat( + repeatClone({ title: 'event0', start: '2014-07-28' }, 9), + repeatClone({ title: 'event1', start: '2014-08-04' }, 9), + repeatClone({ title: 'event2', start: '2014-08-11' }, 9), + repeatClone({ title: 'event3', start: '2014-08-18' }, 9), + repeatClone({ title: 'event4', start: '2014-08-25' }, 9), + repeatClone({ title: 'event5', start: '2014-09-01' }, 9) + ); + }); + it('height is really tall and there are no scrollbars', function() { + init('auto'); + expect(heightElm.outerHeight()).toBeGreaterThan(1000); // pretty tall + expect($('.fc-day-grid-container')).not.toHaveScrollbars(); + }); + }); + }); + + [ 'basicWeek', 'basicDay' ].forEach(function(viewName) { + describe('in ' + viewName + ' view', function() { + beforeEach(function() { + options.defaultView = viewName; + }); + + describe('as a number, when there are no events', function() { + it('should be the specified height, with no scrollbars', function() { + init(600); + expectHeight(600); + expect('.fc-day-grid-container').not.toHaveScrollbars(); + }); + }); + + describe('as a number, when there are many events', function() { + beforeEach(function() { + options.events = repeatClone({ title: 'event', start: '2014-08-01' }, 100); + }); + it('should have the correct height, with scrollbars', function() { + init(600); + expectHeight(600); + expect('.fc-day-grid-container').toHaveScrollbars(); + }); + }); + + describe('as "auto", when there are many events', function() { + beforeEach(function() { + options.events = repeatClone({ title: 'event', start: '2014-08-01' }, 100); + }); + it('should be really tall with no scrollbars', function() { + init('auto'); + expect(heightElm.outerHeight()).toBeGreaterThan(1000); // pretty tall + expect('.fc-day-grid-container').not.toHaveScrollbars(); + }); + }); + }); + }); + + [ 'agendaWeek', 'agendaDay' ].forEach(function(viewName) { + describe('in ' + viewName + ' view', function() { + beforeEach(function() { + options.defaultView = viewName; + }); + + $.each({ + 'with no all-day section': { allDaySlot: false }, + 'with no all-day events': { }, + 'with some all-day events': { events: repeatClone({ title: 'event', start: '2014-08-01' }, 6) } + }, function(desc, moreOptions) { + describe(desc, function() { + beforeEach(function() { + $.extend(options, moreOptions); + }); + + describe('as a number, with only a few slots', function() { + beforeEach(function() { + options.minTime = '06:00:00'; + options.maxTime = '10:00:00'; + }); + it('should be the correct height, with a horizontal rule to occupy space', function() { + init(600); + expectHeight(600); + expect($('.fc-time-grid > hr')).toBeVisible(); + }); + }); + + describe('as a number, with many slots', function() { + beforeEach(function() { + options.minTime = '00:00:00'; + options.maxTime = '24:00:00'; + }); + it('should be the correct height, with scrollbars and no filler hr', function() { + init(600); + expectHeight(600); + expect($('.fc-time-grid-container')).toHaveScrollbars(); + expect($('.fc-time-grid > hr')).not.toBeVisible(); + }); + }); + + describe('as "auto", with only a few slots', function() { + beforeEach(function() { + options.minTime = '06:00:00'; + options.maxTime = '10:00:00'; + }); + it('should be really short with no scrollbars nor horizontal rule', function() { + init('auto'); + expect(heightElm.outerHeight()).toBeLessThan(500); // pretty short + expect($('.fc-time-grid-container')).not.toHaveScrollbars(); + expect($('.fc-time-grid > hr')).not.toBeVisible(); + }); + }); + + describe('as a "auto", with many slots', function() { + beforeEach(function() { + options.minTime = '00:00:00'; + options.maxTime = '24:00:00'; + }); + it('should be really tall with no scrollbars nor horizontal rule', function() { + init('auto'); + expect(heightElm.outerHeight()).toBeGreaterThan(900); // pretty tall + expect($('.fc-time-grid-container')).not.toHaveScrollbars(); + expect($('.fc-time-grid > hr')).not.toBeVisible(); + }); + }); + }); + }); + }); + }); + }); }); +}); }); + + +function repeatClone(srcObj, times) { + var a = []; + var i; + + for (i = 0; i < times; i++) { + a.push($.extend({}, srcObj)); + } + + return a; +} + +})(); \ No newline at end of file diff --git a/tests/automated/hiddenDays.js b/tests/automated/hiddenDays.js new file mode 100644 index 0000000..9b2ae74 --- /dev/null +++ b/tests/automated/hiddenDays.js @@ -0,0 +1,88 @@ + +describe('hiddenDays', function() { + + beforeEach(function() { + affix('#cal'); + }); + + describe('when using default', function() { + beforeEach(function() { + $('#cal').fullCalendar(); + }); + it('should show 7 days of the week', function() { + var daysCount = $('.fc-day-header').length; + expect(daysCount).toEqual(7); + }); + }); + + describe('when setting an empty hiddenDays', function() { + beforeEach(function() { + $('#cal').fullCalendar({ + hiddenDays: [] + }); + }); + it('should return 7 days of the week', function() { + var daysCount = $('.fc-day-header').length; + expect(daysCount).toEqual(7); + }); + }); + + describe('when setting hiddenDays with 1', function() { + beforeEach(function() { + $('#cal').fullCalendar({ + hiddenDays: [ 1 ] + }); + }); + it('should return 6 days', function() { + var daysCount = $('.fc-day-header').length; + expect(daysCount).toEqual(6); + }); + it('should return sun,tue,wed..sat days', function() { + var daysOfWeek = $('.fc-day-header'); + expect(daysOfWeek[0]).toHaveClass('fc-sun'); + expect(daysOfWeek[1]).toHaveClass('fc-tue'); + expect(daysOfWeek[5]).toHaveClass('fc-sat'); + }); + it('should expect 7th day to be undefined', function() { + var daysOfWeek = $('.fc-day-header'); + expect(daysOfWeek[6]).toBeUndefined(); + }); + }); + + describe('when setting hiddenDays with 3,5', function() { + beforeEach(function() { + $('#cal').fullCalendar({ + hiddenDays: [ 3, 5 ] + }); + }); + it('should return 6 days', function() { + var daysCount = $('.fc-day-header').length; + expect(daysCount).toEqual(5); + }); + it('should return s,m,t,t,s ', function() { + var daysOfWeek = $('.fc-day-header'); + expect(daysOfWeek[0]).toHaveClass('fc-sun'); + expect(daysOfWeek[1]).toHaveClass('fc-mon'); + expect(daysOfWeek[2]).toHaveClass('fc-tue'); + expect(daysOfWeek[3]).toHaveClass('fc-thu'); + expect(daysOfWeek[4]).toHaveClass('fc-sat'); + }); + it('should expect wed and fri be undefined', function() { + var fri = $('.fc-day-header.fc-fri')[0]; + var wed = $('.fc-day-header.fc-wed')[0]; + expect(fri).toBeUndefined(); + expect(wed).toBeUndefined(); + }); + }); + + describe('when setting all hiddenDays', function() { + it('should expect to throw an exception', function() { + var options = { + hiddenDays: [ 0, 1, 2, 3, 4, 5, 6 ] + }; + expect(function() { + $('#cal').fullCalendar(options); + }).toThrow('invalid hiddenDays'); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/isRTL.js b/tests/automated/isRTL.js new file mode 100644 index 0000000..435a236 --- /dev/null +++ b/tests/automated/isRTL.js @@ -0,0 +1,15 @@ +describe('isRTL', function() { + + it('has it\'s default value computed differently based off of the language', function() { + affix('#cal'); + $('#cal').fullCalendar({ + lang: 'ar' // Arabic is RTL + }); + var isRTL = $('#cal').fullCalendar('option', 'isRTL'); + expect(isRTL).toEqual(true); + }); + + // NOTE: don't put tests related to other options in here! + // Put them in the test file for the individual option! + +}); \ No newline at end of file diff --git a/tests/automated/lang.js b/tests/automated/lang.js new file mode 100644 index 0000000..2ed6928 --- /dev/null +++ b/tests/automated/lang.js @@ -0,0 +1,53 @@ + +describe('lang', function() { + + afterEach(function() { + moment.lang('en'); + }); + + it('is not affected by global moment lang when unset', function() { + moment.lang('fr'); + affix('#cal'); + $('#cal').fullCalendar(); + var calendar = $('#cal').fullCalendar('getCalendar'); + var mom = calendar.moment('2014-05-01'); + var s = mom.format('dddd MMMM Do YYYY'); + expect(s).toEqual('Thursday May 1st 2014'); + }); + + it('is not affected by global moment lang when unset', function() { + moment.lang('fr'); + affix('#cal'); + $('#cal').fullCalendar({ + lang: 'es' + }); + var calendar = $('#cal').fullCalendar('getCalendar'); + var mom = calendar.moment('2014-05-01'); + var s = mom.format('dddd MMMM Do YYYY'); + expect(s).toEqual('jueves mayo 1º 2014'); + }); + + it('doesn\'t side-effect the global moment lang when customized', function() { + moment.lang('fr'); + affix('#cal'); + $('#cal').fullCalendar({ + lang: 'es' + }); + var mom = moment.utc('2014-05-01'); + var s = mom.format('dddd MMMM Do YYYY'); + expect(s).toEqual('jeudi mai 1er 2014'); + expect(moment.lang()).toEqual('fr'); + }); + + it('defaults to English when configured to language that isn\'t loaded', function() { + affix('#cal'); + $('#cal').fullCalendar({ + lang: 'zz' + }); + var calendar = $('#cal').fullCalendar('getCalendar'); + var mom = calendar.moment('2014-05-01'); + var s = mom.format('dddd MMMM Do YYYY'); + expect(s).toEqual('Thursday May 1st 2014'); + }); + +}); \ No newline at end of file diff --git a/tests/automated/maxTime.js b/tests/automated/maxTime.js new file mode 100644 index 0000000..c0eb550 --- /dev/null +++ b/tests/automated/maxTime.js @@ -0,0 +1,124 @@ +describe('maxTime', function() { + + beforeEach(function() { + affix('#cal'); + }); + + var numToStringConverter = function(timeIn) { + var time = (timeIn % 12) || 12; + var amPm = 'am'; + if ((timeIn % 24) > 11) { + amPm = 'pm'; + } + return time + amPm; + }; + + describe('when using the default settings', function() { + + describe('in agendaWeek', function() { + it('should start at 12am', function() { + var options = { + defaultView: 'agendaWeek' + }; + $('#cal').fullCalendar(options); + var lastSlotText = $('.fc-slats tr:not(.fc-minor):last .fc-time').text(); + expect(lastSlotText).toEqual('11pm'); + }); + }); + + describe('in agendaDay', function() { + it('should start at 12am', function() { + var options = { + defaultView: 'agendaDay' + }; + $('#cal').fullCalendar(options); + var lastSlotText = $('.fc-slats tr:not(.fc-minor):last .fc-time').text(); + expect(lastSlotText).toEqual('11pm'); + }); + }); + }); + + describe('when using a whole number', function() { + + var hourNumbers = [ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 ]; + + describe('in agendaWeek', function() { + beforeEach(function() { + affix('#cal2'); + }); + hourNumbers.forEach(function(hourNumber) { + it('should end at ' + hourNumber, function() { + var options = { + defaultView: 'agendaWeek', + maxTime: { hours: hourNumber } + }; + $('#cal2').fullCalendar(options); + var lastSlotText = $('.fc-slats tr:not(.fc-minor):last .fc-time').text(); + var expected = numToStringConverter(hourNumber - 1); + expect(lastSlotText).toEqual(expected); + }); + }); + }); + + describe('in agendaDay', function() { + beforeEach(function() { + affix('#cal2'); + }); + hourNumbers.forEach(function(hourNumber) { + it('should end at ' + hourNumber, function() { + var options = { + defaultView: 'agendaDay', + maxTime: hourNumber + ':00' // in addition, test string duration input + }; + $('#cal2').fullCalendar(options); + var lastSlotText = $('.fc-slats tr:not(.fc-minor):last .fc-time').text(); + var expected = numToStringConverter(hourNumber - 1); + expect(lastSlotText).toEqual(expected); + }); + }); + }); + }); + + describe('when using default slotInterval and \'uneven\' maxTime', function() { + + var hourNumbers = [ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 ]; + + describe('in agendaWeek', function() { + beforeEach(function() { + affix('#cal2'); + }); + hourNumbers.forEach(function(hourNumber) { + it('should end at ' + hourNumber + ':20', function() { + var options = { + defaultView: 'agendaWeek', + maxTime: { hours: hourNumber, minutes: 20 } + }; + $('#cal2').fullCalendar(options); + var lastSlotText = $('.fc-slats tr:not(.fc-minor):last .fc-time').text(); + // since exclusive end is :20, last slot will be on the current hour's 00:00 + var expected = numToStringConverter(hourNumber); + expect(lastSlotText).toEqual(expected); + }); + }); + }); + + describe('in agendaDay', function() { + beforeEach(function() { + affix('#cal2'); + }); + hourNumbers.forEach(function(hourNumber) { + it('should end at ' + hourNumber + ':20', function() { + var options = { + defaultView: 'agendaDay', + maxTime: { hours: hourNumber, minutes: 20 } + }; + $('#cal2').fullCalendar(options); + var lastSlotText = $('.fc-slats tr:not(.fc-minor):last .fc-time').text(); + // since exclusive end is :20, last slot will be on the current hour's 00:00 + var expected = numToStringConverter(hourNumber); + expect(lastSlotText).toEqual(expected); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/minTime.js b/tests/automated/minTime.js new file mode 100644 index 0000000..76c7593 --- /dev/null +++ b/tests/automated/minTime.js @@ -0,0 +1,126 @@ +describe('minTime', function() { + + beforeEach(function() { + affix('#cal'); + }); + + var numToStringConverter = function(timeIn, mins) { + var time = (timeIn % 12) || 12; + var amPm = 'am'; + if ((timeIn % 24) > 11) { + amPm = 'pm'; + } + return time + (mins != null ? ':' + mins : '') + amPm; + }; + + describe('when using the default settings', function() { + + describe('in agendaWeek', function() { + it('should start at 12am', function() { + var options = { + defaultView: 'agendaWeek' + }; + $('#cal').fullCalendar(options); + var firstSlotText = $('.fc-slats tr:eq(0) .fc-time').text(); + expect(firstSlotText).toEqual('12am'); + }); + }); + + describe('in agendaDay', function() { + it('should start at 12am', function() { + var options = { + defaultView: 'agendaDay' + }; + $('#cal').fullCalendar(options); + var firstSlotText = $('.fc-slats tr:eq(0) .fc-time').text(); + expect(firstSlotText).toEqual('12am'); + }); + }); + }); + + describe('when using a whole number', function() { + + var hourNumbers = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]; + + describe('in agendaWeek', function() { + beforeEach(function() { + affix('#cal2'); + }); + hourNumbers.forEach(function(hourNumber) { + it('should start at ' + hourNumber, function() { + var options = { + defaultView: 'agendaWeek', + minTime: { hours: hourNumber } + }; + $('#cal2').fullCalendar(options); + var firstSlotText = $('.fc-slats tr:eq(0) .fc-time').text(); + var expected = numToStringConverter(hourNumber); + expect(firstSlotText).toEqual(expected); + }); + }); + }); + + describe('in agendaDay', function() { + beforeEach(function() { + affix('#cal2'); + }); + hourNumbers.forEach(function(hourNumber) { + it('should start at ' + hourNumber, function() { + var options = { + defaultView: 'agendaDay', + minTime: hourNumber + ':00' // in addition, test string duration input + }; + $('#cal2').fullCalendar(options); + var firstSlotText = $('.fc-slats tr:eq(0) .fc-time').text(); + var expected = numToStringConverter(hourNumber); + expect(firstSlotText).toEqual(expected); + }); + }); + }); + }); + + describe('when using default slotInterval and \'uneven\' minTime', function() { + + var hourNumbers = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]; + + describe('in agendaWeek', function() { + beforeEach(function() { + affix('#cal2'); + }); + hourNumbers.forEach(function(hourNumber) { + it('should start at ' + hourNumber + ':20', function() { + var options = { + defaultView: 'agendaWeek', + minTime: { hours: hourNumber, minutes: 20 } + }; + $('#cal2').fullCalendar(options); + var slatRows = $('.fc-slats tr'); + expect(slatRows.eq(0)).toHaveClass('fc-minor'); + expect(slatRows.eq(1)).toHaveClass('fc-minor'); + expect(slatRows.eq(2)).toHaveClass('fc-minor'); + // TODO: fix bad behavior in src where no slots have text + }); + }); + }); + + describe('in agendaDay', function() { + beforeEach(function() { + affix('#cal2'); + }); + hourNumbers.forEach(function(hourNumber) { + it('should start at ' + hourNumber + ':20', function() { + var options = { + defaultView: 'agendaDay', + minTime: { hours: hourNumber, minutes: 20 } + }; + $('#cal2').fullCalendar(options); + var slatRows = $('.fc-slats tr'); + expect(slatRows.eq(0)).toHaveClass('fc-minor'); + expect(slatRows.eq(1)).toHaveClass('fc-minor'); + expect(slatRows.eq(2)).toHaveClass('fc-minor'); + // TODO: fix bad behavior in src where no slots have text + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/moment-ambig.js b/tests/automated/moment-ambig.js new file mode 100644 index 0000000..6b686d6 --- /dev/null +++ b/tests/automated/moment-ambig.js @@ -0,0 +1,184 @@ + +describe('ambiguously-zoned moment', function() { + + it('has a false hasZone', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08T10:00:00'); + expect(mom.hasZone()).toBe(false); + }); + + it('has a true hasTime', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08T10:00:00'); + expect(mom.hasTime()).toBe(true); + }); + + it('has a zero zone', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08T10:00:00'); + expect(mom.zone()).toBe(0); + }); + + it('formats without a zone part', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08T10:00:00'); + expect(mom.format()).toBe('2014-06-08T10:00:00'); + }); + + it('formats via toISOString without a zone part', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08T10:00:00'); + expect(mom.toISOString()).toBe('2014-06-08T10:00:00'); + }); + + it('is correctly cloned', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08T10:00:00'); + var clone = mom.clone(); + expect(clone.hasZone()).toBe(false); + expect(clone.format()).toBe('2014-06-08T10:00:00'); + expect(clone).not.toBe(mom); + clone.add(1, 'months'); + expect(+clone).not.toBe(+mom); + }); + + it('can be given a zone via utc', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08T10:00:00'); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(false); + expect(mom.zone()).toBe(0); + mom.utc(); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(0); + }); + + it('can be given a zone via local', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08T10:00:00'); + var equivDate = new Date(Date.UTC(2014, 5, 8, 10, 0, 0)); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 10, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(false); + expect(mom.zone()).toBe(0); + mom.local(); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 10, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(equivDate.getTimezoneOffset()); + }); + + it('can be given a zone via zone', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08T10:00:00'); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(false); + expect(mom.zone()).toBe(0); + mom.zone(-420); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(-420); + }); + +}); + +describe('ambiguously-timed moment', function() { + + it('has a false hasTime', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08'); + expect(mom.hasTime()).toBe(false); + }); + + it('has a false hasZone', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08'); + expect(mom.hasZone()).toBe(false); + }); + + it('has a zero time', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08'); + var time = mom.time(); + expect(+time).toBe(0); + }); + + it('formats without a zone part', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08'); + expect(mom.format()).toBe('2014-06-08'); + }); + + it('formats via toISOString without a time part', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08'); + expect(mom.toISOString()).toBe('2014-06-08'); + }); + + it('is correctly cloned', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08'); + var clone = mom.clone(); + expect(clone.hasTime()).toBe(false); + expect(clone.format()).toBe('2014-06-08'); + expect(clone).not.toBe(mom); + clone.add(1, 'months'); + expect(+clone).not.toBe(+mom); + }); + + it('can be given a time', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08'); + expect(mom.hasTime()).toBe(false); + var time = moment.duration({ hours: 1, minutes: 25 }); + mom.time(time); + expect(mom.hasTime()).toBe(true); + expect(+mom.time()).toBe(+time); + }); + + it('can be given a time and zone via utc', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08'); + expect(mom.hasTime()).toBe(false); + expect(mom.hasZone()).toBe(false); + expect(mom.zone()).toBe(0); + mom.utc(); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(0); + }); + + it('can be given a time and zone via local', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08'); + var equivDate = new Date(2014, 5, 8, 10, 0, 0); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(false); + expect(mom.hasZone()).toBe(false); + expect(mom.zone()).toBe(0); + mom.local(); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(equivDate.getTimezoneOffset()); + }); + + it('can be given a time and zone via zone', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-08'); + expect(mom.hasTime()).toBe(false); + expect(mom.hasZone()).toBe(false); + expect(mom.zone()).toBe(0); + mom.zone(-420); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(-420); + }); + +}); + +describe('unambiguous moment', function() { + + it('can be made ambiguously-zoned via stripZone', function() { + var mom = $.fullCalendar.moment.utc('2014-06-08T10:00:00-0000'); + expect(mom.hasZone()).toBe(true); + expect(mom.hasTime()).toBe(true); + mom.stripZone(); + expect(mom.format()).toBe('2014-06-08T10:00:00'); + expect(mom.hasZone()).toBe(false); + expect(mom.hasTime()).toBe(true); + }); + + it('can be made ambigously-timed via stripTime', function() { + var mom = $.fullCalendar.moment.utc('2014-06-08T10:00:00-0000'); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + mom.stripTime(); + expect(mom.format()).toBe('2014-06-08'); + expect(mom.hasTime()).toBe(false); + expect(mom.hasZone()).toBe(false); + }); + +}); diff --git a/tests/automated/moment-construct.js b/tests/automated/moment-construct.js new file mode 100644 index 0000000..c7585c0 --- /dev/null +++ b/tests/automated/moment-construct.js @@ -0,0 +1,646 @@ +(function() { + +describe('$.fullCalendar.moment', function() { + testDefaultProcessing($.fullCalendar.moment); +}); + +describe('$.fullCalendar.moment.utc', function() { + testForcedUTCProcessing($.fullCalendar.moment.utc); +}); + +describe('$.fullCalendar.moment.parseZone', function() { + testLiteralProcessing($.fullCalendar.moment.parseZone); +}); + +describe('Calendar::moment', function() { + [ + { + description: 'when there is no timezone', + timezone: false, + testMethod: testLiteralProcessing + }, + { + description: 'when timezone is local', + timezone: 'local', + testMethod: testForcedLocalProcessing + }, + { + description: 'when timezone is UTC', + timezone: 'UTC', + testMethod: testForcedUTCProcessing + }, + { + description: 'when timezone is custom', + timezone: 'America/Unknown', + testMethod: testLiteralProcessing + } + ] + .forEach(function(scenario) { + describe(scenario.description, function() { + var calendarObj; + + beforeEach(function() { + affix('#cal'); + $('#cal').fullCalendar({ + timezone: scenario.timezone + }); + calendarObj = $('#cal').fullCalendar('getCalendar'); + }); + + scenario.testMethod(function() { + return calendarObj.moment.apply(calendarObj, arguments); + }); + }); + }); +}); + +function testDefaultProcessing(construct) { + + describe('when given an ISO8601 string', function() { + + it('is local regardless of inputted zone', function() { + var mom = construct('2014-06-08T10:00:00+0130'); + var simpleMoment = moment('2014-06-08T10:00:00+0130'); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(simpleMoment.zone()); + }); + + it('parses as local when no zone', function() { + var mom = construct('2014-06-08T10:00:00'); + var dateEquiv = new Date(2014, 5, 8, 10); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 10, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + + it('accepts an ambiguous time', function() { + var mom = construct('2014-06-08'); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(false); + expect(mom.hasZone()).toBe(false); + }); + + it('assumes first-of-month and ambiguous time when no date-of-month', function() { + var mom = construct('2014-06'); + expect(mom.toArray()).toEqual([ 2014, 5, 1, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(false); + expect(mom.hasZone()).toBe(false); + }); + }); + + it('parses string/format combo as local', function() { + var mom = construct('12-25-1995', 'MM-DD-YYYY'); + var dateEquiv = new Date(1995, 11, 25); + expect(mom.toArray()).toEqual([ 1995, 11, 25, 0, 0, 0, 0 ]); + expect(mom.hasZone()).toBe(true); + expect(mom.hasTime()).toBe(true); + expect(mom.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + + it('is local when given no arguments', function() { + var mom = construct(); + var dateEquiv = new Date(); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + + it('is local when given a native Date', function() { + var date = new Date(); + var mom = construct(date); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(date.getTimezoneOffset()); + }); + + describe('when given an array', function() { + + it('is local and has a time when given hours/minutes/seconds', function() { + var a = [ 2014, 5, 8, 11, 0, 0 ]; + var dateEquiv = new Date(2014, 5, 8, 11, 0, 0); + var mom = construct(a); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 11, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + + it('is local and has a time even when no hours/minutes/seconds', function() { + var a = [ 2014, 5, 8 ]; + var dateEquiv = new Date(2014, 5, 8); + var mom = construct(a); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + }); + + describe('when given an existing FullCalendar moment', function() { + + it('remains ambiguously-zoned', function() { + var noTzMoment = $.fullCalendar.moment.parseZone('2014-05-28T00:00:00'); + var newMoment = construct(noTzMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(false); + }); + + it('remains ambiguously-timed', function() { + var noTimeMoment = $.fullCalendar.moment('2014-05-28'); + var newMoment = construct(noTimeMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(false); + expect(newMoment.hasZone()).toBe(false); + }); + }); + + [ + { description: 'when given an existing FullCalendar moment', moment: $.fullCalendar.moment }, + { description: 'when given an existing basic moment', moment: moment } + ] + .forEach(function(scenario) { + describe(scenario.description, function() { + + it('remains local', function() { + var localMoment = scenario.moment('2014-05-28T00:00:00'); + var newMoment = construct(localMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(localMoment.zone()); + }); + + it('remains UTC', function() { + var utcMoment = scenario.moment.utc('2014-05-28T00:00:00'); + var newMoment = construct(utcMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(0); + }); + + it('remains in a custom timezone', function() { + var tzMoment = scenario.moment.parseZone('2014-05-28T00:00:00+13:00'); + var newMoment = construct(tzMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(-780); + }); + + it('produces a new moment that is in no way bound to the old', function() { + var oldMoment = scenario.moment(); + var newMoment = construct(oldMoment); + expect(newMoment).not.toBe(oldMoment); + expect(+newMoment).toBe(+oldMoment); + newMoment.add(1, 'months'); + expect(+newMoment).not.toBe(+oldMoment); + }); + }); + }); +} + +function testForcedLocalProcessing(construct) { + + describe('when given an ISO8601 string', function() { + + it('is local regardless of inputted zone', function() { + var mom = construct('2014-06-08T10:00:00+0130'); + var simpleMoment = moment('2014-06-08T10:00:00+0130'); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(simpleMoment.zone()); + }); + + it('parses as local when no zone', function() { + var mom = construct('2014-06-08T10:00:00'); + var dateEquiv = new Date(2014, 5, 8, 10); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 10, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + + it('accepts an ambiguous time', function() { + var mom = construct('2014-06-08'); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(false); + expect(mom.hasZone()).toBe(false); + }); + + it('assumes first-of-month and ambiguous time when no date-of-month', function() { + var mom = construct('2014-06'); + expect(mom.toArray()).toEqual([ 2014, 5, 1, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(false); + expect(mom.hasZone()).toBe(false); + }); + }); + + it('parses string/format combo as local', function() { + var mom = construct('12-25-1995', 'MM-DD-YYYY'); + var dateEquiv = new Date(1995, 11, 25); + expect(mom.toArray()).toEqual([ 1995, 11, 25, 0, 0, 0, 0 ]); + expect(mom.hasZone()).toBe(true); + expect(mom.hasTime()).toBe(true); + expect(mom.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + + it('is local when given no arguments', function() { + var mom = construct(); + var dateEquiv = new Date(); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + + it('is local when given a native Date', function() { + var date = new Date(); + var mom = construct(date); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(date.getTimezoneOffset()); + }); + + describe('when given an array', function() { + + it('is local and has a time when given hours/minutes/seconds', function() { + var a = [ 2014, 5, 8, 11, 0, 0 ]; + var dateEquiv = new Date(2014, 5, 8, 11, 0, 0); + var mom = construct(a); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 11, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + + it('is local and has a time even when no hours/minutes/seconds', function() { + var a = [ 2014, 5, 8 ]; + var dateEquiv = new Date(2014, 5, 8); + var mom = construct(a); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + }); + + describe('when given an existing FullCalendar moment', function() { + + it('converts to local when ambiguously-zoned', function() { + var noTzMoment = $.fullCalendar.moment.parseZone('2014-05-28T00:00:00'); + var newMoment = construct(noTzMoment); + var dateEquiv = new Date(2014, 4, 28); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + + it('remains ambiguously-timed', function() { + var noTimeMoment = $.fullCalendar.moment('2014-05-28'); + var newMoment = construct(noTimeMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(false); + expect(newMoment.hasZone()).toBe(false); + }); + }); + + [ + { description: 'when given an existing FullCalendar moment', moment: $.fullCalendar.moment }, + { description: 'when given an existing basic moment', moment: moment } + ] + .forEach(function(scenario) { + describe(scenario.description, function() { + + it('remains local', function() { + var localMoment = scenario.moment('2014-05-28T00:00:00'); + var newMoment = construct(localMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(localMoment.zone()); + }); + + it('converts to local when UTC', function() { + var utcMoment = scenario.moment.utc('2014-05-28T00:00:00'); + var newMoment = construct(utcMoment); + var dateEquiv = new Date(Date.UTC(2014, 4, 28)); + expect(+newMoment).toBe(+dateEquiv); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + + it('converts to local when in a custom zone', function() { + var tzMoment = scenario.moment.parseZone('2014-05-28T00:00:00+13:00'); + var dateEquiv = tzMoment.toDate(); + var newMoment = construct(tzMoment); + expect(+newMoment).toBe(+dateEquiv); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + + it('produces a new moment that is in no way bound to the old', function() { + var oldMoment = scenario.moment(); + var newMoment = construct(oldMoment); + expect(newMoment).not.toBe(oldMoment); + expect(+newMoment).toBe(+oldMoment); + newMoment.add(1, 'months'); + expect(+newMoment).not.toBe(+oldMoment); + }); + }); + }); +} + +function testForcedUTCProcessing(construct) { + + describe('when given an ISO8601 string', function() { + + it('is UTC regardless of inputted zone', function() { + var mom = construct('2014-06-08T10:00:00+0130'); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(0); + }); + + it('parses as UTC when no zone', function() { + var mom = construct('2014-06-08T10:00:00'); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 10, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(0); + }); + + it('accepts an ambiguous time', function() { + var mom = construct('2014-06-08'); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(false); + expect(mom.hasZone()).toBe(false); + }); + + it('assumes first-of-month and ambiguous time when no date-of-month', function() { + var mom = construct('2014-06'); + expect(mom.toArray()).toEqual([ 2014, 5, 1, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(false); + expect(mom.hasZone()).toBe(false); + }); + }); + + it('parses string/format combo as UTC', function() { + var mom = construct('12-25-1995', 'MM-DD-YYYY'); + expect(mom.toArray()).toEqual([ 1995, 11, 25, 0, 0, 0, 0 ]); + expect(mom.hasZone()).toBe(true); + expect(mom.hasTime()).toBe(true); + expect(mom.zone()).toBe(0); + }); + + it('is UTC when given no arguments', function() { + var mom = construct(); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(0); + }); + + it('is UTC when given a native Date', function() { + var date = new Date(); + var mom = construct(date); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(0); + }); + + describe('when given an array', function() { + + it('is UTC and has a time when given hours/minutes/seconds', function() { + var a = [ 2014, 5, 8, 11, 0, 0 ]; + var mom = construct(a); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 11, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(0); + }); + + it('is UTC and has a time even when no hours/minutes/seconds', function() { + var a = [ 2014, 5, 8 ]; + var mom = construct(a); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(0); + }); + }); + + describe('when given an existing FullCalendar moment', function() { + + it('converts to UTC when ambiguously-zoned', function() { + var noTzMoment = $.fullCalendar.moment.utc('2014-05-28T00:00:00'); + var newMoment = construct(noTzMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(0); + }); + + it('remains ambiguously-timed', function() { + var noTimeMoment = $.fullCalendar.moment('2014-05-28'); + var newMoment = construct(noTimeMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(false); + expect(newMoment.hasZone()).toBe(false); + }); + }); + + [ + { description: 'when given an existing FullCalendar moment', moment: $.fullCalendar.moment }, + { description: 'when given an existing basic moment', moment: moment } + ] + .forEach(function(scenario) { + describe(scenario.description, function() { + + it('converts to UTC when local', function() { + var localMoment = scenario.moment('2014-05-28T00:00:00'); + var newMoment = construct(localMoment); + expect(+newMoment).toBe(+localMoment); // same point in time + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(0); + }); + + it('remains UTC', function() { + var utcMoment = scenario.moment.utc('2014-05-28T00:00:00'); + var newMoment = construct(utcMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(0); + }); + + it('converts to UTC when in a custom zone', function() { + var tzMoment = scenario.moment.parseZone('2014-05-28T00:00:00+13:00'); + var newMoment = construct(tzMoment); + expect(+newMoment).toBe(+tzMoment); // same point in time + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(0); + }); + + it('produces a new moment that is in no way bound to the old', function() { + var oldMoment = scenario.moment.utc(); + var newMoment = construct(oldMoment); + expect(newMoment).not.toBe(oldMoment); + expect(+newMoment).toBe(+oldMoment); + newMoment.add(1, 'months'); + expect(+newMoment).not.toBe(+oldMoment); + }); + }); + }); +} + +function testLiteralProcessing(construct) { + + describe('when given an ISO8601 string', function() { + + it('retains the inputted zone', function() { + var mom = construct('2014-06-08T11:00:00+0130'); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 11, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(-90); + }); + + it('accepts an ambiguous zone', function() { + var mom = construct('2014-06-08T11:00:00'); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 11, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(false); + }); + + it('accepts an ambiguous time', function() { + var mom = construct('2014-06-08'); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(false); + expect(mom.hasZone()).toBe(false); + }); + + it('assumes first-of-month and ambiguous time when no date-of-month', function() { + var mom = construct('2014-06'); + expect(mom.toArray()).toEqual([ 2014, 5, 1, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(false); + expect(mom.hasZone()).toBe(false); + }); + }); + + it('parses string/format combo as UTC', function() { + var mom = construct('12-25-1995', 'MM-DD-YYYY'); + expect(mom.toArray()).toEqual([ 1995, 11, 25, 0, 0, 0, 0 ]); + expect(mom.hasZone()).toBe(true); + expect(mom.hasTime()).toBe(true); + expect(mom.zone()).toBe(0); + }); + + it('is local when given no arguments', function() { + var mom = construct(); + var dateEquiv = new Date(); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(dateEquiv.getTimezoneOffset()); + }); + + it('is local when given a native Date', function() { + var date = new Date(); + var mom = construct(date); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(true); + expect(mom.zone()).toBe(date.getTimezoneOffset()); + }); + + describe('when given an array', function() { + + it('is ambiguously-zoned and has a time when given hours/minutes/seconds', function() { + var a = [ 2014, 5, 8, 11, 0, 0 ]; + var mom = construct(a); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 11, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(false); + }); + + it('is ambiguously-zoned and has a time even when no hours/minutes/seconds', function() { + var a = [ 2014, 5, 8 ]; + var mom = construct(a); + expect(mom.toArray()).toEqual([ 2014, 5, 8, 0, 0, 0, 0 ]); + expect(mom.hasTime()).toBe(true); + expect(mom.hasZone()).toBe(false); + }); + }); + + describe('when given an existing FullCalendar moment', function() { + + it('remains ambiguously-zoned', function() { + var noTzMoment = $.fullCalendar.moment.parseZone('2014-05-28T00:00:00'); + var newMoment = construct(noTzMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(false); + }); + + it('remains ambiguously-timed', function() { + var noTimeMoment = $.fullCalendar.moment('2014-05-28'); + var newMoment = construct(noTimeMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(false); + expect(newMoment.hasZone()).toBe(false); + }); + }); + + [ + { description: 'when given an existing FullCalendar moment', moment: $.fullCalendar.moment }, + { description: 'when given an existing basic moment', moment: moment } + ] + .forEach(function(scenario) { + describe(scenario.description, function() { + + it('remains local', function() { + var localMoment = scenario.moment('2014-05-28T00:00:00'); + var newMoment = construct(localMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(localMoment.zone()); + }); + + it('remains UTC', function() { + var utcMoment = scenario.moment.utc('2014-05-28T00:00:00'); + var newMoment = construct(utcMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(0); + }); + + it('remains in a custom timezone', function() { + var tzMoment = scenario.moment.parseZone('2014-05-28T00:00:00+13:00'); + var newMoment = construct(tzMoment); + expect(newMoment.toArray()).toEqual([ 2014, 4, 28, 0, 0, 0, 0 ]); + expect(newMoment.hasTime()).toBe(true); + expect(newMoment.hasZone()).toBe(true); + expect(newMoment.zone()).toBe(-780); + }); + + it('produces a new moment that is in no way bound to the old', function() { + var oldMoment = scenario.moment(); + var newMoment = construct(oldMoment); + expect(newMoment).not.toBe(oldMoment); + expect(+newMoment).toBe(+oldMoment); + newMoment.add(1, 'months'); + expect(+newMoment).not.toBe(+oldMoment); + }); + + }); + }); +} + +})(); \ No newline at end of file diff --git a/tests/automated/moment-formatting.js b/tests/automated/moment-formatting.js new file mode 100644 index 0000000..85f09c3 --- /dev/null +++ b/tests/automated/moment-formatting.js @@ -0,0 +1,68 @@ + +describe('moment date formatting', function() { + + it('should let vanilla momentjs formatting to work correctly', function() { + var mom = $.fullCalendar.moment.utc('2014-05-20T14:00:00'); + var s1 = mom.format('dddd, MMMM Do YYYY, h:mm:ss a'); + var s2 = mom.format('ddd, hA Z'); + expect(s1).toEqual('Tuesday, May 20th 2014, 2:00:00 pm'); + expect(s2).toEqual('Tue, 2PM +00:00'); + }); + + it('should allow momentjs text escaping', function() { + var mom = $.fullCalendar.moment.utc('2014-05-20T14:00:00'); + var s = mom.format('MMMM Do YYYY [TIME:] h:mm:ss a'); + expect(s).toEqual('May 20th 2014 TIME: 2:00:00 pm'); + }); + + it('should correctly output LT (regression)', function() { + var mom = $.fullCalendar.moment.utc('2014-05-20T06:00:00'); + var s = mom.format('ddd, LT'); + expect(s).toEqual('Tue, 6:00 AM'); + }); + + it('should correctly output hT (regression)', function() { + var mom = $.fullCalendar.moment.utc('2014-05-20T06:00:00'); + var s = mom.format('ddd, hT'); + expect(s).toEqual('Tue, 6A'); + }); + + it('should output A/P with the \'T\' formatting character', function() { + var mom1 = $.fullCalendar.moment.utc('2014-05-20T06:00:00'); + var mom2 = $.fullCalendar.moment.utc('2014-05-20T14:00:00'); + var s1 = mom1.format('ddd, h T'); + var s2 = mom2.format('ddd, h T'); + expect(s1).toEqual('Tue, 6 A'); + expect(s2).toEqual('Tue, 2 P'); + }); + + it('should output A/P with the \'t\' formatting character', function() { + var mom1 = $.fullCalendar.moment.utc('2014-05-20T06:00:00'); + var mom2 = $.fullCalendar.moment.utc('2014-05-20T14:00:00'); + var s1 = mom1.format('ddd, h t'); + var s2 = mom2.format('ddd, h t'); + expect(s1).toEqual('Tue, 6 a'); + expect(s2).toEqual('Tue, 2 p'); + }); + + it('should output non-zero numbers enclosed in parenthesis', function() { + var mom = $.fullCalendar.moment.utc('2014-05-20T06:30:00'); + var s = mom.format('ddd h(:mm)a'); + expect(s).toEqual('Tue 6:30am'); + }); + + it('should not output zero numbers enclosed in parenthesis', function() { + var mom = $.fullCalendar.moment.utc('2014-05-20T06:00:00'); + var s = mom.format('ddd h(:mm)a'); + expect(s).toEqual('Tue 6am'); + }); + + it('should allow escaping of parenthesis as literal text', function() { + var mom = $.fullCalendar.moment.utc('2014-05-20T06:00:00'); + var s1 = mom.format('ddd h[(]:mm)a'); + //var s2 = mom.format('ddd h(:mm[)]a'); // we currently cant nest [] inside () + expect(s1).toEqual('Tue 6(:00)am'); + //expect(s2).toEqual('Tue 6(:00)am'); + }); + +}); diff --git a/tests/automated/moment-misc.js b/tests/automated/moment-misc.js new file mode 100644 index 0000000..d166701 --- /dev/null +++ b/tests/automated/moment-misc.js @@ -0,0 +1,67 @@ +describe('FCMoment::time', function() { + + describe('getter', function() { + + // the scenario where an ambiguously-timed moment's 00:00 time is checked + // is taken care of in moment-ambig.js + + it('should return 00:00 for a moment with 00:00 time', function() { + var mom = $.fullCalendar.moment.utc('2014-06-08T00:00:00'); + var time = mom.time(); + expect(time).toEqualDuration('00:00'); + }); + + it('should return the time of a moment with a time', function() { + var mom = $.fullCalendar.moment.utc('2014-06-08T07:30:00'); + var time = mom.time(); + expect(time).toEqualDuration('07:30'); + }); + }); + + describe('setter', function() { + + // the scenario where an ambiguously-timed moment is given a time via the setter + // is taken care of in moment-ambig.js + + describe('when setting with a Duration', function() { + + it('should give a moment with 00:00 a time', function() { + var mom = $.fullCalendar.moment.utc('2014-06-08T00:00:00'); + var dur = moment.duration('13:25'); + mom.time(dur); + expect(mom).toEqualMoment('2014-06-08T13:25:00+00:00'); + }); + + it('should overwrite the time of a moment with a time', function() { + var mom = $.fullCalendar.moment.utc('2014-06-08T05:00:00'); + var dur = moment.duration('13:25'); + mom.time(dur); + expect(mom).toEqualMoment('2014-06-08T13:25:00+00:00'); + }); + + it('should move to next day if greater than 24 hours', function() { + var mom = $.fullCalendar.moment.utc('2014-06-08T00:00:00'); + var dur = moment.duration('1.01:00:00'); // 1 day, 1 hour + mom.time(dur); + expect(mom).toEqualMoment('2014-06-09T01:00:00+00:00'); + }); + }); + + describe('when setting with another Moment', function() { + + it('should give a moment with 00:00 a time', function() { + var mom1 = $.fullCalendar.moment.utc('2014-06-09T00:00:00'); + var mom2 = $.fullCalendar.moment.utc('2014-07-22T05:30:00'); // a Tues, so .days() -> 2 + mom1.time(mom2); + expect(mom1).toEqualMoment('2014-06-09T05:30:00+00:00'); + }); + + it('should overwrite the time of a moment with a time', function() { + var mom1 = $.fullCalendar.moment.utc('2014-06-09T04:15:00'); + var mom2 = $.fullCalendar.moment.utc('2014-07-22T05:30:00'); // a Tues, so .days() -> 2 + mom1.time(mom2); + expect(mom1).toEqualMoment('2014-06-09T05:30:00+00:00'); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/moment-query.js b/tests/automated/moment-query.js new file mode 100644 index 0000000..5d659e9 --- /dev/null +++ b/tests/automated/moment-query.js @@ -0,0 +1,177 @@ + +(function() { + + var momentTypeSuffixes = { + 'ambiguously-timed': '', + 'ambiguously-zoned': 'T00:30:00', + timed: 'T00:30:00-0500' + }; + + describe('isSame', function() { + + describe('when no units provided', function() { + + it('returns false when the dates are different', function() { + var m1 = $.fullCalendar.moment.parseZone('2014-08-25T06:00:00'); + var m2 = $.fullCalendar.moment.parseZone('2014-08-25T07:00:00'); + expect(m1.isSame(m2)).toBe(false); + }); + + it('returns false when the dates are the same, but different zone-ambiguation', function() { + var m1 = $.fullCalendar.moment.parseZone('2014-08-25T06:00:00'); + var m2 = $.fullCalendar.moment.parseZone('2014-08-25T06:00:00+00:00'); + expect(m1.isSame(m2)).toBe(false); + }); + + it('returns false when the dates are the same, but different hasTime', function() { + var m1 = $.fullCalendar.moment.parseZone('2014-08-25'); + var m2 = $.fullCalendar.moment.parseZone('2014-08-25T06:00:00'); + expect(m1.isSame(m2)).toBe(false); + }); + + it('returns true when the dates are exactly the same', function() { + var m1 = $.fullCalendar.moment.parseZone('2014-08-25'); + var m2 = $.fullCalendar.moment.parseZone('2014-08-25'); + expect(m1.isSame(m2)).toBe(true); + m1 = $.fullCalendar.moment.parseZone('2014-08-25T06:00:00'); + m2 = $.fullCalendar.moment.parseZone('2014-08-25T06:00:00'); + expect(m1.isSame(m2)).toBe(true); + m1 = $.fullCalendar.moment.parseZone('2014-08-25T06:00:00+05:00'); + m2 = $.fullCalendar.moment.parseZone('2014-08-25T06:00:00+05:00'); + expect(m1.isSame(m2)).toBe(true); + }); + }); + + describe('when units are provided', function() { + + it('returns true when dates are the same day but different zone-ambiguation', function() { + var m1 = $.fullCalendar.moment.parseZone('2014-08-25T06:00:00'); + var m2 = $.fullCalendar.moment.parseZone('2014-08-25T06:00:00-11:00'); + expect(m1.isSame(m2, 'day')).toBe(true); + }); + + it('returns true when dates are the same day but different hasTime', function() { + var m1 = $.fullCalendar.moment.parseZone('2014-08-25'); + var m2 = $.fullCalendar.moment.parseZone('2014-08-25T06:00:00-11:00'); + expect(m1.isSame(m2, 'day')).toBe(true); + }); + + it('returns false when dates are a different day', function() { + var m1 = $.fullCalendar.moment.parseZone('2014-08-24T00:00:00'); + var m2 = $.fullCalendar.moment.parseZone('2014-08-25T06:00:00'); + expect(m1.isSame(m2, 'day')).toBe(false); + }); + }); + }); + + describe('isWithin', function() { + $.each(momentTypeSuffixes, function(thisType, thisSuffix) { + describe('when the moment is ' + thisType, function() { + $.each(momentTypeSuffixes, function(otherType, otherSuffix) { + describe('and other moments are ' + otherType, function() { + + it('is clearly within', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-15' + thisSuffix); + var other1 = $.fullCalendar.moment.parseZone('2014-06-01' + otherSuffix); + var other2 = $.fullCalendar.moment.parseZone('2014-07-01' + otherSuffix); + var res = mom.isWithin(other1, other2); + expect(res).toBe(true); + }); + + it('is clearly not within', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-15' + thisSuffix); + var other1 = $.fullCalendar.moment.parseZone('2014-09-01' + otherSuffix); + var other2 = $.fullCalendar.moment.parseZone('2014-10-01' + otherSuffix); + var res = mom.isWithin(other1, other2); + expect(res).toBe(false); + }); + + it('is within when equal to start', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-15' + thisSuffix); + var other1 = $.fullCalendar.moment.parseZone('2014-06-15' + otherSuffix); + var other2 = $.fullCalendar.moment.parseZone('2014-07-01' + otherSuffix); + var res = mom.isWithin(other1, other2); + expect(res).toBe(true); + }); + + it('is not within when equal to end', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-15' + thisSuffix); + var other1 = $.fullCalendar.moment.parseZone('2014-06-01' + otherSuffix); + var other2 = $.fullCalendar.moment.parseZone('2014-06-15' + otherSuffix); + var res = mom.isWithin(other1, other2); + expect(res).toBe(false); + }); + + }); + }); + }); + }); + }); + + describe('isBefore', function() { + $.each(momentTypeSuffixes, function(thisType, thisSuffix) { + describe('when the moment is ' + thisType, function() { + $.each(momentTypeSuffixes, function(otherType, otherSuffix) { + describe('and other moment is ' + otherType, function() { + + it('is clearly before', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-15' + thisSuffix); + var other = $.fullCalendar.moment.parseZone('2015-01-01' + otherSuffix); + var res = mom.isBefore(other); + expect(res).toBe(true); + }); + + it('is clearly not before', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-15' + thisSuffix); + var other = $.fullCalendar.moment.parseZone('2013-01-01' + otherSuffix); + var res = mom.isBefore(other); + expect(res).toBe(false); + }); + + it('is equal', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-15' + thisSuffix); + var other = $.fullCalendar.moment.parseZone('2014-06-15' + otherSuffix); + var res = mom.isBefore(other); + expect(res).toBe(false); + }); + + }); + }); + }); + }); + }); + + describe('isAfter', function() { + $.each(momentTypeSuffixes, function(thisType, thisSuffix) { + describe('when the moment is ' + thisType, function() { + $.each(momentTypeSuffixes, function(otherType, otherSuffix) { + describe('and other moment is ' + otherType, function() { + + it('is clearly after', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-15' + thisSuffix); + var other = $.fullCalendar.moment.parseZone('2013-01-01' + otherSuffix); + var res = mom.isAfter(other); + expect(res).toBe(true); + }); + + it('is clearly not after', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-15' + thisSuffix); + var other = $.fullCalendar.moment.parseZone('2015-01-01' + otherSuffix); + var res = mom.isAfter(other); + expect(res).toBe(false); + }); + + it('is equal', function() { + var mom = $.fullCalendar.moment.parseZone('2014-06-15' + thisSuffix); + var other = $.fullCalendar.moment.parseZone('2014-06-15' + otherSuffix); + var res = mom.isAfter(other); + expect(res).toBe(false); + }); + + }); + }); + }); + }); + }); + +})(); diff --git a/tests/automated/monthNames.js b/tests/automated/monthNames.js new file mode 100644 index 0000000..99ec619 --- /dev/null +++ b/tests/automated/monthNames.js @@ -0,0 +1,103 @@ +describe('month name', function() { + var settings = {}; + var referenceDate = '2014-01-01'; // The day the world is hung-over + var languages = [ 'es', 'fr', 'de', 'zh-cn', 'nl' ]; + + beforeEach(function() { + affix('#cal'); + settings = { + defaultDate: referenceDate + }; + }); + + afterEach(function() { + moment.lang('en'); // reset moment's global language + }); + + [ 'month', 'agendaDay', 'basicDay' ].forEach(function(viewClass, index, viewClasses) { + describe('when view is ' + viewClass, function() { + beforeEach(function() { + settings.defaultView = viewClass; + }); + + describe('when lang is default', function() { + beforeEach(function() { + settings.lang = 'en'; + moment.lang('en'); + }); + + moment.months().forEach(function(month, index, months) { + it('should be ' + month, function(done) { + settings.defaultDate = $.fullCalendar.moment(referenceDate).add(index, 'months'); + settings.eventAfterAllRender = function() { + expect($('.fc-toolbar h2')).toContainText(month); + done(); + }; + + $('#cal').fullCalendar(settings); + }); + }); + }); + + languages.forEach(function(language, index, languages) { + describe('when lang is ' + language, function() { + beforeEach(function() { + settings.lang = language; + moment.lang(language); + }); + + moment.months().forEach(function(month, index, months) { // `month` will always be English + it('should be the translated name for ' + month, function(done) { + var langMonths = moment.months(); + var langMonth = langMonths[index]; + + settings.defaultDate = $.fullCalendar.moment(referenceDate).add(index, 'months'); + settings.eventAfterAllRender = function() { + if (viewClass == 'month') { // with month view check for occurence of the monthname in the title + expect($('.fc-toolbar h2')).toContainText(langMonth); + } + else { // with day views ensure that title contains the properly formatted phrase + expect($('.fc-toolbar h2')).toHaveText(settings.defaultDate.format('LL')); + } + done(); + }; + + $('#cal').fullCalendar(settings); + }); + }); + }); + }); + + describe('when names are specified', function() { + var months = [ + 'I', + 'II', + 'III', + 'IV', + 'V', + 'VI', + 'VII', + 'IIX', + 'IX', + 'X', + 'XI', + 'XII' + ]; + + months.forEach(function(month, index, months) { // `month` is our custom month name + it('should be the translated name for ' + month, function(done) { + settings.defaultDate = $.fullCalendar.moment(referenceDate).add(index, 'months'); + settings.monthNames = months; + settings.eventAfterAllRender = function() { + expect($('.fc-toolbar h2')).toContainText(month); + done(); + }; + + $('#cal').fullCalendar(settings); + }); + }); + }); + }); + }); + +}); diff --git a/tests/automated/monthNamesShort.js b/tests/automated/monthNamesShort.js new file mode 100644 index 0000000..533227a --- /dev/null +++ b/tests/automated/monthNamesShort.js @@ -0,0 +1,98 @@ +describe('short month name', function() { + var settings = {}; + var referenceDate = '2014-01-01'; // The day the world is hung-over + var languages = [ 'es', 'fr', 'de', 'zh-cn', 'nl' ]; + + beforeEach(function() { + affix('#cal'); + settings = { + defaultDate: referenceDate + }; + }); + + afterEach(function() { + moment.lang('en'); // reset moment's global language + }); + + [ 'agendaWeek', 'basicWeek' ].forEach(function(viewClass, index, viewClasses) { + describe('when view is ' + viewClass, function() { + beforeEach(function() { + settings.defaultView = viewClass; + }); + + describe('when lang is default', function() { + beforeEach(function() { + settings.lang = 'en'; + moment.lang('en'); + }); + + moment.monthsShort().forEach(function(monthShort, index) { + it('should be ' + monthShort, function(done) { + settings.defaultDate = $.fullCalendar.moment(referenceDate).add(index, 'months'); + settings.eventAfterAllRender = function() { + expect($('.fc-toolbar h2')).toContainText(monthShort); + done(); + }; + + $('#cal').fullCalendar(settings); + }); + }); + }); + + languages.forEach(function(language, index, languages) { + describe('when lang is ' + language, function() { + beforeEach(function() { + settings.lang = language; + moment.lang(language); + }); + + moment.monthsShort().forEach(function(monthShort, index) { // `monthShort` will always be English + it('should be the translated name for ' + monthShort, function(done) { + var langMonthsShort = moment.monthsShort(); + var langMonthShort = langMonthsShort[index]; + + settings.defaultDate = $.fullCalendar.moment(referenceDate).add(index, 'months'); + settings.eventAfterAllRender = function() { + expect($('.fc-toolbar h2')).toContainText(langMonthShort); + done(); + }; + + $('#cal').fullCalendar(settings); + }); + }); + }); + }); + + describe('when names are specified', function() { + var monthsShort = [ + 'I', + 'II', + 'III', + 'IV', + 'V', + 'VI', + 'VII', + 'IIX', + 'IX', + 'X', + 'XI', + 'XII' + ]; + + monthsShort.forEach(function(monthShort, index) { // `monthShort` will be our custom month name + it('should be the translated name for ' + monthShort, function(done) { + settings.defaultDate = $.fullCalendar.moment(referenceDate).add(index, 'months'); + settings.monthNamesShort = monthsShort; + settings.eventAfterAllRender = function() { + expect($('.fc-toolbar h2')).toContainText(monthShort); + done(); + }; + + $('#cal').fullCalendar(settings); + }); + }); + }); + }); + }); + +}); diff --git a/tests/automated/nextDayThreshold.js b/tests/automated/nextDayThreshold.js new file mode 100644 index 0000000..c934d4c --- /dev/null +++ b/tests/automated/nextDayThreshold.js @@ -0,0 +1,70 @@ + +describe('nextDayThreshold', function() { + + // when a view object exposes its nextDayThreshold value (after some refactoring)... + // TODO: detect the default of 9am + // TODO: detect 2 or more different types of Duration-ish parsing + + beforeEach(function() { + affix('#cal'); + }); + + it('renders an event before the threshold', function() { + $('#cal').fullCalendar({ + nextDayThreshold: '10:00:00', + defaultDate: '2014-06', + defaultView: 'month', + events: [ + { + title: 'event1', + start: '2014-06-08T22:00:00', + end: '2014-06-10T09:00:00' + } + ] + }); + expect(renderedDayCount()).toBe(2); + }); + + it('renders an event equal to the threshold', function() { + $('#cal').fullCalendar({ + nextDayThreshold: '10:00:00', + defaultDate: '2014-06', + defaultView: 'month', + events: [ + { + title: 'event1', + start: '2014-06-08T22:00:00', + end: '2014-06-10T10:00:00' + } + ] + }); + expect(renderedDayCount()).toBe(3); + }); + + it('renders an event after the threshold', function() { + $('#cal').fullCalendar({ + nextDayThreshold: '10:00:00', + defaultDate: '2014-06', + defaultView: 'month', + events: [ + { + title: 'event1', + start: '2014-06-08T22:00:00', + end: '2014-06-10T11:00:00' + } + ] + }); + expect(renderedDayCount()).toBe(3); + }); + + + function renderedDayCount() { // assumes only one event on the calendar + var cellWidth = $('.fc-sun').outerWidth(); // works with basic and agenda + var totalWidth = 0; + $('.fc-event').each(function() { + totalWidth += $(this).outerWidth(); + }); + return Math.round(totalWidth / cellWidth); + } + +}); \ No newline at end of file diff --git a/tests/automated/now.js b/tests/automated/now.js new file mode 100644 index 0000000..2ab1106 --- /dev/null +++ b/tests/automated/now.js @@ -0,0 +1,59 @@ +describe('now', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-05-01' + }; + }); + + describe('when month view', function() { + beforeEach(function() { + options.defaultView = 'month'; + }); + it('changes the highlighted day when customized', function() { + options.now = '2014-05-06'; + $('#cal').fullCalendar(options); + var todayCell = $('#cal td.fc-today'); + var todayDate = todayCell.data('date'); + expect(todayDate).toEqual('2014-05-06'); + }); + }); + + describe('when agendaWeek view', function() { + beforeEach(function() { + options.defaultView = 'agendaWeek'; + }); + it('changes the highlighted day when customized', function() { + options.now = '2014-04-29T12:00:00'; + $('#cal').fullCalendar(options); + var todayCell = $('#cal td.fc-today'); + expect(todayCell.data('date')).toBe('2014-04-29'); + }); + }); + + it('accepts a function that returns a moment', function() { + options.defaultView = 'month'; + options.now = function() { + return moment.utc('2014-05-01'); + }; + $('#cal').fullCalendar(options); + var todayCell = $('#cal td.fc-today'); + var todayDate = todayCell.data('date'); + expect(todayDate).toEqual('2014-05-01'); + }); + + it('accepts a function that returns a moment-ish string', function() { + options.defaultView = 'month'; + options.now = function() { + return '2014-05-01'; + }; + $('#cal').fullCalendar(options); + var todayCell = $('#cal td.fc-today'); + var todayDate = todayCell.data('date'); + expect(todayDate).toEqual('2014-05-01'); + }); + +}); \ No newline at end of file diff --git a/tests/automated/removeEventSource.js b/tests/automated/removeEventSource.js new file mode 100644 index 0000000..db6a86f --- /dev/null +++ b/tests/automated/removeEventSource.js @@ -0,0 +1,111 @@ +describe('removeEventSource', function() { + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-08-01' + }; + $.mockjax({ + url: '*', + contentType: 'text/json', + responseText: buildEventArray() + }); + $.mockjaxSettings.log = function() { }; // don't console.log + }); + + afterEach(function() { + $.mockjaxClear(); + }); + + describe('with a URL', function() { + testInput('/myscript.php'); // will go to mockjax + }); + + describe('with an array', function() { + testInput(buildEventArray()); + }); + + describe('with a function', function() { + testInput(function(start, end, timezone, callback) { + callback(buildEventArray()); + }); + }); + + describe('with an object+url', function() { + testInput({ + url: '/myscript.php' // will go to mockjax + }); + }); + + describe('with an object+array', function() { + testInput({ + events: buildEventArray() + }); + }); + + describe('with an object+function', function() { + testInput({ + events: function(start, end, timezone, callback) { + callback(buildEventArray()); + } + }); + }); + + function testInput(eventInput) { + + it('correctly removes events provided via `events` at initialization', function(done) { + var callCnt = 0; + options.eventAfterAllRender = function() { + if (!(callCnt++)) { // only the first time + expectEventCnt(2); + $('#cal').fullCalendar('removeEventSource', eventInput); + expectEventCnt(0); + done(); + } + }; + options.events = eventInput; + $('#cal').fullCalendar(options); + }); + + it('correctly removes events provided via `eventSources` at initialization', function(done) { + var callCnt = 0; + options.eventAfterAllRender = function() { + if (!(callCnt++)) { // only the first time + expectEventCnt(2); + $('#cal').fullCalendar('removeEventSource', eventInput); + expectEventCnt(0); + done(); + } + }; + options.eventSources = [ eventInput ]; + $('#cal').fullCalendar(options); + }); + + it('correctly removes events provided via `addEventSource` method', function(done) { + var callCnt = 0; + options.eventAfterAllRender = function() { + if ((callCnt++) === 1) { // the second time (the first time is upon initial render) + expectEventCnt(2); + $('#cal').fullCalendar('removeEventSource', eventInput); + expectEventCnt(0); + done(); + } + }; + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('addEventSource', eventInput); + }); + } + + function buildEventArray() { + return [ + { title: 'event1', start: '2014-08-01' }, + { title: 'event2', start: '2014-08-02' } + ]; + } + + function expectEventCnt(cnt) { + expect($('.fc-event').length).toBe(cnt); + expect($('#cal').fullCalendar('clientEvents').length).toBe(cnt); + } +}); \ No newline at end of file diff --git a/tests/automated/removeEvents.js b/tests/automated/removeEvents.js new file mode 100644 index 0000000..fca6172 --- /dev/null +++ b/tests/automated/removeEvents.js @@ -0,0 +1,150 @@ +describe('removeEvents', function() { + + var options; + var eventArray = [ + { id: 0, title: 'event zero', start: '2014-06-24', className: 'event-zero' }, + { id: 1, title: 'event one', start: '2014-06-24', className: 'event-non-zero event-one' }, + { id: 2, title: 'event two', start: '2014-06-24', className: 'event-non-zero event-two' } + ]; + var eventFunc = function(start, end, timezone, callback) { + callback(eventArray); + }; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-06-24', + defaultView: 'month' + }; + }); + + $.each({ + 'with an array of events': eventArray, + 'with an event function': eventFunc + }, function(description, events) { + + describe(description, function() { + + it('can remove all events if no args specified', function(done) { + go( + events, + function() { + $('#cal').fullCalendar('removeEvents'); + }, + function() { + expect($('#cal').fullCalendar('clientEvents').length).toEqual(0); + expect($('.fc-event').length).toEqual(0); + }, + done + ); + }); + + it('can remove events with a numeric ID', function(done) { + go( + events, + function() { + $('#cal').fullCalendar('removeEvents', 1); + }, + function() { + expect($('#cal').fullCalendar('clientEvents').length).toEqual(2); + expect($('.fc-event').length).toEqual(2); + expect($('.event-zero').length).toEqual(1); + expect($('.event-two').length).toEqual(1); + }, + done + ); + }); + + it('can remove events with a string ID', function(done) { + go( + events, + function() { + $('#cal').fullCalendar('removeEvents', '1'); + }, + function() { + expect($('#cal').fullCalendar('clientEvents').length).toEqual(2); + expect($('.fc-event').length).toEqual(2); + expect($('.event-zero').length).toEqual(1); + expect($('.event-two').length).toEqual(1); + }, + done + ); + }); + + it('can remove events with a filter function', function(done) { + go( + events, + function() { + $('#cal').fullCalendar('removeEvents', function(event) { + return $.inArray('event-one', event.className) !== -1; + }); + }, + function() { + expect($('#cal').fullCalendar('clientEvents').length).toEqual(2); + expect($('.fc-event').length).toEqual(2); + expect($('.event-zero').length).toEqual(1); + expect($('.event-two').length).toEqual(1); + }, + done + ); + }); + + it('can remove an event with ID 0', function(done) { // for issue 2082 + go( + events, + function() { + $('#cal').fullCalendar('removeEvents', 0); + }, + function() { + expect($('#cal').fullCalendar('clientEvents').length).toEqual(2); + expect($('.fc-event').length).toEqual(2); + expect($('.event-zero').length).toEqual(0); + expect($('.event-non-zero').length).toEqual(2); + }, + done + ); + }); + }); + }); + + + // Verifies the actions in removeFunc executed correctly by calling checkFunc. + function go(events, removeFunc, checkFunc, doneFunc) { + var called = false; + options.events = events; + options.eventAfterAllRender = function() { + if (!called) { // don't execute on subsequent removeEvents/next/prev + called = true; + + checkAllEvents(); // make sure all events initially rendered correctly + removeFunc(); // remove the events + checkFunc(); // check correctness + + // move the calendar back out of view, then back in + $('#cal').fullCalendar('next'); + $('#cal').fullCalendar('prev'); + + // array event sources should maintain the same state + // whereas "dynamic" event sources should refetch and reset the state + if ($.isArray(events)) { + checkFunc(); // for issue 2187 + } + else { + checkAllEvents(); + } + + doneFunc(); + } + }; + $('#cal').fullCalendar(options); + } + + + // Checks to make sure all events have been rendered and that the calendar + // has internal info on all the events. + function checkAllEvents() { + expect($('#cal').fullCalendar('clientEvents').length).toEqual(3); + expect($('.fc-event').length).toEqual(3); + } + +}); \ No newline at end of file diff --git a/tests/automated/scrollTime.js b/tests/automated/scrollTime.js new file mode 100644 index 0000000..586a203 --- /dev/null +++ b/tests/automated/scrollTime.js @@ -0,0 +1,40 @@ +describe('scrollTime', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultView: 'agendaWeek' + }; + }); + + it('accepts a string Duration', function() { + options.scrollTime = '02:00:00'; + options.height = 400; // short enough to make scrolling happen + $('#cal').fullCalendar(options); + var slotCell = $('.fc-slats tr:eq(4)'); // 2am slot + var slotTop = slotCell.position().top; + var scrollContainer = $('.fc-time-grid-container'); + var scrollTop = scrollContainer.scrollTop(); + var diff = Math.abs(slotTop - scrollTop); + expect(slotTop).toBeGreaterThan(0); + expect(scrollTop).toBeGreaterThan(0); + expect(diff).toBeLessThan(3); + }); + + it('accepts a Duration object', function() { + options.scrollTime = { hours: 2 }; + options.height = 400; // short enough to make scrolling happen + $('#cal').fullCalendar(options); + var slotCell = $('.fc-slats tr:eq(4)'); // 2am slot + var slotTop = slotCell.position().top; + var scrollContainer = $('.fc-time-grid-container'); + var scrollTop = scrollContainer.scrollTop(); + var diff = Math.abs(slotTop - scrollTop); + expect(slotTop).toBeGreaterThan(0); + expect(scrollTop).toBeGreaterThan(0); + expect(diff).toBeLessThan(3); + }); + +}); \ No newline at end of file diff --git a/tests/automated/select-callback.js b/tests/automated/select-callback.js new file mode 100644 index 0000000..da430f5 --- /dev/null +++ b/tests/automated/select-callback.js @@ -0,0 +1,185 @@ +describe('select callback', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-05-25', + selectable: true + }; + }); + + afterEach(function() { + $('#cal').fullCalendar('destroy'); + }); + + [ false, true ].forEach(function(isRTL) { + describe('when isRTL is ' + isRTL, function() { + beforeEach(function() { + options.isRTL = isRTL; + }); + describe('when in month view', function() { + beforeEach(function() { + options.defaultView = 'month'; + }); + it('gets fired correctly when the user selects cells', function(done) { + options.select = function(start, end, jsEvent, view) { + expect(moment.isMoment(start)).toEqual(true); + expect(moment.isMoment(end)).toEqual(true); + expect(typeof jsEvent).toEqual('object'); // TODO: more descrimination + expect(typeof view).toEqual('object'); // " + expect(start.hasTime()).toEqual(false); + expect(end.hasTime()).toEqual(false); + expect(start).toEqualMoment('2014-04-28'); + expect(end).toEqualMoment('2014-05-07'); + }; + spyOn(options, 'select').and.callThrough(); + $('#cal').fullCalendar(options); + $('.fc-day[data-date="2014-04-28"]').simulate('drag-n-drop', { + dropTarget: '.fc-day[data-date="2014-05-06"]', + callback: function() { + expect(options.select).toHaveBeenCalled(); + done(); + } + }); + }); + it('gets fired correctly when the user selects just one cell', function(done) { + options.select = function(start, end, jsEvent, view) { + expect(moment.isMoment(start)).toEqual(true); + expect(moment.isMoment(end)).toEqual(true); + expect(typeof jsEvent).toEqual('object'); // TODO: more descrimination + expect(typeof view).toEqual('object'); // " + expect(start.hasTime()).toEqual(false); + expect(end.hasTime()).toEqual(false); + expect(start).toEqualMoment('2014-04-28'); + expect(end).toEqualMoment('2014-04-29'); + }; + spyOn(options, 'select').and.callThrough(); + $('#cal').fullCalendar(options); + $('.fc-day[data-date="2014-04-28"]').simulate('drag-n-drop', { + dropTarget: '.fc-day[data-date="2014-04-28"]', + callback: function() { + expect(options.select).toHaveBeenCalled(); + done(); + } + }); + }); + }); + + describe('when in agendaWeek view', function() { + beforeEach(function() { + options.defaultView = 'agendaWeek'; + }); + describe('when selecting all-day slots', function() { + it('gets fired correctly when the user selects cells', function(done) { + options.select = function(start, end, jsEvent, view) { + expect(moment.isMoment(start)).toEqual(true); + expect(moment.isMoment(end)).toEqual(true); + expect(typeof jsEvent).toEqual('object'); // TODO: more descrimination + expect(typeof view).toEqual('object'); // " + expect(start.hasTime()).toEqual(false); + expect(end.hasTime()).toEqual(false); + expect(start).toEqualMoment('2014-05-28'); + expect(end).toEqualMoment('2014-05-30'); + }; + spyOn(options, 'select').and.callThrough(); + $('#cal').fullCalendar(options); + $('.fc-agenda-view .fc-day-grid .fc-day:eq(3)').simulate('drag-n-drop', { // will be 2014-05-28 for LTR and RTL + dx: $('.fc-sun').outerWidth() * (isRTL ? -1 : 1), // the width of one column + callback: function() { + expect(options.select).toHaveBeenCalled(); + done(); + } + }); + }); + it('gets fired correctly when the user selects a single cell', function(done) { + options.select = function(start, end, jsEvent, view) { + expect(moment.isMoment(start)).toEqual(true); + expect(moment.isMoment(end)).toEqual(true); + expect(typeof jsEvent).toEqual('object'); // TODO: more descrimination + expect(typeof view).toEqual('object'); // " + expect(start.hasTime()).toEqual(false); + expect(end.hasTime()).toEqual(false); + expect(start).toEqualMoment('2014-05-28'); + expect(end).toEqualMoment('2014-05-29'); + }; + spyOn(options, 'select').and.callThrough(); + $('#cal').fullCalendar(options); + $('.fc-agenda-view .fc-day-grid .fc-day:eq(3)').simulate('drag-n-drop', { // will be 2014-05-28 for LTR and RTL + callback: function() { + expect(options.select).toHaveBeenCalled(); + done(); + } + }); + }); + }); + describe('when selecting timed slots', function(done) { + it('gets fired correctly when the user selects slots', function(done) { + options.select = function(start, end, jsEvent, view) { + expect(moment.isMoment(start)).toEqual(true); + expect(moment.isMoment(end)).toEqual(true); + expect(typeof jsEvent).toEqual('object'); // TODO: more descrimination + expect(typeof view).toEqual('object'); // " + expect(start.hasTime()).toEqual(true); + expect(end.hasTime()).toEqual(true); + expect(start).toEqualMoment('2014-05-28T09:00:00'); + expect(end).toEqualMoment('2014-05-28T10:30:00'); + }; + spyOn(options, 'select').and.callThrough(); + $('#cal').fullCalendar(options); + $('.fc-slats tr:eq(18) td:not(.fc-time)').simulate('drag-n-drop', { // middle will be 2014-05-28T09:00:00 + dy: $('.fc-slats tr:eq(18)').outerHeight() * 2, // move down two slots + callback: function() { + expect(options.select).toHaveBeenCalled(); + done(); + } + }); + }); + it('gets fired correctly when the user selects slots in a different day', function(done) { + options.select = function(start, end, jsEvent, view) { + expect(moment.isMoment(start)).toEqual(true); + expect(moment.isMoment(end)).toEqual(true); + expect(typeof jsEvent).toEqual('object'); // TODO: more descrimination + expect(typeof view).toEqual('object'); // " + expect(start.hasTime()).toEqual(true); + expect(end.hasTime()).toEqual(true); + expect(start).toEqualMoment('2014-05-28T09:00:00'); + expect(end).toEqualMoment('2014-05-29T10:30:00'); + }; + spyOn(options, 'select').and.callThrough(); + $('#cal').fullCalendar(options); + $('.fc-slats tr:eq(18) td:not(.fc-time)').simulate('drag-n-drop', { // middle will be 2014-05-28T09:00:00 + dx: $('.fc-day-header:first').outerWidth() * .9 * (isRTL ? -1 : 1), // one day ahead + dy: $('.fc-slats tr:eq(18)').outerHeight() * 2, // move down two slots + callback: function() { + expect(options.select).toHaveBeenCalled(); + done(); + } + }); + }); + it('gets fired correctly when the user selects a single slot', function(done) { + options.select = function(start, end, jsEvent, view) { + expect(moment.isMoment(start)).toEqual(true); + expect(moment.isMoment(end)).toEqual(true); + expect(typeof jsEvent).toEqual('object'); // TODO: more descrimination + expect(typeof view).toEqual('object'); // " + expect(start.hasTime()).toEqual(true); + expect(end.hasTime()).toEqual(true); + expect(start).toEqualMoment('2014-05-28T09:00:00'); + expect(end).toEqualMoment('2014-05-28T09:30:00'); + }; + spyOn(options, 'select').and.callThrough(); + $('#cal').fullCalendar(options); + $('.fc-slats tr:eq(18) td:not(.fc-time)').simulate('drag-n-drop', { // middle will be 2014-05-28T09:00:00 + callback: function() { + expect(options.select).toHaveBeenCalled(); + done(); + } + }); + }); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/select-method.js b/tests/automated/select-method.js new file mode 100644 index 0000000..63299d4 --- /dev/null +++ b/tests/automated/select-method.js @@ -0,0 +1,198 @@ +describe('select method', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-05-25', + selectable: true + }; + }); + + afterEach(function() { + $('#cal').fullCalendar('destroy'); + }); + + /* + THINGS TO IMPLEMENT IN SRC (in addition to notes further down): + - better date normalization (for both render and reporting to select callback) + - if second date is the same or before the first + - if given a mixture of timed/all-day + - for basic/month views, when given timed dates, should really be all-day + */ + + [ false, true ].forEach(function(isRTL) { + describe('when isRTL is ' + isRTL, function() { + beforeEach(function() { + options.isRTL = isRTL; + }); + describe('when in month view', function() { + beforeEach(function() { + options.defaultView = 'month'; + }); + describe('when called with all-day moments', function() { + describe('when in bounds', function() { + it('renders a selection', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2014-05-07', '2014-05-09'); + expect($('.fc-highlight')).toBeVisible(); + }); + it('renders a selection when called with one argument', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2014-05-07'); + expect($('.fc-highlight')).toBeVisible(); + }); + it('fires a selection event', function() { + options.select = function(start, end) { + expect(start.hasTime()).toEqual(false); + expect(end.hasTime()).toEqual(false); + expect(start).toEqualMoment('2014-05-07'); + expect(end).toEqualMoment('2014-05-09'); + }; + spyOn(options, 'select').and.callThrough(); + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2014-05-07', '2014-05-09'); + expect(options.select).toHaveBeenCalled(); + }); + }); + describe('when out of bounds', function() { + it('doesn\'t render a selection', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2015-05-07', '2015-05-09'); + expect($('.fc-highlight')).not.toBeVisible(); + }); + /* + TODO: implement this behavior + it('doesn\'t fire a selection event', function() { + options.select = function(start, end) { + expect(start).toEqualMoment('2014-05-07'); + expect(end).toEqualMoment('2014-05-09'); + }; + spyOn(options, 'select').and.callThrough(); + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2015-05-07', '2015-05-09'); + expect(options.select).not.toHaveBeenCalled(); + }); + */ + }); + }); + describe('when called with timed moments', function() { + it('renders a selection', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2014-05-07T06:00:00', '2014-05-09T07:00:00'); + expect($('.fc-highlight')).toBeVisible(); + }); + it('fires a selection event', function() { + options.select = function(start, end) { + expect(start.hasTime()).toEqual(true); + expect(end.hasTime()).toEqual(true); + expect(start).toEqualMoment('2014-05-07T06:00:00'); + expect(end).toEqualMoment('2014-05-09T06:00:00'); + }; + spyOn(options, 'select').and.callThrough(); + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2014-05-07T06:00:00', '2014-05-09T06:00:00'); + expect(options.select).toHaveBeenCalled(); + }); + }); + }); + describe('when in agendaWeek view', function() { // May 25 - 31 + beforeEach(function() { + options.defaultView = 'agendaWeek'; + options.scrollTime = '01:00:00'; // so that most events will be below the divider + options.height = 400; // short enought to make scrolling happen + }); + describe('when called with timed moments', function() { + describe('when in bounds', function() { + it('renders a selection when called with one argument', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2014-05-26T06:00:00'); + expect($('.fc-highlight')).toBeVisible(); + }); + it('renders a selection over the slot area', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2014-05-26T06:00:00', '2014-05-26T08:00:00'); + expect($('.fc-highlight')).toBeVisible(); + var slotAreaTop = $('.fc-time-grid-container').offset().top; + var overlayTop = $('.fc-highlight').offset().top; + expect(overlayTop).toBeGreaterThan(slotAreaTop); + }); + }); + describe('when out of bounds', function() { + it('doesn\'t render a selection', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2015-05-26T06:00:00', '2015-05-26T07:00:00'); + expect($('.fc-highlight')).not.toBeVisible(); + }); + /* + TODO: implement this behavior + it('doesn\'t fire a selection event', function() { + options.select = function(start, end) { + expect(start).toEqualMoment('2015-05-07T06:00:00'); + expect(end).toEqualMoment('2015-05-09T07:00:00'); + }; + spyOn(options, 'select').and.callThrough(); + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2015-05-07T06:00:00', '2015-05-09T07:00:00'); + expect(options.select).not.toHaveBeenCalled(); + }); + */ + }); + }); + describe('when called with all-day moments', function() { // forget about in/out bounds for this :) + describe('when allDaySlot is on', function() { + beforeEach(function() { + options.allDaySlot = true; + }); + it('renders a selection over the day area', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2014-05-26', '2014-05-28'); + expect($('.fc-highlight')).toBeVisible(); + var slotAreaTop = $('.fc-time-grid-container').offset().top; + var overlayTop = $('.fc-highlight').offset().top; + expect(overlayTop).toBeLessThan(slotAreaTop); + }); + it('fires a selection event', function() { + options.select = function(start, end) { + expect(start.hasTime()).toEqual(false); + expect(end.hasTime()).toEqual(false); + expect(start).toEqualMoment('2014-05-26'); + expect(end).toEqualMoment('2014-05-28'); + }; + spyOn(options, 'select').and.callThrough(); + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2014-05-26', '2014-05-28'); + expect(options.select).toHaveBeenCalled(); + }); + }); + describe('when allDaySlot is off', function() { + beforeEach(function() { + options.allDaySlot = false; + }); + it('doesn\'t render', function() { + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2014-05-26', '2014-05-28'); + expect($('.fc-highlight')).not.toBeVisible(); + }); + /* + TODO: implement + it('doesn\'t fire a selection event', function() { + options.select = function(start, end) { + expect(start.hasTime()).toEqual(false); + expect(end.hasTime()).toEqual(false); + expect(start).toEqualMoment('2014-05-26'); + expect(end).toEqualMoment('2014-05-28'); + }; + spyOn(options, 'select').and.callThrough(); + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2014-05-26', '2014-05-28'); + expect(options.select).not.toHaveBeenCalled(); + }); + */ + }); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/selectHelper.js b/tests/automated/selectHelper.js new file mode 100644 index 0000000..045948e --- /dev/null +++ b/tests/automated/selectHelper.js @@ -0,0 +1,22 @@ +describe('selectHelper', function() { + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-08-03', + defaultView: 'agendaWeek', + scrollTime: '00:00:00', + selectHelper: true + }; + }); + + it('goes through eventRender', function() { + options.eventRender = function(event, element, view) { + element.addClass('didEventRender'); + }; + $('#cal').fullCalendar(options); + $('#cal').fullCalendar('select', '2014-08-04T01:00:00', '2014-08-04T04:00:00'); + expect($('.fc-helper')).toHaveClass('didEventRender'); + }); +}); \ No newline at end of file diff --git a/tests/automated/slotDuration.js b/tests/automated/slotDuration.js new file mode 100644 index 0000000..d7cd87e --- /dev/null +++ b/tests/automated/slotDuration.js @@ -0,0 +1,96 @@ + +describe('slotDuration', function() { + + var minutesInADay = 1440; + + beforeEach(function() { + affix('#cal'); + }); + + describe('when using the default settings', function() { + describe('in agendaWeek', function() { + it('should have slots 1440/30 slots', function() { + var options = { + defaultView: 'agendaWeek' + }; + $('#cal').fullCalendar(options); + var slotCount = $('.fc-slats tr').length; + expect(slotCount).toEqual(Math.ceil(minutesInADay / 30)); + }); + }); + describe('in agendaDay', function() { + it('should have slots 1440/30 slots', function() { + var options = { + defaultView: 'agendaDay' + }; + $('#cal').fullCalendar(options); + var slotCount = $('.fc-slats tr').length; + expect(slotCount).toEqual(Math.ceil(minutesInADay / 30)); + }); + }); + }); + + describe('when slotMinutes is set to 30', function() { + describe('in agendaWeek', function() { + it('should have slots 1440/30 slots', function() { + var options = { + defaultView: 'agendaWeek' + }; + $('#cal').fullCalendar(options); + var slotCount = $('.fc-slats tr').length; + expect(slotCount).toEqual(Math.ceil(minutesInADay / 30)); + }); + }); + describe('in agendaDay', function() { + it('should have slots 1440/30 slots', function() { + var options = { + defaultView: 'agendaDay' + }; + $('#cal').fullCalendar(options); + var slotCount = $('.fc-slats tr').length; + expect(slotCount).toEqual(Math.ceil(minutesInADay / 30)); + }); + }); + }); + + describe('when slotMinutes is set to a series of times', function() { + + var slotMinutesList = [ 10, 12, 15, 17, 20, 30, 35, 45, 60, 62, 120, 300 ]; + + describe('in agendaWeek', function() { + beforeEach(function() { + affix('#cal2'); + }); + slotMinutesList.forEach(function(slotMinutes) { + it('should have slots 1440/x slots', function() { + var options = { + defaultView: 'agendaWeek', + slotDuration: { minutes: slotMinutes } + }; + $('#cal2').fullCalendar(options); + var slotCount = $('.fc-slats tr').length; + var expected = Math.ceil(minutesInADay / slotMinutes); + expect(slotCount).toEqual(expected); + }); + }); + }); + + describe('in agendaDay', function() { + beforeEach(function() { + affix('#cal2'); + }); + slotMinutesList.forEach(function(slotMinutes) { + it('should have slots 1440/x slots', function() { + var options = { + defaultView: 'agendaWeek', + slotDuration: { minutes: slotMinutes } + }; + $('#cal2').fullCalendar(options); + var slotCount = $('.fc-slats tr').length; + var expected = Math.ceil(minutesInADay / slotMinutes); + expect(slotCount).toEqual(expected); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/template.js.txt b/tests/automated/template.js.txt new file mode 100644 index 0000000..01ec3e0 --- /dev/null +++ b/tests/automated/template.js.txt @@ -0,0 +1,27 @@ +ddescribe('', function() { + + var options; + + beforeEach(function() { + //affix('#cal'); + $('body').append('
'); + options = { + defaultDate: '' + }; + }); + + afterEach(function() { + //$('#cal').fullCalendar('destroy'); + }); + + describe('', function() { + beforeEach(function() { + options.defaultView = ''; + }); + + iit('', function() { + $('#cal').fullCalendar(options); + }); + }); + +}); \ No newline at end of file diff --git a/tests/automated/themeButtonIcons.js b/tests/automated/themeButtonIcons.js new file mode 100644 index 0000000..11c279a --- /dev/null +++ b/tests/automated/themeButtonIcons.js @@ -0,0 +1,65 @@ +describe('themeButtonIcons', function() { + + var options; + var defaultSelectors = [ + '.ui-icon-circle-triangle-w', + '.ui-icon-circle-triangle-e', + '.ui-icon-seek-prev', + '.ui-icon-seek-next' + ]; + + beforeEach(function() { + affix('#cal'); + options = { + header: { + left: 'prevYear,prev,next,nextYear today', + center: 'title', + right: 'month,agendaWeek,agendaDay' + } + }; + }); + + describe('when theme is off', function() { + beforeEach(function() { + options.theme = false; + }); + it('should not have any of the default theme icon classes', function() { + $('#cal').fullCalendar(options); + defaultSelectors.forEach(function(selector) { + expect($(selector)).not.toBeInDOM(); + }); + }); + }); + + describe('when theme is on', function() { + + beforeEach(function() { + options.theme = true; + }); + + it('should have all of the deafult theme icon classes', function() { + $('#cal').fullCalendar(options); + defaultSelectors.forEach(function(selector) { + expect($(selector)).toBeInDOM(); + }); + }); + + it('should accept values that override the individual defaults', function() { + options.themeButtonIcons = { + prev: 'arrowthickstop-1-w', + next: 'arrowthickstop-1-e' + }; + $('#cal').fullCalendar(options); + [ + '.ui-icon-arrowthickstop-1-w', + '.ui-icon-arrowthickstop-1-e', + '.ui-icon-seek-prev', // prev/next year should remain + '.ui-icon-seek-next' // + ] + .forEach(function(selector) { + expect($(selector)).toBeInDOM(); + }); + }); + }); + +}); \ No newline at end of file diff --git a/tests/automated/timeFormat.js b/tests/automated/timeFormat.js new file mode 100644 index 0000000..bca652d --- /dev/null +++ b/tests/automated/timeFormat.js @@ -0,0 +1,68 @@ +describe('timeFormat', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-06-04', + events: [ { + title: 'my event', + start: '2014-06-04T15:00:00', + end: '2014-06-04T17:00:00' + } ] + }; + }); + + function getRenderedEventTime() { + return $('.fc-event:first .fc-time').text(); + } + + describe('when in month view', function() { + + beforeEach(function() { + options.defaultView = 'month'; + }); + + it('renders correctly when default', function() { + $('#cal').fullCalendar(options); + expect(getRenderedEventTime()).toBe('3p'); + }); + + it('renders correctly when default and the language is customized', function() { + options.lang = 'en-gb'; + $('#cal').fullCalendar(options); + expect(getRenderedEventTime()).toBe('15'); + }); + + it('renders correctly when customized', function() { + options.timeFormat = 'Hh:mm:mm'; + $('#cal').fullCalendar(options); + expect(getRenderedEventTime()).toBe('153:00:00'); + }); + }); + + describe('when in agendaWeek view', function() { + + beforeEach(function() { + options.defaultView = 'agendaWeek'; + }); + + it('renders correctly when default', function() { + $('#cal').fullCalendar(options); + expect(getRenderedEventTime()).toBe('3:00 - 5:00'); + }); + + it('renders correctly when default and the language is customized', function() { + options.lang = 'en-gb'; + $('#cal').fullCalendar(options); + expect(getRenderedEventTime()).toBe('15:00 - 17:00'); + }); + + it('renders correctly when customized', function() { + options.timeFormat = 'Hh:mm:mm'; + $('#cal').fullCalendar(options); + expect(getRenderedEventTime()).toBe('153:00:00 - 175:00:00'); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/timezone.js b/tests/automated/timezone.js new file mode 100644 index 0000000..0860151 --- /dev/null +++ b/tests/automated/timezone.js @@ -0,0 +1,113 @@ + +describe('timezone', function() { + + // NOTE: Only deals with the processing of *received* events. + // Verification of a correct AJAX *request* is done in events-json-feed.js + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + defaultView: 'month', + defaultDate: '2014-05-01', + events: [ + { + id: '1', + title: 'all day event', + start: '2014-05-02' + }, + { + id: '2', + title: 'timed event', + start: '2014-05-10T12:00:00' + }, + { + id: '3', + title: 'timed and zoned event', + start: '2014-05-10T14:00:00+11:00' + } + ] + }; + }); + + it('receives events correctly when no timezone', function(done) { + options.eventAfterAllRender = function() { + var allDayEvent = $('#cal').fullCalendar('clientEvents', '1')[0]; + var timedEvent = $('#cal').fullCalendar('clientEvents', '2')[0]; + var zonedEvent = $('#cal').fullCalendar('clientEvents', '3')[0]; + expect(allDayEvent.start.hasZone()).toEqual(false); + expect(allDayEvent.start.hasTime()).toEqual(false); + expect(allDayEvent.start.format()).toEqual('2014-05-02'); + expect(timedEvent.start.hasZone()).toEqual(false); + expect(timedEvent.start.hasTime()).toEqual(true); + expect(timedEvent.start.format()).toEqual('2014-05-10T12:00:00'); + expect(zonedEvent.start.hasZone()).toEqual(true); + expect(zonedEvent.start.hasTime()).toEqual(true); + expect(zonedEvent.start.format()).toEqual('2014-05-10T14:00:00+11:00'); + done(); + }; + $('#cal').fullCalendar(options); + }); + + it('receives events correctly when local timezone', function(done) { + options.timezone = 'local'; + options.eventAfterAllRender = function() { + var allDayEvent = $('#cal').fullCalendar('clientEvents', '1')[0]; + var timedEvent = $('#cal').fullCalendar('clientEvents', '2')[0]; + var zonedEvent = $('#cal').fullCalendar('clientEvents', '3')[0]; + expect(allDayEvent.start.hasZone()).toEqual(false); + expect(allDayEvent.start.hasTime()).toEqual(false); + expect(allDayEvent.start.format()).toEqual('2014-05-02'); + expect(timedEvent.start.hasZone()).toEqual(true); + expect(timedEvent.start.hasTime()).toEqual(true); + expect(timedEvent.start.zone()).toEqual(new Date(2014, 4, 10, 12).getTimezoneOffset()); + expect(zonedEvent.start.hasZone()).toEqual(true); + expect(zonedEvent.start.hasTime()).toEqual(true); + expect(zonedEvent.start.zone()).toEqual(new Date('Sat May 10 2014 14:00:00 GMT+1100').getTimezoneOffset()); + done(); + }; + $('#cal').fullCalendar(options); + }); + + it('receives events correctly when UTC timezone', function(done) { + options.timezone = 'UTC'; + options.eventAfterAllRender = function() { + var allDayEvent = $('#cal').fullCalendar('clientEvents', '1')[0]; + var timedEvent = $('#cal').fullCalendar('clientEvents', '2')[0]; + var zonedEvent = $('#cal').fullCalendar('clientEvents', '3')[0]; + expect(allDayEvent.start.hasZone()).toEqual(false); + expect(allDayEvent.start.hasTime()).toEqual(false); + expect(allDayEvent.start.format()).toEqual('2014-05-02'); + expect(timedEvent.start.hasZone()).toEqual(true); + expect(timedEvent.start.hasTime()).toEqual(true); + expect(timedEvent.start.format()).toEqual('2014-05-10T12:00:00+00:00'); + expect(zonedEvent.start.hasZone()).toEqual(true); + expect(zonedEvent.start.hasTime()).toEqual(true); + expect(zonedEvent.start.format()).toEqual('2014-05-10T03:00:00+00:00'); + done(); + }; + $('#cal').fullCalendar(options); + }); + + it('receives events correctly when custom timezone', function(done) { + options.timezone = 'America/Chicago'; + options.eventAfterAllRender = function() { + var allDayEvent = $('#cal').fullCalendar('clientEvents', '1')[0]; + var timedEvent = $('#cal').fullCalendar('clientEvents', '2')[0]; + var zonedEvent = $('#cal').fullCalendar('clientEvents', '3')[0]; + expect(allDayEvent.start.hasZone()).toEqual(false); + expect(allDayEvent.start.hasTime()).toEqual(false); + expect(allDayEvent.start.format()).toEqual('2014-05-02'); + expect(timedEvent.start.hasZone()).toEqual(false); + expect(timedEvent.start.hasTime()).toEqual(true); + expect(timedEvent.start.format()).toEqual('2014-05-10T12:00:00'); + expect(zonedEvent.start.hasZone()).toEqual(true); + expect(zonedEvent.start.hasTime()).toEqual(true); + expect(zonedEvent.start.format()).toEqual('2014-05-10T14:00:00+11:00'); + done(); + }; + $('#cal').fullCalendar(options); + }); + +}); diff --git a/tests/automated/titleFormat.js b/tests/automated/titleFormat.js new file mode 100644 index 0000000..3b687db --- /dev/null +++ b/tests/automated/titleFormat.js @@ -0,0 +1,97 @@ +describe('titleFormat', function() { + + var SELECTOR = '.fc-toolbar h2'; + + beforeEach(function() { + affix('#cal'); + }); + + describe('when default', function() { + + var viewWithFormat = [ + { view: 'month', expected: 'June 2014' }, + { view: 'basicWeek', expected: /Jun 8 - 14,? 2014/ }, // moment changed LL defaults after 2.8 + { view: 'agendaWeek', expected: /Jun 8 - 14,? 2014/ }, // " + { view: 'basicDay', expected: /June 12,? 2014/ }, // " + { view: 'agendaDay', expected: /June 12,? 2014/ } // " + ]; + + beforeEach(function() { + $('#cal').fullCalendar({ + defaultDate: '2014-06-12' + }); + }); + + it('should have default values', function() { + var cal = $('#cal'); + + for (var i = 0; i < viewWithFormat.length; i++) { + var crtView = viewWithFormat[i]; + cal.fullCalendar('changeView', crtView.view); + expect(cal.find(SELECTOR).text()).toMatch(crtView.expected); + }; + }); + }); + + describe('when set on a per-view basis', function() { + + var viewWithFormat = [ + { view: 'month', expected: '2014, June' }, + { view: 'basicWeek', expected: '8 - 14 6 2014' }, + { view: 'agendaWeek', expected: '8 - 14, 6, 2014' }, + { view: 'basicDay', expected: 'Thursday June 12 2014' }, + { view: 'agendaDay', expected: 'Thursday, June, 12, 2014' } + ]; + + beforeEach(function() { + $('#cal').fullCalendar({ + defaultDate: '2014-06-12', + titleFormat: { + month: 'YYYY, MMMM', + basicWeek: 'D M YYYY', + agendaWeek: 'D, M, YYYY', + basicDay: 'dddd MMMM D YYYY', + agendaDay: 'dddd, MMMM, D, YYYY' + } + }); + }); + + it('should have the correct values', function() { + var cal = $('#cal'); + + for (var i = 0; i < viewWithFormat.length; i++) { + var crtView = viewWithFormat[i]; + cal.fullCalendar('changeView', crtView.view); + expect(cal.find(SELECTOR).text()).toBe(crtView.expected); + }; + }); + }); + + describe('when default and language is French', function() { + + var viewWithFormat = [ + { view: 'month', expected: 'juin 2014' }, + { view: 'basicWeek', expected: '9 - 15 juin 2014' }, + { view: 'agendaWeek', expected: '9 - 15 juin 2014' }, + { view: 'basicDay', expected: '12 juin 2014' }, + { view: 'agendaDay', expected: '12 juin 2014' } + ]; + + beforeEach(function() { + $('#cal').fullCalendar({ + defaultDate: '2014-06-12', + lang: 'fr' + }); + }); + + it('should have the translated dates', function() { + var cal = $('#cal'); + + for (var i = 0; i < viewWithFormat.length; i++) { + var crtView = viewWithFormat[i]; + cal.fullCalendar('changeView', crtView.view); + expect(cal.find(SELECTOR).text()).toBe(crtView.expected); + }; + }); + }); +}); diff --git a/tests/automated/updateEvent.js b/tests/automated/updateEvent.js new file mode 100644 index 0000000..146b761 --- /dev/null +++ b/tests/automated/updateEvent.js @@ -0,0 +1,485 @@ + +describe('updateEvent', function() { + + var options; + var event; + var relatedEvent; + + beforeEach(function() { + affix('#cal'); + options = { + defaultDate: '2014-05-01', + defaultView: 'month' + }; + event = null; + relatedEvent = null; + }); + + function init() { + $('#cal').fullCalendar(options); + var events = $('#cal').fullCalendar('clientEvents'); + event = events[0]; + relatedEvent = events[1]; + } + + describe('when moving an all-day event\'s start', function() { + describe('when a related event doesn\'t have an end', function() { + it('should move the start by the delta and leave the end as null', function() { + options.events = [ + { id: '1', start: '2014-05-01', allDay: true }, + { id: '1', start: '2014-05-10', allDay: true } + ]; + init(); + event.start.add(2, 'days'); + $('#cal').fullCalendar('updateEvent', event); + expect(event.start).toEqualMoment('2014-05-03'); + expect(event.end).toBeNull(); + expect(relatedEvent.start).toEqualMoment('2014-05-12'); + expect(relatedEvent.end).toBeNull(); + }); + }); + describe('when a related event has an end', function() { + it('should move the start and end by the delta', function() { + options.events = [ + { id: '1', start: '2014-05-01', allDay: true }, + { id: '1', start: '2014-05-10', end: '2014-05-12', allDay: true } + ]; + init(); + event.start.add(2, 'days'); + expect(event.start).toEqualMoment('2014-05-03'); + expect(event.end).toBeNull(); + $('#cal').fullCalendar('updateEvent', event); + expect(relatedEvent.start).toEqualMoment('2014-05-12'); + expect(relatedEvent.end).toEqualMoment('2014-05-14'); + }); + }); + }); + + describe('when moving an timed event\'s start', function() { + describe('when a related event doesn\'t have an end', function() { + it('should move the start by the delta and leave the end as null', function() { + options.events = [ + { id: '1', start: '2014-05-01T12:00:00', allDay: false }, + { id: '1', start: '2014-05-10T06:00:00', allDay: false } + ]; + init(); + event.start.add({ days: 2, hours: 2 }); + $('#cal').fullCalendar('updateEvent', event); + expect(event.start).toEqualMoment('2014-05-03T14:00:00'); + expect(event.end).toBeNull(); + expect(relatedEvent.start).toEqualMoment('2014-05-12T08:00:00'); + expect(relatedEvent.end).toBeNull(); + }); + }); + describe('when a related event has an end', function() { + it('should move the start and end by the delta', function() { + options.events = [ + { id: '1', start: '2014-05-01T12:00:00', allDay: false }, + { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-12T08:00:00', allDay: false } + ]; + init(); + event.start.add({ days: 2, hours: 2 }); + $('#cal').fullCalendar('updateEvent', event); + expect(event.start).toEqualMoment('2014-05-03T14:00:00'); + expect(event.end).toBeNull(); + expect(relatedEvent.start).toEqualMoment('2014-05-12T08:00:00'); + expect(relatedEvent.end).toEqualMoment('2014-05-14T10:00:00'); + }); + }); + }); + + describe('when moving an all-day event\'s end', function() { + describe('when a related event doesn\'t have an end', function() { + it('should give the end a default duration plus the delta', function() { + options.defaultAllDayEventDuration = { days: 2 }; + options.events = [ + { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true }, + { id: '1', start: '2014-05-10', allDay: true } + ]; + init(); + event.end.add(1, 'days'); + $('#cal').fullCalendar('updateEvent', event); + expect(event.start).toEqualMoment('2014-05-01'); + expect(event.end).toEqualMoment('2014-05-04'); + expect(relatedEvent.start).toEqualMoment('2014-05-10'); + expect(relatedEvent.end).toEqualMoment('2014-05-13'); + }); + }); + describe('when a related event has an end', function() { + it('should move the end by the delta', function() { + options.events = [ + { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true }, + { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true } + ]; + init(); + event.end.add(1, 'days'); + $('#cal').fullCalendar('updateEvent', event); + expect(event.start).toEqualMoment('2014-05-01'); + expect(event.end).toEqualMoment('2014-05-04'); + expect(relatedEvent.start).toEqualMoment('2014-05-10'); + expect(relatedEvent.end).toEqualMoment('2014-05-14'); + }); + }); + }); + + describe('when moving a timed event\'s end', function() { + describe('when a related event doesn\'t have an end', function() { + describe('when forceEventDuration is off', function() { + it('should give the end a default duration plus the delta', function() { + options.forceEventDuration = false; + options.defaultTimedEventDuration = { hours: 2 }; + options.events = [ + { id: '1', start: '2014-05-01T12:00:00', end: '2014-05-01T15:00:00', allDay: false }, + { id: '1', start: '2014-05-10T16:00:00', allDay: false } + ]; + init(); + event.end.add({ days: 1, hours: 1 }); + $('#cal').fullCalendar('updateEvent', event); + expect(event.start).toEqualMoment('2014-05-01T12:00:00'); + expect(event.end).toEqualMoment('2014-05-02T16:00:00'); + expect(relatedEvent.start).toEqualMoment('2014-05-10T16:00:00'); + expect(relatedEvent.end).toEqualMoment('2014-05-11T19:00:00'); + }); + }); + describe('when forceEventDuration is on', function() { + it('should give the end a default duration plus the delta', function() { + options.forceEventDuration = true; + options.defaultTimedEventDuration = { hours: 2 }; + options.events = [ + { id: '1', start: '2014-05-01T12:00:00', end: '2014-05-01T15:00:00', allDay: false }, + { id: '1', start: '2014-05-10T16:00:00', allDay: false } + ]; + init(); + event.end.add({ days: 1, hours: 1 }); + relatedEvent.end = null; + $('#cal').fullCalendar('updateEvent', event); + expect(event.start).toEqualMoment('2014-05-01T12:00:00'); + expect(event.end).toEqualMoment('2014-05-02T16:00:00'); + expect(relatedEvent.start).toEqualMoment('2014-05-10T16:00:00'); + expect(relatedEvent.end).toEqualMoment('2014-05-11T19:00:00'); + }); + }); + }); + describe('when a related event has an end', function() { + it('should move the end by the delta', function() { + options.events = [ + { id: '1', start: '2014-05-01T12:00:00', end: '2014-05-01T14:00:00', allDay: false }, + { id: '1', start: '2014-05-10T16:00:00', end: '2014-05-10T19:00:00', allDay: false } + ]; + init(); + event.end.add({ days: 1, hours: 1 }); + $('#cal').fullCalendar('updateEvent', event); + expect(event.start).toEqualMoment('2014-05-01T12:00:00'); + expect(event.end).toEqualMoment('2014-05-02T15:00:00'); + expect(relatedEvent.start).toEqualMoment('2014-05-10T16:00:00'); + expect(relatedEvent.end).toEqualMoment('2014-05-11T20:00:00'); + }); + }); + }); + + describe('when moving an all-day event\'s start and end', function() { + it('should move the start and end of related events', function() { + options.events = [ + { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true }, + { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true } + ]; + init(); + event.start.add(2, 'days'); + event.end.add(3, 'day'); + $('#cal').fullCalendar('updateEvent', event); + expect(event.start).toEqualMoment('2014-05-03'); + expect(event.end).toEqualMoment('2014-05-06'); + expect(relatedEvent.start).toEqualMoment('2014-05-12'); + expect(relatedEvent.end).toEqualMoment('2014-05-16'); + }); + }); + + describe('when moving a timed event\'s start and end', function() { + it('should move the start and end of related events', function() { + options.events = [ + { id: '1', start: '2014-05-01T06:00:00', end: '2014-05-03T06:00:00', allDay: false }, + { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-13T06:00:00', allDay: false } + ]; + init(); + event.start.add({ days: 2, hours: 1 }); + event.end.add({ days: 3, hours: 2 }); + $('#cal').fullCalendar('updateEvent', event); + expect(event.start).toEqualMoment('2014-05-03T07:00:00'); + expect(event.end).toEqualMoment('2014-05-06T08:00:00'); + expect(relatedEvent.start).toEqualMoment('2014-05-12T07:00:00'); + expect(relatedEvent.end).toEqualMoment('2014-05-16T08:00:00'); + }); + }); + + describe('when giving a time to an all-day event\'s start', function() { + it('should make the event and related events timed', function() { + options.events = [ + { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true }, + { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true } + ]; + init(); + event.start.time('18:00'); + $('#cal').fullCalendar('updateEvent', event); + expect(event.allDay).toEqual(false); + expect(event.start).toEqualMoment('2014-05-01T18:00:00'); + expect(event.end).toBeNull(); + expect(relatedEvent.allDay).toEqual(false); + expect(relatedEvent.start).toEqualMoment('2014-05-10T18:00:00'); + expect(relatedEvent.end).toBeNull(); + }); + }); + + describe('when changing an event from all-day to timed', function() { + describe('when the event\'s dates remain all-day', function() { + it('should make the event and related events allDay=false and 00:00', function() { + options.events = [ + { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true }, + { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true } + ]; + init(); + event.allDay = false; + $('#cal').fullCalendar('updateEvent', event); + expect(event.allDay).toEqual(false); + expect(event.start).toEqualMoment('2014-05-01T00:00:00'); + expect(event.end).toBeNull(); + expect(relatedEvent.allDay).toEqual(false); + expect(relatedEvent.start).toEqualMoment('2014-05-10T00:00:00'); + expect(relatedEvent.end).toBeNull(); + }); + }); + describe('when the event\'s dates are set to a time', function() { + it('should adjust the event and related event\'s allDay/start/end', function() { + options.events = [ + { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true }, + { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true } + ]; + init(); + event.allDay = false; + event.start.time('14:00'); + $('#cal').fullCalendar('updateEvent', event); + expect(event.allDay).toEqual(false); + expect(event.start).toEqualMoment('2014-05-01T14:00:00'); + expect(event.end).toBeNull(); + expect(relatedEvent.allDay).toEqual(false); + expect(relatedEvent.start).toEqualMoment('2014-05-10T14:00:00'); + expect(relatedEvent.end).toBeNull(); + }); + }); + describe('when the event\'s start is also moved', function() { + it('should adjust the event and related event\'s allDay/start/end', function() { + options.events = [ + { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true }, + { id: '1', start: '2014-05-10', end: '2014-05-13', allDay: true } + ]; + init(); + event.allDay = false; + event.start.add(1, 'days'); + $('#cal').fullCalendar('updateEvent', event); + expect(event.allDay).toEqual(false); + expect(event.start).toEqualMoment('2014-05-02T00:00:00'); + expect(event.end).toBeNull(); + expect(relatedEvent.allDay).toEqual(false); + expect(relatedEvent.start).toEqualMoment('2014-05-11T00:00:00'); + expect(relatedEvent.end).toBeNull(); + }); + }); + }); + + describe('when changing an event from timed to all-day', function() { + it('should adjust the event and related event\'s allDay/start/end', function() { + options.events = [ + { id: '1', start: '2014-05-01T06:00:00', end: '2014-05-03T06:00:00', allDay: false }, + { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-13T06:00:00', allDay: false } + ]; + init(); + event.allDay = true; + $('#cal').fullCalendar('updateEvent', event); + expect(event.allDay).toEqual(true); + expect(event.start).toEqualMoment('2014-05-01'); + expect(event.end).toBeNull(); + expect(relatedEvent.allDay).toEqual(true); + expect(relatedEvent.start).toEqualMoment('2014-05-10'); + expect(relatedEvent.end).toBeNull(); + }); + it('should adjust the event and related event\'s allDay/start/end and account for a new start', function() { + options.events = [ + { id: '1', start: '2014-05-01T06:00:00', end: '2014-05-03T06:00:00', allDay: false }, + { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-13T06:00:00', allDay: false } + ]; + init(); + event.allDay = true; + event.start.add(1, 'days'); + $('#cal').fullCalendar('updateEvent', event); + expect(event.allDay).toEqual(true); + expect(event.start).toEqualMoment('2014-05-02'); + expect(event.end).toBeNull(); + expect(relatedEvent.allDay).toEqual(true); + expect(relatedEvent.start).toEqualMoment('2014-05-11'); + expect(relatedEvent.end).toBeNull(); + }); + }); + + it('should accept moments that have unnormalized start/end', function() { + options.events = [ + { id: '1', start: '2014-05-01T06:00:00', end: '2014-05-03T06:00:00', allDay: false }, + { id: '1', start: '2014-05-10T06:00:00', end: '2014-05-13T06:00:00', allDay: false } + ]; + init(); + event.start = '2014-05-02T06:00:00'; // move by 1 day + event.end = '2014-05-05T06:00:00'; // increase duration by 1 day + $('#cal').fullCalendar('updateEvent', event); + expect(event.allDay).toEqual(false); + expect(moment.isMoment(event.start)).toEqual(true); + expect(event.start).toEqualMoment('2014-05-02T06:00:00'); + expect(moment.isMoment(event.end)).toEqual(true); + expect(event.end).toEqualMoment('2014-05-05T06:00:00'); + expect(relatedEvent.allDay).toEqual(false); + expect(moment.isMoment(relatedEvent.start)).toEqual(true); + expect(relatedEvent.start).toEqualMoment('2014-05-11T06:00:00'); + expect(moment.isMoment(relatedEvent.end)).toEqual(true); + expect(relatedEvent.end).toEqualMoment('2014-05-15T06:00:00'); + }); + + function whenMovingStart(should) { + describe('when moving an timed event\'s start', function() { + beforeEach(function() { + options.events = [ + { id: '1', start: '2014-05-01T06:00:00+05:00', end: '2014-05-03T06:00:00+05:00', allDay: false }, + { id: '1', start: '2014-05-11T06:00:00+05:00', end: '2014-05-13T06:00:00+05:00', allDay: false } + ]; + init(); + event.start.add(2, 'hours'); + $('#cal').fullCalendar('updateEvent', event); + }); + should(); + }); + } + + function whenMovingEnd(should) { + describe('when moving a timed event\'s end', function() { + beforeEach(function() { + options.events = [ + { id: '1', start: '2014-05-01T06:00:00+05:00', end: '2014-05-03T06:00:00+05:00', allDay: false }, + { id: '1', start: '2014-05-11T06:00:00+05:00', end: '2014-05-13T06:00:00+05:00', allDay: false } + ]; + init(); + event.end.add(2, 'hours'); + $('#cal').fullCalendar('updateEvent', event); + }); + should(); + }); + } + + function whenMovingToTimed(should) { + describe('when moving an all-day event to timed', function() { + beforeEach(function() { + options.events = [ + { id: '1', start: '2014-05-01', end: '2014-05-03', allDay: true }, + { id: '1', start: '2014-05-11', end: '2014-05-13', allDay: true } + ]; + init(); + event.allDay = false; + $('#cal').fullCalendar('updateEvent', event); + }); + should(); + }); + } + + function whenReportingUnchanged(should) { // not used right now + describe('when reporting an event that hasn\'t changed', function() { + beforeEach(function() { + options.events = [ + { id: '1', start: '2014-05-01T06:00:00+05:00', end: '2014-05-03T06:00:00+05:00', allDay: false }, + { id: '1', start: '2014-05-11T06:00:00+05:00', end: '2014-05-13T06:00:00+05:00', allDay: false } + ]; + init(); + $('#cal').fullCalendar('updateEvent', event); + }); + should(); + }); + } + + function shouldBeAmbiguouslyZoned() { + it('should make the events ambiguously zoned', function() { + expect(event.start.hasZone()).toEqual(false); + if (event.end) { + expect(event.end.hasZone()).toEqual(false); + } + expect(relatedEvent.start.hasZone()).toEqual(false); + if (relatedEvent.end) { + expect(relatedEvent.end.hasZone()).toEqual(false); + } + }); + } + + function shouldBeLocal() { + it('should make the events local', function() { + expect(event.start.hasZone()).toEqual(true); + expect(event.start._isUTC).toEqual(false); + if (event.end) { + expect(event.end.hasZone()).toEqual(true); + expect(event.end._isUTC).toEqual(false); + } + expect(relatedEvent.start.hasZone()).toEqual(true); + expect(relatedEvent.start._isUTC).toEqual(false); + if (relatedEvent.end) { + expect(relatedEvent.end.hasZone()).toEqual(true); + expect(relatedEvent.end._isUTC).toEqual(false); + } + }); + } + + function shouldBeUTC() { + it('should make the events UTC', function() { + expect(event.start.hasZone()).toEqual(true); + expect(event.start._isUTC).toEqual(true); + if (event.end) { + expect(event.end.hasZone()).toEqual(true); + expect(event.end._isUTC).toEqual(true); + } + expect(event.start.hasZone()).toEqual(true); + expect(event.start._isUTC).toEqual(true); + if (relatedEvent.end) { + expect(relatedEvent.end.hasZone()).toEqual(true); + expect(relatedEvent.end._isUTC).toEqual(true); + } + }); + } + + describe('when calendar has no timezone', function() { + beforeEach(function() { + options.timezone = false; + }); + whenMovingStart(shouldBeAmbiguouslyZoned); + whenMovingEnd(shouldBeAmbiguouslyZoned); + whenMovingToTimed(shouldBeAmbiguouslyZoned); + }); + + describe('when calendar has a local timezone', function() { + beforeEach(function() { + options.timezone = 'local'; + }); + whenMovingStart(shouldBeLocal); + whenMovingEnd(shouldBeLocal); + whenMovingToTimed(shouldBeLocal); + }); + + describe('when calendar has a UTC timezone', function() { + beforeEach(function() { + options.timezone = 'UTC'; + }); + whenMovingStart(shouldBeUTC); + whenMovingEnd(shouldBeUTC); + whenMovingToTimed(shouldBeUTC); + }); + + describe('when calendar has a custom timezone', function() { + beforeEach(function() { + options.timezone = 'America/Chicago'; + }); + whenMovingStart(shouldBeAmbiguouslyZoned); + whenMovingEnd(shouldBeAmbiguouslyZoned); + whenMovingToTimed(shouldBeAmbiguouslyZoned); + }); + +}); \ No newline at end of file diff --git a/tests/automated/weekMode.js b/tests/automated/weekMode.js new file mode 100644 index 0000000..c6a755b --- /dev/null +++ b/tests/automated/weekMode.js @@ -0,0 +1,169 @@ + +describe('weekMode', function() { + + beforeEach(function() { + affix('#cal'); + }); + + describe('when weekMode is default', function() { + beforeEach(function() { + $('#cal').fullCalendar(); + }); + it('should show 6 weeks for a 5 week month', function() { + $('#cal').fullCalendar('gotoDate', '2013-10-01'); + var weekCount = $('.fc-week').length; + expect(weekCount).toEqual(6); + }); + it('should show 6 weeks for a 4 week month', function() { + $('#cal').fullCalendar('gotoDate', '2009-03-01'); + var weekCount = $('.fc-week').length; + expect(weekCount).toEqual(6); + }); + it('should show 6 weeks for a 6 week month', function() { + $('#cal').fullCalendar('gotoDate', '2013-06-01'); + var weekCount = $('.fc-week').length; + expect(weekCount).toEqual(6); + }); + it('should not change height whether 4,5 or weeks', function() { + $('#cal').fullCalendar('gotoDate', '2009-02-01'); + var fourWeekHeight = $('.fc-week:first').outerHeight(); + $('#cal').fullCalendar('gotoDate', '2013-10-01'); + var fiveWeekHeight = $('.fc-week:first').outerHeight(); + $('#cal').fullCalendar('gotoDate', '2013-06-01'); + var sixWeekHeight = $('.fc-week:first').outerHeight(); + expect(fourWeekHeight).toBeGreaterThan(0); + expect(fiveWeekHeight).toBeGreaterThan(0); + expect(sixWeekHeight).toBeGreaterThan(0); + expect(fourWeekHeight).toEqual(fiveWeekHeight); + expect(fiveWeekHeight).toEqual(sixWeekHeight); + }); + }); + + describe('when weekMode is set to fixed', function() { + beforeEach(function() { + $('#cal').fullCalendar({ + weekMode: 'fixed' + }); + }); + it('should show 6 weeks for a 5 week month', function() { + $('#cal').fullCalendar('gotoDate', '2013-10-01'); + var weekCount = $('.fc-week').length; + expect(weekCount).toEqual(6); + }); + it('should show 6 weeks for a 4 week month', function() { + $('#cal').fullCalendar('gotoDate', '2009-03-01'); + var weekCount = $('.fc-week').length; + expect(weekCount).toEqual(6); + }); + it('should show 6 weeks for a 6 week month', function() { + $('#cal').fullCalendar('gotoDate', '2013-06-01'); + var weekCount = $('.fc-week').length; + expect(weekCount).toEqual(6); + }); + }); + + describe('when weekMode is set to liquid', function() { + beforeEach(function() { + $('#cal').fullCalendar({ + weekMode: 'liquid' + }); + }); + it('should show 5 weeks for a 5 week month', function() { + $('#cal').fullCalendar('gotoDate', '2013-10-01'); + var weekCount = $('.fc-week').length; + expect(weekCount).toEqual(5); + }); + it('should show 4 weeks for a 4 week month', function() { + $('#cal').fullCalendar('gotoDate', '2009-02-01'); + var weekCount = $('.fc-week').length; + expect(weekCount).toEqual(4); + }); + it('should show 6 weeks for a 6 week month', function() { + $('#cal').fullCalendar('gotoDate', '2013-06-01'); + var weekCount = $('.fc-week').length; + expect(weekCount).toEqual(6); + }); + it('should increase height when moving from 6 week to 5 weeks', function() { + $('#cal').fullCalendar('gotoDate', '2013-10-01'); + var fiveWeekHeight = $('.fc-week:first').outerHeight(); + $('#cal').fullCalendar('gotoDate', '2013-06-01'); + var sixWeekHeight = $('.fc-week:first').outerHeight(); + expect(fiveWeekHeight).toBeGreaterThan(sixWeekHeight); + }); + it('should reduce height when moving from 5 weeks to 6 weeks', function() { + $('#cal').fullCalendar('gotoDate', '2013-06-01'); + var sixWeekHeight = $('.fc-week:first').outerHeight(); + $('#cal').fullCalendar('gotoDate', '2013-10-01'); + var fiveWeekHeight = $('.fc-week:first').outerHeight(); + expect(fiveWeekHeight).toBeGreaterThan(sixWeekHeight); + }); + it('should increase height when moving from 5 weeks to 4 weeks', function() { + $('#cal').fullCalendar('gotoDate', '2013-05-01'); + var fiveWeekHeight = $('.fc-week:first').outerHeight(); + $('#cal').fullCalendar('gotoDate', '2009-02-01'); + var fourWeekHeight = $('.fc-week:first').outerHeight(); + expect(fourWeekHeight).toBeGreaterThan(fiveWeekHeight); + }); + it('should reduce height when moving from 4 weeks to 5 weeks', function() { + $('#cal').fullCalendar('gotoDate', '2009-02-01'); + var fourWeekHeight = $('.fc-week:first').outerHeight(); + $('#cal').fullCalendar('gotoDate', '2013-05-01'); + var fiveWeekHeight = $('.fc-week:first').outerHeight(); + expect(fourWeekHeight).toBeGreaterThan(fiveWeekHeight); + }); + }); + + it('should not display an empty week when no visible days and weekMode is set to liquid', function() { + $('#cal').fullCalendar({ + defaultDate: '2013-06-01', // June 2013 has first day as Saturday, and last as Sunday! + weekMode: 'liquid', + weekends: false + }); + expect($('.fc-week').length).toBe(4); + }); + + describe('when weekMode is set to variable', function() { + beforeEach(function() { + $('#cal').fullCalendar({ + weekMode: 'variable' + }); + }); + it('should show 5 weeks for a 5 week month', function() { + $('#cal').fullCalendar('gotoDate', '2013-10-01'); + var weekCount = $('.fc-week').length; + expect(weekCount).toEqual(5); + }); + it('should show 4 weeks for a 4 week month', function() { + $('#cal').fullCalendar('gotoDate', '2009-02-01'); + var weekCount = $('.fc-week').length; + expect(weekCount).toEqual(4); + }); + it('should show 6 weeks for a 6 week month', function() { + $('#cal').fullCalendar('gotoDate', '2013-06-01'); + var weekCount = $('.fc-week').length; + expect(weekCount).toEqual(6); + }); + it('should not change height whether 4,5 or weeks', function() { + $('#cal').fullCalendar('gotoDate', '2009-02-01'); + var fourWeekHeight = $('.fc-week:first').outerHeight(); + $('#cal').fullCalendar('gotoDate', '2013-10-01'); + var fiveWeekHeight = $('.fc-week:first').outerHeight(); + $('#cal').fullCalendar('gotoDate', '2013-06-01'); + var sixWeekHeight = $('.fc-week:first').outerHeight(); + expect(fourWeekHeight).toBeGreaterThan(0); + expect(fiveWeekHeight).toBeGreaterThan(0); + expect(sixWeekHeight).toBeGreaterThan(0); + expect(fourWeekHeight).toEqual(fiveWeekHeight); + expect(fiveWeekHeight).toEqual(sixWeekHeight); + }); + }); + + it('should not display an empty week when no visible days and weekMode is set to variable', function() { + $('#cal').fullCalendar({ + defaultDate: '2013-06-01', // June 2013 has first day as Saturday, and last as Sunday! + weekMode: 'variable', + weekends: false + }); + expect($('.fc-week').length).toBe(4); + }); +}); \ No newline at end of file diff --git a/tests/automated/weekNumberCalculation.js b/tests/automated/weekNumberCalculation.js new file mode 100644 index 0000000..274364f --- /dev/null +++ b/tests/automated/weekNumberCalculation.js @@ -0,0 +1,68 @@ + +describe('weekNumberCalculation', function() { + + var options; + + beforeEach(function() { + options = { + weekNumbers: true + }; + }); + + function getRenderedWeekNumber() { + // works for both kinds of views + var text = $('.fc-agenda-view .fc-week-number, .fc-week:first .fc-content-skeleton .fc-week-number').text(); + return parseInt(text.replace(/\D/g, ''), 10); + } + + beforeEach(function() { + affix('#cal'); + }); + + [ 'basicDay', 'agendaDay' ].forEach(function(viewType) { + describe('when in ' + viewType + ' view', function() { + + beforeEach(function() { + options.defaultView = viewType; + }); + + it('should display the American standard when using \'local\'', function() { + options.defaultDate = '2013-11-23'; // a Saturday + options.weekNumberCalculation = 'local'; + $('#cal').fullCalendar(options); + expect(getRenderedWeekNumber()).toBe(47); + }); + + it('should display a language-specific local week number', function() { + options.defaultDate = '2013-11-23'; // a Saturday + options.lang = 'ar'; + options.weekNumberCalculation = 'local'; + $('#cal').fullCalendar(options); + expect(getRenderedWeekNumber()).toBe(48); + }); + + // another local test, but to make sure it is different from ISO + it('should display the American standard when using \'local\'', function() { + options.defaultDate = '2013-11-17'; // a Sunday + options.weekNumberCalculation = 'local'; + $('#cal').fullCalendar(options); + expect(getRenderedWeekNumber()).toBe(47); + }); + + it('should display ISO standard when using \'ISO\'', function() { + options.defaultDate = '2013-11-17'; // a Sunday + options.weekNumberCalculation = 'ISO'; + $('#cal').fullCalendar(options); + expect(getRenderedWeekNumber()).toBe(46); + }); + + it('should display the calculated number when a custom function', function() { + options.weekNumberCalculation = function() { + return 4; + }; + $('#cal').fullCalendar(options); + expect(getRenderedWeekNumber()).toBe(4); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/weekNumberTitle.js b/tests/automated/weekNumberTitle.js new file mode 100644 index 0000000..2d37208 --- /dev/null +++ b/tests/automated/weekNumberTitle.js @@ -0,0 +1,51 @@ +describe('weekNumberTitle', function() { + + var options; + + beforeEach(function() { + affix('#cal'); + options = { + weekNumbers: true + }; + }); + + function getRenderedWeekNumberTitle() { + // works for both kinds of views + var text = $('th.fc-week-number').text(); + return text.replace(/\d/g, ''); + } + + [ 'basicWeek', 'agendaWeek' ].forEach(function(viewName) { + describe('when views is ' + viewName, function() { + + beforeEach(function() { + options.defaultView = viewName; + }); + + it('renders correctly by default', function() { + $('#cal').fullCalendar(options); + expect(getRenderedWeekNumberTitle()).toBe('W'); + }); + + it('renders correctly when unspecified and when language is customized', function() { + options.lang = 'es'; + $('#cal').fullCalendar(options); + expect(getRenderedWeekNumberTitle()).toBe('Sm'); + }); + + it('renders correctly when customized and LTR', function() { + options.isRTL = false; + options.weekNumberTitle = 'YO'; + $('#cal').fullCalendar(options); + expect(getRenderedWeekNumberTitle()).toBe('YO'); + }); + + it('renders correctly when customized and RTL', function() { + options.isRTL = true; + options.weekNumberTitle = 'YO'; + $('#cal').fullCalendar(options); + expect(getRenderedWeekNumberTitle()).toBe('YO'); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/automated/weekNumbers.js b/tests/automated/weekNumbers.js new file mode 100644 index 0000000..3bd6d1a --- /dev/null +++ b/tests/automated/weekNumbers.js @@ -0,0 +1,118 @@ + +describe('weekNumbers', function() { + + beforeEach(function() { + affix('#cal'); + }); + + describe('when using month view', function() { + + describe('when using default weekNumbers', function() { + it('should not display weekNumbers', function() { + $('#cal').fullCalendar({ + defaultView: 'month' + }); + var weekNumbersCount = $('.fc-content-skeleton thead .fc-week-number').length; + expect(weekNumbersCount).toEqual(0); + }); + }); + + describe('when setting weekNumbers to false', function() { + it('should not display weekNumbers', function() { + $('#cal').fullCalendar({ + defaultView: 'month', + weekNumbers: false + }); + var weekNumbersCount = $('.fc-content-skeleton thead .fc-week-number').length; + expect(weekNumbersCount).toEqual(0); + }); + }); + + describe('when setting weekNumbers to true', function() { + it('should not display weekNumbers', function() { + $('#cal').fullCalendar({ + defaultView: 'month', + weekNumbers: true, + weekMode: 'fixed' // will make 6 rows + }); + var weekNumbersCount = $('.fc-content-skeleton thead .fc-week-number').length; + expect(weekNumbersCount).toEqual(6); + }); + }); + + }); + + describe('when using basicWeek view', function() { + + describe('with default weekNumbers ', function() { + it('should not display weekNumbers', function() { + $('#cal').fullCalendar({ + defaultView: 'basicWeek' + }); + var weekNumbersCount = $('.fc-content-skeleton thead .fc-week-number').length; + expect(weekNumbersCount).toEqual(0); + }); + }); + + describe('with weekNumbers to false', function() { + it('should not display weekNumbers', function() { + $('#cal').fullCalendar({ + defaultView: 'basicWeek', + weekNumbers: false + }); + var weekNumbersCount = $('.fc-content-skeleton thead .fc-week-number').length; + expect(weekNumbersCount).toEqual(0); + }); + }); + + describe('with weekNumbers to true', function() { + it('should display weekNumbers', function() { + $('#cal').fullCalendar({ + defaultView: 'basicWeek', + weekNumbers: true + }); + var weekNumbersCount = $('.fc-content-skeleton thead .fc-week-number').length; + expect(weekNumbersCount).toEqual(1); + }); + }); + + }); + + describe('when using an agenda view', function() { + + describe('with default weekNumbers', function() { + it('should not display weekNumbers', function() { + $('#cal').fullCalendar({ + defaultView: 'agendaWeek' + }); + var weekNumbersCount = $('.fc-week-number').length; + expect(weekNumbersCount).toEqual(0); + }); + }); + + describe('with weekNumbers to false', function() { + it('should not display weekNumbers', function() { + $('#cal').fullCalendar({ + defaultView: 'agendaWeek', + weekNumbers: false + }); + var weekNumbersCount = $('.fc-week-number').length; + expect(weekNumbersCount).toEqual(0); + }); + }); + + describe('with weekNumbers to true', function() { + it('should display weekNumbers', function() { + $('#cal').fullCalendar({ + defaultView: 'agendaWeek', + weekNumbers: true + }); + var weekNumbersCount = $('.fc-week-number').length; + // 1 row is axis + expect(weekNumbersCount).toEqual(1); + }); + }); + + }); + +}); \ No newline at end of file diff --git a/tests/automated/weekends.js b/tests/automated/weekends.js new file mode 100644 index 0000000..5a2e89a --- /dev/null +++ b/tests/automated/weekends.js @@ -0,0 +1,30 @@ + +describe('when weekends option is set', function() { + + beforeEach(function() { + affix('#calendar'); + }); + + it('should show sat and sun if true', function() { + var options = { + weekends: true + }; + $('#calendar').fullCalendar(options); + var sun = $('.fc-day-header.fc-sun')[0]; + var sat = $('.fc-day-header.fc-sun')[0]; + expect(sun).toBeDefined(); + expect(sat).toBeDefined(); + }); + + it('should not show sat and sun if false', function() { + var options = { + weekends: false + }; + $('#calendar').fullCalendar(options); + var sun = $('.fc-day-header.fc-sun')[0]; + var sat = $('.fc-day-header.fc-sun')[0]; + expect(sun).not.toBeDefined(); + expect(sat).not.toBeDefined(); + }); + +}); \ No newline at end of file diff --git a/tests/base.css b/tests/base.css new file mode 100644 index 0000000..6370e1e --- /dev/null +++ b/tests/base.css @@ -0,0 +1,12 @@ + +body { + margin-top: 40px; + text-align: center; + font-size: 13px; + font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif; +} + +#calendar { + width: 900px; + margin: 0 auto; +} diff --git a/tests/bootstrap.html b/tests/bootstrap.html new file mode 100644 index 0000000..1eef0ae --- /dev/null +++ b/tests/bootstrap.html @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + +
+ + + diff --git a/tests/data_as_a_function.html b/tests/data_as_a_function.html new file mode 100644 index 0000000..09b562d --- /dev/null +++ b/tests/data_as_a_function.html @@ -0,0 +1,54 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/date_util_test.html b/tests/date_util_test.html new file mode 100644 index 0000000..ce04e4e --- /dev/null +++ b/tests/date_util_test.html @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/tests/day_render.html b/tests/day_render.html new file mode 100644 index 0000000..2745034 --- /dev/null +++ b/tests/day_render.html @@ -0,0 +1,108 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/dbclick.html b/tests/dbclick.html new file mode 100644 index 0000000..dbe23a5 --- /dev/null +++ b/tests/dbclick.html @@ -0,0 +1,101 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/droppable.html b/tests/droppable.html new file mode 100644 index 0000000..6ce8139 --- /dev/null +++ b/tests/droppable.html @@ -0,0 +1,167 @@ + + + + + + + + + + + + +
+
+
Draggable 1
+
Draggable 2
+
Draggable 3
+
+
+
+ + diff --git a/tests/event_data_transform.html b/tests/event_data_transform.html new file mode 100644 index 0000000..975e97b --- /dev/null +++ b/tests/event_data_transform.html @@ -0,0 +1,109 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/event_stress_test.html b/tests/event_stress_test.html new file mode 100644 index 0000000..2e38bb3 --- /dev/null +++ b/tests/event_stress_test.html @@ -0,0 +1,77 @@ + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tests/fullheight.html b/tests/fullheight.html new file mode 100644 index 0000000..52b248b --- /dev/null +++ b/tests/fullheight.html @@ -0,0 +1,106 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/gcal.html b/tests/gcal.html new file mode 100644 index 0000000..4e7b133 --- /dev/null +++ b/tests/gcal.html @@ -0,0 +1,62 @@ + + + + + + + + + + + + + +
+ + diff --git a/tests/hiddenDays.html b/tests/hiddenDays.html new file mode 100644 index 0000000..97bd90b --- /dev/null +++ b/tests/hiddenDays.html @@ -0,0 +1,81 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/issue_1035.html b/tests/issue_1035.html new file mode 100644 index 0000000..19fe9ef --- /dev/null +++ b/tests/issue_1035.html @@ -0,0 +1,106 @@ + + + + + + + + + + + + + +

The selection box for selecting agenda slot cells should align to the grid.

+

(even though .fc-agenda-slots td div has been set to 10px tall)

+ +
+ + + diff --git a/tests/issue_2048.html b/tests/issue_2048.html new file mode 100644 index 0000000..8892b22 --- /dev/null +++ b/tests/issue_2048.html @@ -0,0 +1,68 @@ + + + + + + + + + + + + + +
+ + diff --git a/tests/issue_206_parseDate_dst.html b/tests/issue_206_parseDate_dst.html new file mode 100644 index 0000000..44f31fe --- /dev/null +++ b/tests/issue_206_parseDate_dst.html @@ -0,0 +1,64 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/issue_2154.html b/tests/issue_2154.html new file mode 100644 index 0000000..d18557b --- /dev/null +++ b/tests/issue_2154.html @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + +
+ + + diff --git a/tests/issue_220_buttons_ie6.html b/tests/issue_220_buttons_ie6.html new file mode 100644 index 0000000..19d3bb6 --- /dev/null +++ b/tests/issue_220_buttons_ie6.html @@ -0,0 +1,54 @@ + + + + + + + + + + + + +
+
+
+ +
Nav
+ +
+ +

+this is a paragraph +

+ +
+ +
+ +
+
+ + diff --git a/tests/issue_221_quick_remove_source.html b/tests/issue_221_quick_remove_source.html new file mode 100644 index 0000000..393ed87 --- /dev/null +++ b/tests/issue_221_quick_remove_source.html @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + +
+ + diff --git a/tests/issue_230_height_json_events.html b/tests/issue_230_height_json_events.html new file mode 100644 index 0000000..9878dea --- /dev/null +++ b/tests/issue_230_height_json_events.html @@ -0,0 +1,48 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/issue_244_aspectRatio_0.html b/tests/issue_244_aspectRatio_0.html new file mode 100644 index 0000000..525ddeb --- /dev/null +++ b/tests/issue_244_aspectRatio_0.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/issue_251_empty_end_date.html b/tests/issue_251_empty_end_date.html new file mode 100644 index 0000000..9dd43d5 --- /dev/null +++ b/tests/issue_251_empty_end_date.html @@ -0,0 +1,56 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/issue_333_blinking.html b/tests/issue_333_blinking.html new file mode 100644 index 0000000..1930f6c --- /dev/null +++ b/tests/issue_333_blinking.html @@ -0,0 +1,100 @@ + + + + + + + + + + + +
+ + diff --git a/tests/issue_417_refetchEvents.html b/tests/issue_417_refetchEvents.html new file mode 100644 index 0000000..87f56f5 --- /dev/null +++ b/tests/issue_417_refetchEvents.html @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + +
+ + diff --git a/tests/issue_429_gotoDate.html b/tests/issue_429_gotoDate.html new file mode 100644 index 0000000..1b021b0 --- /dev/null +++ b/tests/issue_429_gotoDate.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + +
+ + diff --git a/tests/issue_477_event_width.html b/tests/issue_477_event_width.html new file mode 100644 index 0000000..f33e423 --- /dev/null +++ b/tests/issue_477_event_width.html @@ -0,0 +1,53 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/issue_517_js_error_ie.html b/tests/issue_517_js_error_ie.html new file mode 100644 index 0000000..f90be1a --- /dev/null +++ b/tests/issue_517_js_error_ie.html @@ -0,0 +1,131 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/issue_554.html b/tests/issue_554.html new file mode 100644 index 0000000..48e1439 --- /dev/null +++ b/tests/issue_554.html @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + +
+ + diff --git a/tests/issue_586_refetchEvents.html b/tests/issue_586_refetchEvents.html new file mode 100644 index 0000000..ba399a9 --- /dev/null +++ b/tests/issue_586_refetchEvents.html @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + +
+ + diff --git a/tests/issue_616.html b/tests/issue_616.html new file mode 100644 index 0000000..57cef76 --- /dev/null +++ b/tests/issue_616.html @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + +
+ + diff --git a/tests/issue_679.html b/tests/issue_679.html new file mode 100644 index 0000000..b61f9cf --- /dev/null +++ b/tests/issue_679.html @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + +
+ + diff --git a/tests/issue_688_parseInt.html b/tests/issue_688_parseInt.html new file mode 100644 index 0000000..108cc59 --- /dev/null +++ b/tests/issue_688_parseInt.html @@ -0,0 +1,98 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/issue_740_event_resizing.html b/tests/issue_740_event_resizing.html new file mode 100644 index 0000000..90ce650 --- /dev/null +++ b/tests/issue_740_event_resizing.html @@ -0,0 +1,98 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/issue_750.html b/tests/issue_750.html new file mode 100644 index 0000000..6373282 --- /dev/null +++ b/tests/issue_750.html @@ -0,0 +1,99 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/issue_757_removeEvents.html b/tests/issue_757_removeEvents.html new file mode 100644 index 0000000..9547ddb --- /dev/null +++ b/tests/issue_757_removeEvents.html @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + +
+ + diff --git a/tests/languages-datepicker.html b/tests/languages-datepicker.html new file mode 100644 index 0000000..879f88a --- /dev/null +++ b/tests/languages-datepicker.html @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + +
+ + Language: + + +
+ +
+ +
+ + + diff --git a/tests/lib/jasmine-ext.js b/tests/lib/jasmine-ext.js new file mode 100644 index 0000000..ac2ef45 --- /dev/null +++ b/tests/lib/jasmine-ext.js @@ -0,0 +1,170 @@ + +beforeEach(function() { + + // don't show warnings about deprecated methods like `moment.lang`, etc. + // at some point we'll require moment version above 2.8. + // until then, it's too annoying to support two versions. + // (not the best place for this) + moment.suppressDeprecationWarnings = true; + + jasmine.addMatchers({ + + // Moment and Duration + + toEqualMoment: function() { + return { + compare: function(actual, expected) { + var actualStr = $.fullCalendar.moment.parseZone(actual).format(); + var expectedStr = $.fullCalendar.moment.parseZone(expected).format(); + var result = { + pass: actualStr === expectedStr + }; + if (!result.pass) { + result.message = 'Moment ' + actualStr + ' does not equal ' + expectedStr; + } + return result; + } + }; + }, + toEqualNow: function() { + return { + compare: function(actual) { + var actualMoment = $.fullCalendar.moment.parseZone(actual); + var result = { + pass: Math.abs(actualMoment - new Date()) < 1000 // within a second of current datetime + }; + if (!result.pass) { + result.message = 'Moment ' + actualMoment.format() + ' is not close enough to now'; + } + return result; + } + }; + }, + toEqualDuration: function() { + return { + compare: function(actual, expected) { + var actualStr = serializeDuration(moment.duration(actual)); + var expectedStr = serializeDuration(moment.duration(expected)); + var result = { + pass: actualStr === expectedStr + }; + if (!result.pass) { + result.message = 'Duration ' + actualStr + ' does not equal ' + expectedStr; + } + return result; + } + }; + }, + + + // DOM + + toHaveScrollbars: function() { + return { + compare: function(actual) { + var elm = $(actual); + var result = { + pass: elm[0].scrollWidth - 1 > elm[0].clientWidth || // -1 !!! + elm[0].scrollHeight - 1 > elm[0].clientHeight // -1 !!! + }; + // !!! - IE was reporting a scrollWidth/scrollHeight 1 pixel taller than what it was :( + return result; + } + }; + }, + + + // Geometry + + toBeBoundedBy: function() { + return { + compare: function(actual, expected) { + var outer = getBounds(expected); + var inner = getBounds(actual); + var result = { + pass: outer && inner && + inner.left >= outer.left && + inner.right <= outer.right && + inner.top >= outer.top && + inner.bottom <= outer.bottom + }; + if (!result.pass) { + result.message = 'Element does not bound other element'; + } + return result; + } + }; + }, + toBeLeftOf: function() { + return { + compare: function(actual, expected) { + var subjectBounds = getBounds(actual); + var otherBounds = getBounds(expected); + var result = { + pass: subjectBounds && otherBounds && + Math.round(subjectBounds.right) <= Math.round(otherBounds.left) + // need to round because IE was giving weird fractions + }; + if (!result.pass) { + result.message = 'Element is not to the left of the other element'; + } + return result; + } + }; + }, + toBeRightOf: function() { + return { + compare: function(actual, expected) { + var subjectBounds = getBounds(actual); + var otherBounds = getBounds(expected); + var result = { + pass: subjectBounds && otherBounds && + Math.round(subjectBounds.left) >= Math.round(otherBounds.right) + // need to round because IE was giving weird fractions + }; + if (!result.pass) { + result.message = 'Element is not to the right of the other element'; + } + return result; + } + }; + } + + }); + + function serializeDuration(duration) { + return Math.floor(duration.asDays()) + '.' + + pad(duration.hours(), 2) + ':' + + pad(duration.minutes(), 2) + ':' + + pad(duration.seconds(), 2) + '.' + + pad(duration.milliseconds(), 3); + } + + function pad(n, width) { + n = n + ''; + return n.length >= width ? n : new Array(width - n.length + 1).join('0') + n; + } + + function getBounds(node) { + var el = $(node); + var offset = el.offset(); + + if (!offset) { + return false; + } + + return { + top: offset.top, + left: offset.left, + right: offset.left + el.outerWidth(), + bottom: offset.top + el.outerHeight() + }; + } + +}); + +// Destroy the calendar afterwards, to prevent memory leaks +// (not the best place for this) +afterEach(function() { + $('#calendar,#cal').fullCalendar('destroy'); // common id's for calendars in tests +}); diff --git a/tests/lib/jquery-simulate-hacks.js b/tests/lib/jquery-simulate-hacks.js new file mode 100644 index 0000000..fa529f3 --- /dev/null +++ b/tests/lib/jquery-simulate-hacks.js @@ -0,0 +1,54 @@ +(function($) { + + /* + Addresses a shortcoming with jquery-simulate[-ext] where a click event doesn't + provide pageX/pageY coordinates. Should be the centered coordinates of the element. + This file needs to be loaded after jquery-simulate and jquery-simulate-ext. + */ + + var originalMouseEvent = $.simulate.prototype.mouseEvent; + + $.simulate.prototype.mouseEvent = function(type, options) { + if (type === 'click' && options.pageX === undefined && options.pageY === undefined) { + $.extend(options, findCenterPageXY(this.target)); + } + // after this, jquery-simulate-ext will calculate screenX/screenY + // and jquery-simulate will calculate clientX/clientY... + return originalMouseEvent.apply(this, [ type, options ]); + }; + + function findCenterPageXY( elem ) { + var offset, + $elem = $(elem), + offset = $elem.offset(); + + return { + pageX: Math.round(offset.left + $elem.outerWidth() / 2), + pageY: Math.round(offset.top + $elem.outerHeight() / 2) + }; + } + + + /* + Give jquery-simulate-ext drag-n-drop a default interpolation. + */ + + var originalSimulate = $.fn.simulate; + + $.fn.simulate = function(eventName, options) { + + if (eventName == 'drag' || eventName == 'drop' || eventName == 'drag-n-drop') { + options = options || {}; + if (options.interpolation === undefined) { + options.interpolation = { + stepCount: 10, + duration: 100 + }; + } + return originalSimulate.call(this, eventName, options); + } + + return originalSimulate.apply(this, arguments); + }; + +})(jQuery); \ No newline at end of file diff --git a/tests/liquidwidth.html b/tests/liquidwidth.html new file mode 100644 index 0000000..8bc2aab --- /dev/null +++ b/tests/liquidwidth.html @@ -0,0 +1,111 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/locale.html b/tests/locale.html new file mode 100644 index 0000000..e23e275 --- /dev/null +++ b/tests/locale.html @@ -0,0 +1,129 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/long_event_titles.html b/tests/long_event_titles.html new file mode 100644 index 0000000..94781cb --- /dev/null +++ b/tests/long_event_titles.html @@ -0,0 +1,76 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/many_agenda_events.html b/tests/many_agenda_events.html new file mode 100644 index 0000000..9000fb3 --- /dev/null +++ b/tests/many_agenda_events.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/many_events.html b/tests/many_events.html new file mode 100644 index 0000000..43460e6 --- /dev/null +++ b/tests/many_events.html @@ -0,0 +1,52 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/memory_leak.html b/tests/memory_leak.html new file mode 100644 index 0000000..f58a97a --- /dev/null +++ b/tests/memory_leak.html @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + +
+ + + diff --git a/tests/method_destroy.html b/tests/method_destroy.html new file mode 100644 index 0000000..65e2330 --- /dev/null +++ b/tests/method_destroy.html @@ -0,0 +1,50 @@ + + + + + + + + + + + + +
some text in here
+ + diff --git a/tests/methods.html b/tests/methods.html new file mode 100644 index 0000000..2f3b105 --- /dev/null +++ b/tests/methods.html @@ -0,0 +1,192 @@ + + + + + + + + + + + + + +

+ + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + +

+ +
+ + diff --git a/tests/no_event_titles.html b/tests/no_event_titles.html new file mode 100644 index 0000000..2363842 --- /dev/null +++ b/tests/no_event_titles.html @@ -0,0 +1,67 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/options.html b/tests/options.html new file mode 100644 index 0000000..abda52b --- /dev/null +++ b/tests/options.html @@ -0,0 +1,149 @@ + + + + + + + + + + + +
+ + diff --git a/tests/past_future_classNames.html b/tests/past_future_classNames.html new file mode 100644 index 0000000..ade1164 --- /dev/null +++ b/tests/past_future_classNames.html @@ -0,0 +1,103 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/plain.html b/tests/plain.html new file mode 100644 index 0000000..1468563 --- /dev/null +++ b/tests/plain.html @@ -0,0 +1,85 @@ + + + + + + + + + + + + + +
+ + diff --git a/tests/selectable.html b/tests/selectable.html new file mode 100644 index 0000000..a6fe56a --- /dev/null +++ b/tests/selectable.html @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/tests/short_agenda.html b/tests/short_agenda.html new file mode 100644 index 0000000..feac79a --- /dev/null +++ b/tests/short_agenda.html @@ -0,0 +1,98 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/skip-redraw-test.html b/tests/skip-redraw-test.html new file mode 100644 index 0000000..7f77f3f --- /dev/null +++ b/tests/skip-redraw-test.html @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + +

gotoDate test page

+

+ This page is to test refactoring of gotoDate to check whether or not to call render() based on view.visStart and view.visEnd. +

+
+ + diff --git a/tests/slotEventOverlap-demo.html b/tests/slotEventOverlap-demo.html new file mode 100644 index 0000000..92c5d99 --- /dev/null +++ b/tests/slotEventOverlap-demo.html @@ -0,0 +1,89 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/slot_event_overlap.html b/tests/slot_event_overlap.html new file mode 100644 index 0000000..29296de --- /dev/null +++ b/tests/slot_event_overlap.html @@ -0,0 +1,215 @@ + + + + + + + + + + + + + +

Default:

+
+ +

No overlap:

+
+ +

RTL:

+
+ +

RTL, no overlap:

+
+ + + diff --git a/tests/slot_event_overlap.json b/tests/slot_event_overlap.json new file mode 100644 index 0000000..af99810 --- /dev/null +++ b/tests/slot_event_overlap.json @@ -0,0 +1,102 @@ +[ + { + "title": "1", + "start": "2013-07-31T00:30", + "end": "2013-07-31T02:30", + "allDay": false + }, + { + "title": "2", + "start": "2013-07-31T00:30", + "end": "2013-07-31T02:30", + "allDay": false + }, + { + "title": "3", + "start": "2013-07-31T02:30", + "end": "2013-07-31T04:30", + "allDay": false + }, + { + "title": "4", + "start": "2013-07-31T02:30", + "end": "2013-07-31T04:30", + "allDay": false + }, + { + "title": "5", + "start": "2013-07-31T02:30", + "end": "2013-07-31T04:30", + "allDay": false + }, + { + "title": "6", + "start": "2013-07-31T01:00", + "end": "2013-07-31T03:00", + "allDay": false + }, + { + "title": "7", + "start": "2013-07-31T01:30", + "end": "2013-07-31T03:30", + "allDay": false + }, + { + "title": "8", + "start": "2013-07-31T02:50", + "end": "2013-07-31T04:50", + "allDay": false + }, + { + "title": "9", + "start": "2013-07-31T04:30", + "end": "2013-07-31T09:00", + "allDay": false + }, + { + "title": "10", + "start": "2013-07-31T02:00", + "end": "2013-07-31T04:05", + "allDay": false + }, + { + "title": "11", + "start": "2013-07-31T02:50", + "end": "2013-07-31T04:50", + "allDay": false + }, + { + "title": "12", + "start": "2013-07-31T02:50", + "end": "2013-07-31T04:50", + "allDay": false + }, + { + "title": "13", + "start": "2013-07-31T04:00", + "end": "2013-07-31T06:00", + "allDay": false, + "className": "equalwidth1" + }, + { + "title": "14", + "start": "2013-07-31T04:10", + "end": "2013-07-31T06:09", + "allDay": false, + "className": "equalwidth1" + }, + { + "title": "15", + "start": "2013-07-31T06:30", + "end": "2013-07-31T08:30", + "allDay": false, + "className": "equalwidth2" + }, + { + "title": "16", + "start": "2013-07-31T06:30", + "end": "2013-07-31T08:30", + "allDay": false, + "className": "equalwidth2" + } +] \ No newline at end of file diff --git a/tests/snap.html b/tests/snap.html new file mode 100644 index 0000000..c02f67b --- /dev/null +++ b/tests/snap.html @@ -0,0 +1,105 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/sources.html b/tests/sources.html new file mode 100644 index 0000000..ec3f8f7 --- /dev/null +++ b/tests/sources.html @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + +

+ +

+
+ + diff --git a/tests/sources_new.html b/tests/sources_new.html new file mode 100644 index 0000000..a1026f7 --- /dev/null +++ b/tests/sources_new.html @@ -0,0 +1,194 @@ + + + + + + + + + + + + + +
+ + diff --git a/tests/stacking.html b/tests/stacking.html new file mode 100644 index 0000000..2e838b3 --- /dev/null +++ b/tests/stacking.html @@ -0,0 +1,136 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/tabs.html b/tests/tabs.html new file mode 100644 index 0000000..d1cc3a4 --- /dev/null +++ b/tests/tabs.html @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + +
+ + + +
+

This is a test

+
+ +
+
+
+ +
+ + + + diff --git a/tests/theming.html b/tests/theming.html new file mode 100644 index 0000000..ad91aa4 --- /dev/null +++ b/tests/theming.html @@ -0,0 +1,108 @@ + + + + + + + + + + + + +
+
+ + diff --git a/tests/triggers.html b/tests/triggers.html new file mode 100644 index 0000000..0c6f07d --- /dev/null +++ b/tests/triggers.html @@ -0,0 +1,222 @@ + + + + + + + + + + + + +
+ + diff --git a/tests/triggers_view.html b/tests/triggers_view.html new file mode 100644 index 0000000..5258876 --- /dev/null +++ b/tests/triggers_view.html @@ -0,0 +1,152 @@ + + + + + + + + + + + + + +
+ + diff --git a/tests/week_numbers.html b/tests/week_numbers.html new file mode 100644 index 0000000..b0cc678 --- /dev/null +++ b/tests/week_numbers.html @@ -0,0 +1,109 @@ + + + + + + + + + + + + + +
+ +
'),e>t&&(n+=''),i>e&&(n+=''),n=this.bookendCells(n,"highlight"),'
'+n+""+"
"+"
"}}),t.extend(ee.prototype,{segs:null,rowStructs:null,renderEvents:function(e){var i=this.rowStructs=this.renderEventRows(e),n=[];this.rowEls.each(function(e,r){t(r).find(".fc-content-skeleton > table").append(i[e].tbodyEl),n.push.apply(n,i[e].segs)}),this.segs=n},getSegs:function(){return(this.segs||[]).concat(this.popoverSegs||[])},destroyEvents:function(){var t,e;for(J.prototype.destroyEvents.call(this),t=this.rowStructs||[];e=t.pop();)e.tbodyEl.remove();this.segs=null,this.destroySegPopover()},renderEventRows:function(t){var e,i,n=this.eventsToSegs(t),r=[];for(n=this.renderSegs(n),e=this.groupSegRows(n),i=0;e.length>i;i++)r.push(this.renderEventRow(i,e[i]));return r},renderSegHtml:function(t,e){var i,n=this.view,r=n.opt("isRTL"),o=t.event,s=n.isEventDraggable(o),l=!e&&o.allDay&&t.isEnd&&n.isEventResizable(o),a=this.getSegClasses(t,s,l),c=this.getEventSkinCss(o),d="";return a.unshift("fc-day-grid-event"),!o.allDay&&t.isStart&&(d=''+R(n.getEventTimeText(o))+""),i=''+(R(o.title||"")||" ")+"",'"+'
'+(r?i+" "+d:d+" "+i)+"
"+(l?'
':"")+""},renderEventRow:function(e,i){function n(e){for(;e>s;)d=(y[r-1]||[])[s],d?d.attr("rowspan",parseInt(d.attr("rowspan")||1,10)+1):(d=t("
"),l.append(d)),v[r][s]=d,y[r][s]=d,s++}var r,o,s,l,a,c,d,h=this.view,u=h.colCnt,f=this.buildSegLevels(i),g=Math.max(1,f.length),p=t("
').append(c.el),c.leftCol!=c.rightCol?d.attr("colspan",c.rightCol-c.leftCol+1):y[r][s]=d;c.rightCol>=s;)v[r][s]=d,m[r][s]=c,s++;l.append(d)}n(u),this.bookendCells(l,"eventSkeleton"),p.append(l)}return{row:e,tbodyEl:p,cellMatrix:v,segMatrix:m,segLevels:f,segs:i}},buildSegLevels:function(t){var e,i,n,r=[];for(t.sort(te),e=0;t.length>e;e++){for(i=t[e],n=0;r.length>n&&ie(i,r[n]);n++);i.level=n,(r[n]||(r[n]=[])).push(i)}for(n=0;r.length>n;n++)r[n].sort(ne);return r},groupSegRows:function(t){var e,i=this.view,n=[];for(e=0;i.rowCnt>e;e++)n.push([]);for(e=0;t.length>e;e++)n[t[e].row].push(t[e]);return n}}),t.extend(ee.prototype,{segPopover:null,popoverSegs:null,destroySegPopover:function(){this.segPopover&&this.segPopover.hide()},limitRows:function(t){var e,i,n=this.rowStructs||[];for(e=0;n.length>e;e++)this.unlimitRow(e),i=t?"number"==typeof t?t:this.computeRowLevelLimit(e):!1,i!==!1&&this.limitRow(e,i)},computeRowLevelLimit:function(t){var e,i,n=this.rowEls.eq(t),r=n.height(),o=this.rowStructs[t].tbodyEl.children();for(e=0;o.length>e;e++)if(i=o.eq(e).removeClass("fc-limited"),i.position().top+i.outerHeight()>r)return e;return!1},limitRow:function(e,i){function n(n){for(;n>T;)r={row:e,col:T},d=E.getCellSegs(r,i),d.length&&(f=s[i-1][T],w=E.renderMoreLink(r,d),y=t("
").append(w),f.append(y),D.push(y[0])),T++}var r,o,s,l,a,c,d,h,u,f,g,p,m,v,y,w,E=this,b=this.view,S=this.rowStructs[e],D=[],T=0;if(i&&S.segLevels.length>i){for(o=S.segLevels[i-1],s=S.cellMatrix,l=S.tbodyEl.children().slice(i).addClass("fc-limited").get(),a=0;o.length>a;a++){for(c=o[a],n(c.leftCol),u=[],h=0;c.rightCol>=T;)r={row:e,col:T},d=this.getCellSegs(r,i),u.push(d),h+=d.length,T++;if(h){for(f=s[i-1][c.leftCol],g=f.attr("rowspan")||1,p=[],m=0;u.length>m;m++)v=t('
').attr("rowspan",g),d=u[m],r={row:e,col:c.leftCol+m},w=this.renderMoreLink(r,[c].concat(d)),y=t("
").append(w),v.append(y),p.push(v[0]),D.push(v[0]);f.addClass("fc-limited").after(t(p)),l.push(f[0])}}n(b.colCnt),S.moreEls=t(D),S.limitedEls=t(l)}},unlimitRow:function(t){var e=this.rowStructs[t];e.moreEls&&(e.moreEls.remove(),e.moreEls=null),e.limitedEls&&(e.limitedEls.removeClass("fc-limited"),e.limitedEls=null)},renderMoreLink:function(e,i){var n=this,r=this.view;return t('').text(this.getMoreLinkText(i.length)).on("click",function(o){var s=r.opt("eventLimitClick"),l=r.cellToDate(e),a=t(this),c=n.getCellDayEl(e),d=n.getCellSegs(e),h=n.resliceDaySegs(d,l),u=n.resliceDaySegs(i,l);"function"==typeof s&&(s=r.trigger("eventLimitClick",null,{date:l,dayEl:c,moreEl:a,segs:h,hiddenSegs:u},o)),"popover"===s?n.showSegPopover(l,e,a,h):"string"==typeof s&&r.calendar.zoomTo(l,s)})},showSegPopover:function(t,e,i,n){var r,o,s=this,l=this.view,a=i.parent();r=1==l.rowCnt?this.view.el:this.rowEls.eq(e.row),o={className:"fc-more-popover",content:this.renderSegPopoverContent(t,n),parentEl:this.el,top:r.offset().top,autoHide:!0,viewportConstrain:l.opt("popoverViewportConstrain"),hide:function(){s.segPopover.destroy(),s.segPopover=null,s.popoverSegs=null}},l.opt("isRTL")?o.right=a.offset().left+a.outerWidth()+1:o.left=a.offset().left-1,this.segPopover=new j(o),this.segPopover.show()},renderSegPopoverContent:function(e,i){var n,r=this.view,o=r.opt("theme"),s=e.format(r.opt("dayPopoverFormat")),l=t('
'+''+''+R(s)+""+'
'+"
"+'
'+'
'+"
"),a=l.find(".fc-event-container");for(i=this.renderSegs(i,!0),this.popoverSegs=i,n=0;i.length>n;n++)i[n].cellDate=e,a.append(i[n].el);return l},resliceDaySegs:function(e,i){var n=t.map(e,function(t){return t.event}),r=i.clone().stripTime(),o=r.clone().add(1,"days");return this.eventsToSegs(n,r,o)},getMoreLinkText:function(t){var e=this.view,i=e.opt("eventLimitText");return"function"==typeof i?i(t):"+"+t+" "+i},getCellSegs:function(t,e){for(var i,n=this.rowStructs[t.row].segMatrix,r=e||0,o=[];n.length>r;)i=n[r][t.col],i&&o.push(i),r++;return o}}),re.prototype=H(J.prototype),t.extend(re.prototype,{slotDuration:null,snapDuration:null,minTime:null,maxTime:null,dayEls:null,slatEls:null,slatTops:null,highlightEl:null,helperEl:null,render:function(){this.processOptions(),this.el.html(this.renderHtml()),this.dayEls=this.el.find(".fc-day"),this.slatEls=this.el.find(".fc-slats tr"),this.computeSlatTops(),J.prototype.render.call(this)},renderHtml:function(){return'
'+this.rowHtml("slotBg")+"
"+"
"+'
'+""+this.slatRowHtml()+"
"+"
"},slotBgCellHtml:function(t,e,i){return this.bgCellHtml(t,e,i)},slatRowHtml:function(){for(var t,i,n,r=this.view,o=r.calendar,s=r.opt("isRTL"),l="",a=0===this.slotDuration.asMinutes()%15,c=e.duration(+this.minTime);this.maxTime>c;)t=r.start.clone().time(c),i=t.minutes(),n='
"+(a&&i?"":""+R(o.formatDate(t,r.opt("axisFormat")))+"")+"
'+(s?n:"")+"
',d=n.col),r=l.cellToDate(0,d),o=this.computeDateTop(n.start,r),s=this.computeDateTop(n.end,r),c+='
'+"
"+"
'),c=this.bookendCells(c,"highlight"),'
'+c+""+"
"+"
"}}),t.extend(re.prototype,{segs:null,eventSkeletonEl:null,renderEvents:function(e){var i=this.renderEventTable(e);this.eventSkeletonEl=t('
').append(i.tableEl),this.el.append(this.eventSkeletonEl),this.segs=i.segs},getSegs:function(){return this.segs||[]},destroyEvents:function(){J.prototype.destroyEvents.call(this),this.eventSkeletonEl&&(this.eventSkeletonEl.remove(),this.eventSkeletonEl=null),this.segs=null},renderEventTable:function(e){var i,n,r,o,s,l,a=t("
"),c=a.find("tr"),d=this.eventsToSegs(e);for(d=this.renderSegs(d),i=this.groupSegCols(d),this.computeSegVerticals(d),o=0;i.length>o;o++){for(s=i[o],oe(s),l=t('
'),n=0;s.length>n;n++)r=s[n],r.el.css(this.generateSegPositionCss(r)),30>r.bottom-r.top&&r.el.addClass("fc-short"),l.append(r.el);c.append(t("
").append(l))}return this.bookendCells(c,"eventSkeleton"),{tableEl:a,segs:d}},updateSegVerticals:function(){var t,e=this.segs;if(e)for(this.computeSegVerticals(e),t=0;e.length>t;t++)e[t].el.css(this.generateSegVerticalCss(e[t]))},computeSegVerticals:function(t){var e,i;for(e=0;t.length>e;e++)i=t[e],i.top=this.computeDateTop(i.start,i.start),i.bottom=this.computeDateTop(i.end,i.start)},renderSegHtml:function(t,e){var i,n,r,o=this.view,s=t.event,l=o.isEventDraggable(s),a=!e&&t.isEnd&&o.isEventResizable(s),c=this.getSegClasses(t,l,a),d=this.getEventSkinCss(s);return c.unshift("fc-time-grid-event"),o.isMultiDayEvent(s)?(t.isStart||t.isEnd)&&(i=o.getEventTimeText(t.start,t.end),n=o.getEventTimeText(t.start,t.end,"LT"),r=o.getEventTimeText(t.start,null)):(i=o.getEventTimeText(s),n=o.getEventTimeText(s,"LT"),r=o.getEventTimeText(s.start,null)),'"+'
'+(i?'
"+""+R(i)+""+"
":"")+(s.title?'
'+R(s.title)+"
":"")+"
"+'
'+(a?'
':"")+""},generateSegPositionCss:function(t){var e,i,n=this.view,r=n.opt("isRTL"),o=n.opt("slotEventOverlap"),s=t.backwardCoord,l=t.forwardCoord,a=this.generateSegVerticalCss(t);return o&&(l=Math.min(1,s+2*(l-s))),r?(e=1-l,i=s):(e=s,i=1-l),a.zIndex=t.level+1,a.left=100*e+"%",a.right=100*i+"%",o&&t.forwardPressure&&(a[r?"marginLeft":"marginRight"]=20),a},generateSegVerticalCss:function(t){return{top:t.top,bottom:-t.bottom}},groupSegCols:function(t){var e,i=this.view,n=[];for(e=0;i.colCnt>e;e++)n.push([]);for(e=0;t.length>e;e++)n[t[e].col].push(t[e]);return n}}),fe.prototype={calendar:null,coordMap:null,el:null,start:null,end:null,intervalStart:null,intervalEnd:null,rowCnt:null,colCnt:null,isSelected:!1,scrollerEl:null,scrollTop:null,widgetHeaderClass:null,widgetContentClass:null,highlightStateClass:null,documentMousedownProxy:null,documentDragStartProxy:null,init:function(){var e=this.opt("theme")?"ui":"fc";this.widgetHeaderClass=e+"-widget-header",this.widgetContentClass=e+"-widget-content",this.highlightStateClass=e+"-state-highlight",this.documentMousedownProxy=t.proxy(this,"documentMousedown"),this.documentDragStartProxy=t.proxy(this,"documentDragStart")},render:function(){this.updateSize(),this.trigger("viewRender",this,this,this.el),t(document).on("mousedown",this.documentMousedownProxy).on("dragstart",this.documentDragStartProxy)},destroy:function(){this.unselect(),this.trigger("viewDestroy",this,this,this.el),this.destroyEvents(),this.el.empty(),t(document).off("mousedown",this.documentMousedownProxy).off("dragstart",this.documentDragStartProxy)},incrementDate:function(){},updateSize:function(t){t&&this.recordScroll(),this.updateHeight(),this.updateWidth()},updateWidth:function(){},updateHeight:function(){var t=this.calendar;this.setHeight(t.getSuggestedViewHeight(),t.isHeightAuto())},setHeight:function(){},computeScrollerHeight:function(t){var e,i=this.el.add(this.scrollerEl);return i.css({position:"relative",left:-1}),e=this.el.outerHeight()-this.scrollerEl.height(),i.css({position:"",left:""}),t-e},recordScroll:function(){this.scrollerEl&&(this.scrollTop=this.scrollerEl.scrollTop())},restoreScroll:function(){null!==this.scrollTop&&this.scrollerEl.scrollTop(this.scrollTop)},renderEvents:function(){this.segEach(function(t){this.trigger("eventAfterRender",t.event,t.event,t.el)}),this.trigger("eventAfterAllRender")},destroyEvents:function(){this.segEach(function(t){this.trigger("eventDestroy",t.event,t.event,t.el)})},resolveEventEl:function(e,i){var n=this.trigger("eventRender",e,e,i);return n===!1?i=null:n&&n!==!0&&(i=t(n)),i},showEvent:function(t){this.segEach(function(t){t.el.css("visibility","")},t)},hideEvent:function(t){this.segEach(function(t){t.el.css("visibility","hidden")},t)},segEach:function(t,e){var i,n=this.getSegs();for(i=0;n.length>i;i++)e&&n[i].event._id!==e._id||t.call(this,n[i])},getSegs:function(){},renderDrag:function(){},destroyDrag:function(){},documentDragStart:function(e){var i,n=this,r=null;this.opt("droppable")&&(i=new q(this.coordMap,{cellOver:function(t,e){r=e,n.renderDrag(e)},cellOut:function(){r=null,n.destroyDrag()}}),t(document).one("dragstop",function(t,e){n.destroyDrag(),r&&n.trigger("drop",t.target,r,t,e)}),i.startDrag(e))},select:function(t,e,i){this.unselect(i),this.renderSelection(t,e),this.reportSelection(t,e,i)},renderSelection:function(){},reportSelection:function(t,e,i){this.isSelected=!0,this.trigger("select",null,t,e,i)},unselect:function(t){this.isSelected&&(this.isSelected=!1,this.destroySelection(),this.trigger("unselect",null,t))},destroySelection:function(){},documentMousedown:function(e){var i;this.isSelected&&this.opt("unselectAuto")&&E(e)&&(i=this.opt("unselectCancel"),i&&t(e.target).closest(i).length||this.unselect(e))}},ge.prototype=H(fe.prototype),t.extend(ge.prototype,{dayGrid:null,dayNumbersVisible:!1,weekNumbersVisible:!1,weekNumberWidth:null,headRowEl:null,render:function(t,e,i){this.rowCnt=t,this.colCnt=e,this.dayNumbersVisible=i,this.weekNumbersVisible=this.opt("weekNumbers"),this.dayGrid.numbersVisible=this.dayNumbersVisible||this.weekNumbersVisible,this.el.addClass("fc-basic-view").html(this.renderHtml()),this.headRowEl=this.el.find("thead .fc-row"),this.scrollerEl=this.el.find(".fc-day-grid-container"),this.dayGrid.coordMap.containerEl=this.scrollerEl,this.dayGrid.el=this.el.find(".fc-day-grid"),this.dayGrid.render(this.hasRigidRows()),fe.prototype.render.call(this)},destroy:function(){this.dayGrid.destroy(),fe.prototype.destroy.call(this)},renderHtml:function(){return'"+""+""+""+""+'"+""+""+"
'+this.dayGrid.headHtml()+"
'+'
'+'
'+"
"+"
" +},headIntroHtml:function(){return this.weekNumbersVisible?'
"+""+R(this.opt("weekNumberTitle"))+""+""+""+this.calendar.calculateWeekNumber(this.cellToDate(t,0))+""+"'+i.date()+""},weekNumberStyleAttr:function(){return null!==this.weekNumberWidth?'style="width:'+this.weekNumberWidth+'px"':""},hasRigidRows:function(){var t=this.opt("eventLimit");return t&&"number"!=typeof t},updateWidth:function(){this.weekNumbersVisible&&(this.weekNumberWidth=p(this.el.find(".fc-week-number")))},setHeight:function(t,e){var i,n=this.opt("eventLimit");v(this.scrollerEl),u(this.headRowEl),this.dayGrid.destroySegPopover(),n&&"number"==typeof n&&this.dayGrid.limitRows(n),i=this.computeScrollerHeight(t),this.setGridHeight(i,e),n&&"number"!=typeof n&&this.dayGrid.limitRows(n),!e&&m(this.scrollerEl,i)&&(h(this.headRowEl,w(this.scrollerEl)),i=this.computeScrollerHeight(t),this.scrollerEl.height(i),this.restoreScroll())},setGridHeight:function(t,e){e?g(this.dayGrid.rowEls):f(this.dayGrid.rowEls,t,!0)},renderEvents:function(t){this.dayGrid.renderEvents(t),this.updateHeight(),fe.prototype.renderEvents.call(this,t)},getSegs:function(){return this.dayGrid.getSegs()},destroyEvents:function(){fe.prototype.destroyEvents.call(this),this.recordScroll(),this.dayGrid.destroyEvents()},renderDrag:function(t,e,i){return this.dayGrid.renderDrag(t,e,i)},destroyDrag:function(){this.dayGrid.destroyDrag()},renderSelection:function(t,e){this.dayGrid.renderSelection(t,e)},destroySelection:function(){this.dayGrid.destroySelection()}}),r({fixedWeekCount:!0}),xe.month=pe,pe.prototype=H(ge.prototype),t.extend(pe.prototype,{name:"month",incrementDate:function(t,e){return t.clone().stripTime().add(e,"months").startOf("month")},render:function(t){var e;this.intervalStart=t.clone().stripTime().startOf("month"),this.intervalEnd=this.intervalStart.clone().add(1,"months"),this.start=this.intervalStart.clone(),this.start=this.skipHiddenDays(this.start),this.start.startOf("week"),this.start=this.skipHiddenDays(this.start),this.end=this.intervalEnd.clone(),this.end=this.skipHiddenDays(this.end,-1,!0),this.end.add((7-this.end.weekday())%7,"days"),this.end=this.skipHiddenDays(this.end,-1,!0),e=Math.ceil(this.end.diff(this.start,"weeks",!0)),this.isFixedWeeks()&&(this.end.add(6-e,"weeks"),e=6),this.title=this.calendar.formatDate(this.intervalStart,this.opt("titleFormat")),ge.prototype.render.call(this,e,this.getCellsPerWeek(),!0)},setGridHeight:function(t,e){e=e||"variable"===this.opt("weekMode"),e&&(t*=this.rowCnt/6),f(this.dayGrid.rowEls,t,!e)},isFixedWeeks:function(){var t=this.opt("weekMode");return t?"fixed"===t:this.opt("fixedWeekCount")}}),xe.basicWeek=me,me.prototype=H(ge.prototype),t.extend(me.prototype,{name:"basicWeek",incrementDate:function(t,e){return t.clone().stripTime().add(e,"weeks").startOf("week")},render:function(t){this.intervalStart=t.clone().stripTime().startOf("week"),this.intervalEnd=this.intervalStart.clone().add(1,"weeks"),this.start=this.skipHiddenDays(this.intervalStart),this.end=this.skipHiddenDays(this.intervalEnd,-1,!0),this.title=this.calendar.formatRange(this.start,this.end.clone().subtract(1),this.opt("titleFormat")," — "),ge.prototype.render.call(this,1,this.getCellsPerWeek(),!1)}}),xe.basicDay=ve,ve.prototype=H(ge.prototype),t.extend(ve.prototype,{name:"basicDay",incrementDate:function(t,e){var i=t.clone().stripTime().add(e,"days");return i=this.skipHiddenDays(i,0>e?-1:1)},render:function(t){this.start=this.intervalStart=t.clone().stripTime(),this.end=this.intervalEnd=this.start.clone().add(1,"days"),this.title=this.calendar.formatDate(this.start,this.opt("titleFormat")),ge.prototype.render.call(this,1,1,!1)}}),r({allDaySlot:!0,allDayText:"all-day",scrollTime:"06:00:00",slotDuration:"00:30:00",axisFormat:ye,timeFormat:{agenda:we},minTime:"00:00:00",maxTime:"24:00:00",slotEventOverlap:!0});var Ye=5;Ee.prototype=H(fe.prototype),t.extend(Ee.prototype,{timeGrid:null,dayGrid:null,axisWidth:null,noScrollRowEls:null,bottomRuleEl:null,bottomRuleHeight:null,render:function(e){this.rowCnt=1,this.colCnt=e,this.el.addClass("fc-agenda-view").html(this.renderHtml()),this.scrollerEl=this.el.find(".fc-time-grid-container"),this.timeGrid.coordMap.containerEl=this.scrollerEl,this.timeGrid.el=this.el.find(".fc-time-grid"),this.timeGrid.render(),this.bottomRuleEl=t('
').appendTo(this.timeGrid.el),this.dayGrid&&(this.dayGrid.el=this.el.find(".fc-day-grid"),this.dayGrid.render(),this.dayGrid.bottomCoordPadding=this.dayGrid.el.next("hr").outerHeight()),this.noScrollRowEls=this.el.find(".fc-row:not(.fc-scroller *)"),fe.prototype.render.call(this),this.resetScroll()},destroy:function(){this.timeGrid.destroy(),this.dayGrid&&this.dayGrid.destroy(),fe.prototype.destroy.call(this)},renderHtml:function(){return'"+""+""+""+""+'"+""+""+"
'+this.timeGrid.headHtml()+"
'+(this.dayGrid?'

':"")+'
'+'
'+"
"+"
"},headIntroHtml:function(){var t,e,i,n;return this.opt("weekNumbers")?(t=this.cellToDate(0,0),e=this.calendar.calculateWeekNumber(t),i=this.opt("weekNumberTitle"),n=this.opt("isRTL")?e+i:i+e,'
"+""+R(n)+""+""+""+(this.opt("allDayHtml")||R(this.opt("allDayText")))+""+"