fullcalendar-rails 2.1.1.0 → 2.2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -0
- data/lib/fullcalendar-rails/version.rb +1 -1
- data/vendor/assets/javascripts/fullcalendar.js +1340 -470
- data/vendor/assets/javascripts/fullcalendar/gcal.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang-all.js +2 -2
- data/vendor/assets/javascripts/fullcalendar/lang/ar.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/cs.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/de-at.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/de.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/el.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/es.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/fa.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/fi.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/fr.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/hu.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/is.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/ko.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/lt.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/lv.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/nl.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/pl.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/ro.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/sk.js +1 -1
- data/vendor/assets/javascripts/fullcalendar/lang/sr-cyrl.js +1 -1
- data/vendor/assets/stylesheets/fullcalendar.css +58 -14
- data/vendor/assets/stylesheets/fullcalendar.print.css +2 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39c7834b7ba126f3242c0091c7dac7977af0a556
|
4
|
+
data.tar.gz: eae6565cfcf9f0f662dac7e1c0c732995129e9ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb227fa10b55e0c192e77e16351084eb38d5aadcc4530f73096488af5ec63617e34aade1599b199d713d415dce827015b39a525b6b14da807635ba96831852e7
|
7
|
+
data.tar.gz: 9a5f726f28ba0f22c9d9efd9ca21189d56f44b06b8e60a70b7c15e55adcdf5554dbfe47aea91e08399a65a986f9826219395947827d57b8491028d7282e5bc39
|
data/README.md
CHANGED
@@ -23,6 +23,16 @@ If you need a specific version of FullCalendar (e.g X.Y.Z), you can explicitly r
|
|
23
23
|
|
24
24
|
(Note that the last number ("0" in the line above) indicates the release of this gem, so it may change for the same version of FullCalender, see Versioning section below)
|
25
25
|
|
26
|
+
Since version 2.1.1.0 of this gem, the gems `jquery-rails` and `momentjs-rails` are included as dependencies. If you copied moment.js manually into your project, you have to delete the file so it doesn't clash with the required version from the gem. You have to also add
|
27
|
+
|
28
|
+
gem 'momentjs-rails'
|
29
|
+
|
30
|
+
to your Gemfile and
|
31
|
+
|
32
|
+
//= require moment
|
33
|
+
|
34
|
+
to yout application.js manifest.
|
35
|
+
|
26
36
|
Finally execute:
|
27
37
|
|
28
38
|
$ bundle
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*!
|
2
|
-
* FullCalendar v2.
|
2
|
+
* FullCalendar v2.2.0
|
3
3
|
* Docs & License: http://arshaw.com/fullcalendar/
|
4
4
|
* (c) 2013 Adam Shaw
|
5
5
|
*/
|
@@ -174,7 +174,7 @@ var rtlDefaults = {
|
|
174
174
|
|
175
175
|
;;
|
176
176
|
|
177
|
-
var fc = $.fullCalendar = { version: "2.
|
177
|
+
var fc = $.fullCalendar = { version: "2.2.0" };
|
178
178
|
var fcViews = fc.views = {};
|
179
179
|
|
180
180
|
|
@@ -1321,7 +1321,7 @@ function EventManager(options) { // assumed to be a calendar
|
|
1321
1321
|
var currentFetchID = 0;
|
1322
1322
|
var pendingSourceCnt = 0;
|
1323
1323
|
var loadingLevel = 0;
|
1324
|
-
var cache = [];
|
1324
|
+
var cache = []; // holds events that have already been expanded
|
1325
1325
|
|
1326
1326
|
|
1327
1327
|
$.each(
|
@@ -1362,24 +1362,29 @@ function EventManager(options) { // assumed to be a calendar
|
|
1362
1362
|
|
1363
1363
|
|
1364
1364
|
function fetchEventSource(source, fetchID) {
|
1365
|
-
_fetchEventSource(source, function(
|
1365
|
+
_fetchEventSource(source, function(eventInputs) {
|
1366
1366
|
var isArraySource = $.isArray(source.events);
|
1367
|
-
var i;
|
1368
|
-
var
|
1367
|
+
var i, eventInput;
|
1368
|
+
var abstractEvent;
|
1369
1369
|
|
1370
1370
|
if (fetchID == currentFetchID) {
|
1371
1371
|
|
1372
|
-
if (
|
1373
|
-
for (i=0; i<
|
1374
|
-
|
1372
|
+
if (eventInputs) {
|
1373
|
+
for (i = 0; i < eventInputs.length; i++) {
|
1374
|
+
eventInput = eventInputs[i];
|
1375
1375
|
|
1376
|
-
//
|
1377
|
-
|
1378
|
-
|
1376
|
+
if (isArraySource) { // array sources have already been convert to Event Objects
|
1377
|
+
abstractEvent = eventInput;
|
1378
|
+
}
|
1379
|
+
else {
|
1380
|
+
abstractEvent = buildEventFromInput(eventInput, source);
|
1379
1381
|
}
|
1380
1382
|
|
1381
|
-
if (
|
1382
|
-
cache.push(
|
1383
|
+
if (abstractEvent) { // not false (an invalid event)
|
1384
|
+
cache.push.apply(
|
1385
|
+
cache,
|
1386
|
+
expandEvent(abstractEvent) // add individual expanded events to the cache
|
1387
|
+
);
|
1383
1388
|
}
|
1384
1389
|
}
|
1385
1390
|
}
|
@@ -1550,7 +1555,7 @@ function EventManager(options) { // assumed to be a calendar
|
|
1550
1555
|
if ($.isArray(source.events)) {
|
1551
1556
|
source.origArray = source.events; // for removeEventSource
|
1552
1557
|
source.events = $.map(source.events, function(eventInput) {
|
1553
|
-
return
|
1558
|
+
return buildEventFromInput(eventInput, source);
|
1554
1559
|
});
|
1555
1560
|
}
|
1556
1561
|
|
@@ -1640,19 +1645,33 @@ function EventManager(options) { // assumed to be a calendar
|
|
1640
1645
|
}
|
1641
1646
|
|
1642
1647
|
|
1643
|
-
|
1644
|
-
function renderEvent(
|
1645
|
-
var
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1648
|
+
// returns the expanded events that were created
|
1649
|
+
function renderEvent(eventInput, stick) {
|
1650
|
+
var abstractEvent = buildEventFromInput(eventInput);
|
1651
|
+
var events;
|
1652
|
+
var i, event;
|
1653
|
+
|
1654
|
+
if (abstractEvent) { // not false (a valid input)
|
1655
|
+
events = expandEvent(abstractEvent);
|
1656
|
+
|
1657
|
+
for (i = 0; i < events.length; i++) {
|
1658
|
+
event = events[i];
|
1659
|
+
|
1660
|
+
if (!event.source) {
|
1661
|
+
if (stick) {
|
1662
|
+
stickySource.events.push(event);
|
1663
|
+
event.source = stickySource;
|
1664
|
+
}
|
1665
|
+
cache.push(event);
|
1651
1666
|
}
|
1652
|
-
cache.push(event);
|
1653
1667
|
}
|
1668
|
+
|
1654
1669
|
reportEvents(cache);
|
1670
|
+
|
1671
|
+
return events;
|
1655
1672
|
}
|
1673
|
+
|
1674
|
+
return [];
|
1656
1675
|
}
|
1657
1676
|
|
1658
1677
|
|
@@ -1723,49 +1742,108 @@ function EventManager(options) { // assumed to be a calendar
|
|
1723
1742
|
/* Event Normalization
|
1724
1743
|
-----------------------------------------------------------------------------*/
|
1725
1744
|
|
1726
|
-
|
1745
|
+
|
1746
|
+
// Given a raw object with key/value properties, returns an "abstract" Event object.
|
1747
|
+
// An "abstract" event is an event that, if recurring, will not have been expanded yet.
|
1748
|
+
// Will return `false` when input is invalid.
|
1749
|
+
// `source` is optional
|
1750
|
+
function buildEventFromInput(input, source) {
|
1727
1751
|
var out = {};
|
1728
|
-
var start;
|
1729
|
-
var end;
|
1752
|
+
var start, end;
|
1730
1753
|
var allDay;
|
1731
1754
|
var allDayDefault;
|
1732
1755
|
|
1733
1756
|
if (options.eventDataTransform) {
|
1734
|
-
|
1757
|
+
input = options.eventDataTransform(input);
|
1735
1758
|
}
|
1736
1759
|
if (source && source.eventDataTransform) {
|
1737
|
-
|
1760
|
+
input = source.eventDataTransform(input);
|
1738
1761
|
}
|
1739
1762
|
|
1740
|
-
|
1741
|
-
|
1742
|
-
|
1763
|
+
// Copy all properties over to the resulting object.
|
1764
|
+
// The special-case properties will be copied over afterwards.
|
1765
|
+
$.extend(out, input);
|
1766
|
+
|
1767
|
+
if (source) {
|
1768
|
+
out.source = source;
|
1743
1769
|
}
|
1744
1770
|
|
1745
|
-
|
1746
|
-
|
1747
|
-
|
1748
|
-
if (
|
1749
|
-
|
1771
|
+
out._id = input._id || (input.id === undefined ? '_fc' + eventGUID++ : input.id + '');
|
1772
|
+
|
1773
|
+
if (input.className) {
|
1774
|
+
if (typeof input.className == 'string') {
|
1775
|
+
out.className = input.className.split(/\s+/);
|
1776
|
+
}
|
1777
|
+
else { // assumed to be an array
|
1778
|
+
out.className = input.className;
|
1750
1779
|
}
|
1751
1780
|
}
|
1781
|
+
else {
|
1782
|
+
out.className = [];
|
1783
|
+
}
|
1752
1784
|
|
1753
|
-
|
1754
|
-
|
1755
|
-
|
1756
|
-
|
1757
|
-
|
1758
|
-
);
|
1759
|
-
|
1760
|
-
|
1761
|
-
|
1785
|
+
start = input.start || input.date; // "date" is an alias for "start"
|
1786
|
+
end = input.end;
|
1787
|
+
|
1788
|
+
// parse as a time (Duration) if applicable
|
1789
|
+
if (isTimeString(start)) {
|
1790
|
+
start = moment.duration(start);
|
1791
|
+
}
|
1792
|
+
if (isTimeString(end)) {
|
1793
|
+
end = moment.duration(end);
|
1794
|
+
}
|
1795
|
+
|
1796
|
+
if (input.dow || moment.isDuration(start) || moment.isDuration(end)) {
|
1797
|
+
|
1798
|
+
// the event is "abstract" (recurring) so don't calculate exact start/end dates just yet
|
1799
|
+
out.start = start ? moment.duration(start) : null; // will be a Duration or null
|
1800
|
+
out.end = end ? moment.duration(end) : null; // will be a Duration or null
|
1801
|
+
out._recurring = true; // our internal marker
|
1802
|
+
}
|
1803
|
+
else {
|
1804
|
+
|
1805
|
+
if (start) {
|
1806
|
+
start = t.moment(start);
|
1807
|
+
if (!start.isValid()) {
|
1808
|
+
return false;
|
1809
|
+
}
|
1762
1810
|
}
|
1763
|
-
|
1764
|
-
|
1765
|
-
|
1811
|
+
|
1812
|
+
if (end) {
|
1813
|
+
end = t.moment(end);
|
1814
|
+
if (!end.isValid()) {
|
1815
|
+
return false;
|
1816
|
+
}
|
1817
|
+
}
|
1818
|
+
|
1819
|
+
allDay = input.allDay;
|
1820
|
+
if (allDay === undefined) {
|
1821
|
+
allDayDefault = firstDefined(
|
1822
|
+
source ? source.allDayDefault : undefined,
|
1823
|
+
options.allDayDefault
|
1824
|
+
);
|
1825
|
+
if (allDayDefault !== undefined) {
|
1826
|
+
// use the default
|
1827
|
+
allDay = allDayDefault;
|
1828
|
+
}
|
1829
|
+
else {
|
1830
|
+
// if a single date has a time, the event should not be all-day
|
1831
|
+
allDay = !start.hasTime() && (!end || !end.hasTime());
|
1832
|
+
}
|
1766
1833
|
}
|
1834
|
+
|
1835
|
+
assignDatesToEvent(start, end, allDay, out);
|
1767
1836
|
}
|
1768
1837
|
|
1838
|
+
return out;
|
1839
|
+
}
|
1840
|
+
|
1841
|
+
|
1842
|
+
// Normalizes and assigns the given dates to the given partially-formed event object.
|
1843
|
+
// Requires an explicit `allDay` boolean parameter.
|
1844
|
+
// NOTE: mutates the given start/end moments. does not make an internal copy
|
1845
|
+
function assignDatesToEvent(start, end, allDay, event) {
|
1846
|
+
|
1769
1847
|
// normalize the date based on allDay
|
1770
1848
|
if (allDay) {
|
1771
1849
|
// neither date should have a time
|
@@ -1786,39 +1864,88 @@ function EventManager(options) { // assumed to be a calendar
|
|
1786
1864
|
}
|
1787
1865
|
}
|
1788
1866
|
|
1789
|
-
|
1790
|
-
|
1791
|
-
|
1867
|
+
event.allDay = allDay;
|
1868
|
+
event.start = start;
|
1869
|
+
event.end = end || null; // ensure null if falsy
|
1792
1870
|
|
1793
|
-
if (
|
1794
|
-
|
1871
|
+
if (options.forceEventDuration && !event.end) {
|
1872
|
+
event.end = getEventEnd(event);
|
1795
1873
|
}
|
1796
1874
|
|
1797
|
-
|
1875
|
+
backupEventDates(event);
|
1876
|
+
}
|
1798
1877
|
|
1799
|
-
if (data.className) {
|
1800
|
-
if (typeof data.className == 'string') {
|
1801
|
-
out.className = data.className.split(/\s+/);
|
1802
|
-
}
|
1803
|
-
else { // assumed to be an array
|
1804
|
-
out.className = data.className;
|
1805
|
-
}
|
1806
|
-
}
|
1807
|
-
else {
|
1808
|
-
out.className = [];
|
1809
|
-
}
|
1810
1878
|
|
1811
|
-
|
1812
|
-
|
1813
|
-
|
1879
|
+
// If the given event is a recurring event, break it down into an array of individual instances.
|
1880
|
+
// If not a recurring event, return an array with the single original event.
|
1881
|
+
// If given a falsy input (probably because of a failed buildEventFromInput call), returns an empty array.
|
1882
|
+
function expandEvent(abstractEvent) {
|
1883
|
+
var events = [];
|
1884
|
+
var view;
|
1885
|
+
var _rangeStart = rangeStart;
|
1886
|
+
var _rangeEnd = rangeEnd;
|
1887
|
+
var dowHash;
|
1888
|
+
var dow;
|
1889
|
+
var i;
|
1890
|
+
var date;
|
1891
|
+
var startTime, endTime;
|
1892
|
+
var start, end;
|
1893
|
+
var event;
|
1814
1894
|
|
1815
|
-
|
1816
|
-
|
1895
|
+
// hack for when fetchEvents hasn't been called yet (calculating businessHours for example)
|
1896
|
+
if (!_rangeStart || !_rangeEnd) {
|
1897
|
+
view = t.getView();
|
1898
|
+
_rangeStart = view.start;
|
1899
|
+
_rangeEnd = view.end;
|
1817
1900
|
}
|
1818
1901
|
|
1819
|
-
|
1902
|
+
if (abstractEvent) {
|
1903
|
+
if (abstractEvent._recurring) {
|
1820
1904
|
|
1821
|
-
|
1905
|
+
// make a boolean hash as to whether the event occurs on each day-of-week
|
1906
|
+
if ((dow = abstractEvent.dow)) {
|
1907
|
+
dowHash = {};
|
1908
|
+
for (i = 0; i < dow.length; i++) {
|
1909
|
+
dowHash[dow[i]] = true;
|
1910
|
+
}
|
1911
|
+
}
|
1912
|
+
|
1913
|
+
// iterate through every day in the current range
|
1914
|
+
date = _rangeStart.clone().stripTime(); // holds the date of the current day
|
1915
|
+
while (date.isBefore(_rangeEnd)) {
|
1916
|
+
|
1917
|
+
if (!dowHash || dowHash[date.day()]) { // if everyday, or this particular day-of-week
|
1918
|
+
|
1919
|
+
startTime = abstractEvent.start; // the stored start and end properties are times (Durations)
|
1920
|
+
endTime = abstractEvent.end; // "
|
1921
|
+
start = date.clone();
|
1922
|
+
end = null;
|
1923
|
+
|
1924
|
+
if (startTime) {
|
1925
|
+
start = start.time(startTime);
|
1926
|
+
}
|
1927
|
+
if (endTime) {
|
1928
|
+
end = date.clone().time(endTime);
|
1929
|
+
}
|
1930
|
+
|
1931
|
+
event = $.extend({}, abstractEvent); // make a copy of the original
|
1932
|
+
assignDatesToEvent(
|
1933
|
+
start, end,
|
1934
|
+
!startTime && !endTime, // allDay?
|
1935
|
+
event
|
1936
|
+
);
|
1937
|
+
events.push(event);
|
1938
|
+
}
|
1939
|
+
|
1940
|
+
date.add(1, 'days');
|
1941
|
+
}
|
1942
|
+
}
|
1943
|
+
else {
|
1944
|
+
events.push(abstractEvent); // return the original event. will be a one-item array
|
1945
|
+
}
|
1946
|
+
}
|
1947
|
+
|
1948
|
+
return events;
|
1822
1949
|
}
|
1823
1950
|
|
1824
1951
|
|
@@ -1995,6 +2122,202 @@ function EventManager(options) { // assumed to be a calendar
|
|
1995
2122
|
};
|
1996
2123
|
}
|
1997
2124
|
|
2125
|
+
|
2126
|
+
/* Business Hours
|
2127
|
+
-----------------------------------------------------------------------------------------*/
|
2128
|
+
|
2129
|
+
t.getBusinessHoursEvents = getBusinessHoursEvents;
|
2130
|
+
|
2131
|
+
|
2132
|
+
// Returns an array of events as to when the business hours occur in the current view.
|
2133
|
+
// Abuse of our event system :(
|
2134
|
+
function getBusinessHoursEvents() {
|
2135
|
+
var optionVal = options.businessHours;
|
2136
|
+
var defaultVal = {
|
2137
|
+
className: 'fc-nonbusiness',
|
2138
|
+
start: '09:00',
|
2139
|
+
end: '17:00',
|
2140
|
+
dow: [ 1, 2, 3, 4, 5 ], // monday - friday
|
2141
|
+
rendering: 'inverse-background'
|
2142
|
+
};
|
2143
|
+
var eventInput;
|
2144
|
+
|
2145
|
+
if (optionVal) {
|
2146
|
+
if (typeof optionVal === 'object') {
|
2147
|
+
// option value is an object that can override the default business hours
|
2148
|
+
eventInput = $.extend({}, defaultVal, optionVal);
|
2149
|
+
}
|
2150
|
+
else {
|
2151
|
+
// option value is `true`. use default business hours
|
2152
|
+
eventInput = defaultVal;
|
2153
|
+
}
|
2154
|
+
}
|
2155
|
+
|
2156
|
+
if (eventInput) {
|
2157
|
+
return expandEvent(buildEventFromInput(eventInput));
|
2158
|
+
}
|
2159
|
+
|
2160
|
+
return [];
|
2161
|
+
}
|
2162
|
+
|
2163
|
+
|
2164
|
+
/* Overlapping / Constraining
|
2165
|
+
-----------------------------------------------------------------------------------------*/
|
2166
|
+
|
2167
|
+
t.isEventAllowedInRange = isEventAllowedInRange;
|
2168
|
+
t.isSelectionAllowedInRange = isSelectionAllowedInRange;
|
2169
|
+
t.isExternalDragAllowedInRange = isExternalDragAllowedInRange;
|
2170
|
+
|
2171
|
+
|
2172
|
+
function isEventAllowedInRange(event, start, end) {
|
2173
|
+
var source = event.source || {};
|
2174
|
+
var constraint = firstDefined(
|
2175
|
+
event.constraint,
|
2176
|
+
source.constraint,
|
2177
|
+
options.eventConstraint
|
2178
|
+
);
|
2179
|
+
var overlap = firstDefined(
|
2180
|
+
event.overlap,
|
2181
|
+
source.overlap,
|
2182
|
+
options.eventOverlap
|
2183
|
+
);
|
2184
|
+
|
2185
|
+
return isRangeAllowed(start, end, constraint, overlap, event);
|
2186
|
+
}
|
2187
|
+
|
2188
|
+
|
2189
|
+
function isSelectionAllowedInRange(start, end) {
|
2190
|
+
return isRangeAllowed(
|
2191
|
+
start,
|
2192
|
+
end,
|
2193
|
+
options.selectConstraint,
|
2194
|
+
options.selectOverlap
|
2195
|
+
);
|
2196
|
+
}
|
2197
|
+
|
2198
|
+
|
2199
|
+
function isExternalDragAllowedInRange(start, end, eventInput) { // eventInput is optional associated event data
|
2200
|
+
var event;
|
2201
|
+
|
2202
|
+
if (eventInput) {
|
2203
|
+
event = expandEvent(buildEventFromInput(eventInput))[0];
|
2204
|
+
if (event) {
|
2205
|
+
return isEventAllowedInRange(event, start, end);
|
2206
|
+
}
|
2207
|
+
}
|
2208
|
+
|
2209
|
+
return isSelectionAllowedInRange(start, end); // treat it as a selection
|
2210
|
+
}
|
2211
|
+
|
2212
|
+
|
2213
|
+
// Returns true if the given range (caused by an event drop/resize or a selection) is allowed to exist
|
2214
|
+
// according to the constraint/overlap settings.
|
2215
|
+
// `event` is not required if checking a selection.
|
2216
|
+
function isRangeAllowed(start, end, constraint, overlap, event) {
|
2217
|
+
var constraintEvents;
|
2218
|
+
var anyContainment;
|
2219
|
+
var i, otherEvent;
|
2220
|
+
var otherOverlap;
|
2221
|
+
|
2222
|
+
// normalize. fyi, we're normalizing in too many places :(
|
2223
|
+
start = start.clone().stripZone();
|
2224
|
+
end = end.clone().stripZone();
|
2225
|
+
|
2226
|
+
// the range must be fully contained by at least one of produced constraint events
|
2227
|
+
if (constraint != null) {
|
2228
|
+
constraintEvents = constraintToEvents(constraint);
|
2229
|
+
anyContainment = false;
|
2230
|
+
|
2231
|
+
for (i = 0; i < constraintEvents.length; i++) {
|
2232
|
+
if (eventContainsRange(constraintEvents[i], start, end)) {
|
2233
|
+
anyContainment = true;
|
2234
|
+
break;
|
2235
|
+
}
|
2236
|
+
}
|
2237
|
+
|
2238
|
+
if (!anyContainment) {
|
2239
|
+
return false;
|
2240
|
+
}
|
2241
|
+
}
|
2242
|
+
|
2243
|
+
for (i = 0; i < cache.length; i++) { // loop all events and detect overlap
|
2244
|
+
otherEvent = cache[i];
|
2245
|
+
|
2246
|
+
// don't compare the event to itself or other related [repeating] events
|
2247
|
+
if (event && event._id === otherEvent._id) {
|
2248
|
+
continue;
|
2249
|
+
}
|
2250
|
+
|
2251
|
+
// there needs to be an actual intersection before disallowing anything
|
2252
|
+
if (eventIntersectsRange(otherEvent, start, end)) {
|
2253
|
+
|
2254
|
+
// evaluate overlap for the given range and short-circuit if necessary
|
2255
|
+
if (overlap === false) {
|
2256
|
+
return false;
|
2257
|
+
}
|
2258
|
+
else if (typeof overlap === 'function' && !overlap(otherEvent, event)) {
|
2259
|
+
return false;
|
2260
|
+
}
|
2261
|
+
|
2262
|
+
// if we are computing if the given range is allowable for an event, consider the other event's
|
2263
|
+
// EventObject-specific or Source-specific `overlap` property
|
2264
|
+
if (event) {
|
2265
|
+
otherOverlap = firstDefined(
|
2266
|
+
otherEvent.overlap,
|
2267
|
+
(otherEvent.source || {}).overlap
|
2268
|
+
// we already considered the global `eventOverlap`
|
2269
|
+
);
|
2270
|
+
if (otherOverlap === false) {
|
2271
|
+
return false;
|
2272
|
+
}
|
2273
|
+
if (typeof otherOverlap === 'function' && !otherOverlap(event, otherEvent)) {
|
2274
|
+
return false;
|
2275
|
+
}
|
2276
|
+
}
|
2277
|
+
}
|
2278
|
+
}
|
2279
|
+
|
2280
|
+
return true;
|
2281
|
+
}
|
2282
|
+
|
2283
|
+
|
2284
|
+
// Given an event input from the API, produces an array of event objects. Possible event inputs:
|
2285
|
+
// 'businessHours'
|
2286
|
+
// An event ID (number or string)
|
2287
|
+
// An object with specific start/end dates or a recurring event (like what businessHours accepts)
|
2288
|
+
function constraintToEvents(constraintInput) {
|
2289
|
+
|
2290
|
+
if (constraintInput === 'businessHours') {
|
2291
|
+
return getBusinessHoursEvents();
|
2292
|
+
}
|
2293
|
+
|
2294
|
+
if (typeof constraintInput === 'object') {
|
2295
|
+
return expandEvent(buildEventFromInput(constraintInput));
|
2296
|
+
}
|
2297
|
+
|
2298
|
+
return clientEvents(constraintInput); // probably an ID
|
2299
|
+
}
|
2300
|
+
|
2301
|
+
|
2302
|
+
// Is the event's date ranged fully contained by the given range?
|
2303
|
+
// start/end already assumed to have stripped zones :(
|
2304
|
+
function eventContainsRange(event, start, end) {
|
2305
|
+
var eventStart = event.start.clone().stripZone();
|
2306
|
+
var eventEnd = t.getEventEnd(event).stripZone();
|
2307
|
+
|
2308
|
+
return start >= eventStart && end <= eventEnd;
|
2309
|
+
}
|
2310
|
+
|
2311
|
+
|
2312
|
+
// Does the event's date range intersect with the given range?
|
2313
|
+
// start/end already assumed to have stripped zones :(
|
2314
|
+
function eventIntersectsRange(event, start, end) {
|
2315
|
+
var eventStart = event.start.clone().stripZone();
|
2316
|
+
var eventEnd = t.getEventEnd(event).stripZone();
|
2317
|
+
|
2318
|
+
return start < eventEnd && end > eventStart;
|
2319
|
+
}
|
2320
|
+
|
1998
2321
|
}
|
1999
2322
|
|
2000
2323
|
|
@@ -2040,6 +2363,18 @@ function uncompensateScroll(rowEls) {
|
|
2040
2363
|
}
|
2041
2364
|
|
2042
2365
|
|
2366
|
+
// Make the mouse cursor express that an event is not allowed in the current area
|
2367
|
+
function disableCursor() {
|
2368
|
+
$('body').addClass('fc-not-allowed');
|
2369
|
+
}
|
2370
|
+
|
2371
|
+
|
2372
|
+
// Returns the mouse cursor to its original look
|
2373
|
+
function enableCursor() {
|
2374
|
+
$('body').removeClass('fc-not-allowed');
|
2375
|
+
}
|
2376
|
+
|
2377
|
+
|
2043
2378
|
// Given a total available height to fill, have `els` (essentially child rows) expand to accomodate.
|
2044
2379
|
// By default, all elements that are shorter than the recommended height are expanded uniformly, not considering
|
2045
2380
|
// any other els that are already too tall. if `shouldRedistribute` is on, it considers these tall rows and
|
@@ -2269,6 +2604,12 @@ function dateCompare(a, b) { // works with Moments and native Dates
|
|
2269
2604
|
}
|
2270
2605
|
|
2271
2606
|
|
2607
|
+
// Returns a boolean about whether the given input is a time string, like "06:40:00" or "06:00"
|
2608
|
+
function isTimeString(str) {
|
2609
|
+
return /^\d+\:\d+(?:\:\d+\.?(?:\d{3})?)?$/.test(str);
|
2610
|
+
}
|
2611
|
+
|
2612
|
+
|
2272
2613
|
/* General Utilities
|
2273
2614
|
----------------------------------------------------------------------------------------------------------------------*/
|
2274
2615
|
|
@@ -2283,17 +2624,6 @@ function createObject(proto) {
|
|
2283
2624
|
}
|
2284
2625
|
|
2285
2626
|
|
2286
|
-
// Copies specifically-owned (non-protoype) properties of `b` onto `a`.
|
2287
|
-
// FYI, $.extend would copy *all* properties of `b` onto `a`.
|
2288
|
-
function extend(a, b) {
|
2289
|
-
for (var i in b) {
|
2290
|
-
if (b.hasOwnProperty(i)) {
|
2291
|
-
a[i] = b[i];
|
2292
|
-
}
|
2293
|
-
}
|
2294
|
-
}
|
2295
|
-
|
2296
|
-
|
2297
2627
|
function applyAll(functions, thisObj, args) {
|
2298
2628
|
if ($.isFunction(functions)) {
|
2299
2629
|
functions = [ functions ];
|
@@ -2376,13 +2706,18 @@ function debounce(func, wait) {
|
|
2376
2706
|
var ambigDateOfMonthRegex = /^\s*\d{4}-\d\d$/;
|
2377
2707
|
var ambigTimeOrZoneRegex =
|
2378
2708
|
/^\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+)?)?)?)?)?$/;
|
2709
|
+
var newMomentProto = moment.fn; // where we will attach our new methods
|
2710
|
+
var oldMomentProto = $.extend({}, newMomentProto); // copy of original moment methods
|
2711
|
+
var allowValueOptimization;
|
2712
|
+
var setUTCValues; // function defined below
|
2713
|
+
var setLocalValues; // function defined below
|
2379
2714
|
|
2380
2715
|
|
2381
2716
|
// Creating
|
2382
2717
|
// -------------------------------------------------------------------------------------------------
|
2383
2718
|
|
2384
2719
|
// Creates a new moment, similar to the vanilla moment(...) constructor, but with
|
2385
|
-
// extra features (ambiguous time, enhanced formatting). When
|
2720
|
+
// extra features (ambiguous time, enhanced formatting). When given an existing moment,
|
2386
2721
|
// it will function as a clone (and retain the zone of the moment). Anything else will
|
2387
2722
|
// result in a moment in the local zone.
|
2388
2723
|
fc.moment = function() {
|
@@ -2393,7 +2728,8 @@ fc.moment = function() {
|
|
2393
2728
|
fc.moment.utc = function() {
|
2394
2729
|
var mom = makeMoment(arguments, true);
|
2395
2730
|
|
2396
|
-
// Force it into UTC because makeMoment doesn't guarantee it
|
2731
|
+
// Force it into UTC because makeMoment doesn't guarantee it
|
2732
|
+
// (if given a pre-existing moment for example)
|
2397
2733
|
if (mom.hasTime()) { // don't give ambiguously-timed moments a UTC zone
|
2398
2734
|
mom.utc();
|
2399
2735
|
}
|
@@ -2407,8 +2743,8 @@ fc.moment.parseZone = function() {
|
|
2407
2743
|
return makeMoment(arguments, true, true);
|
2408
2744
|
};
|
2409
2745
|
|
2410
|
-
// Builds an
|
2411
|
-
// Date, or called with no arguments (the current time), the resulting moment will be local.
|
2746
|
+
// Builds an enhanced moment from args. When given an existing moment, it clones. When given a
|
2747
|
+
// native Date, or called with no arguments (the current time), the resulting moment will be local.
|
2412
2748
|
// Anything else needs to be "parsed" (a string or an array), and will be affected by:
|
2413
2749
|
// parseAsUTC - if there is no zone information, should we parse the input in UTC?
|
2414
2750
|
// parseZone - if there is zone information, should we force the zone of the moment?
|
@@ -2418,21 +2754,14 @@ function makeMoment(args, parseAsUTC, parseZone) {
|
|
2418
2754
|
var isAmbigTime;
|
2419
2755
|
var isAmbigZone;
|
2420
2756
|
var ambigMatch;
|
2421
|
-
var
|
2757
|
+
var mom;
|
2422
2758
|
|
2423
2759
|
if (moment.isMoment(input)) {
|
2424
|
-
|
2425
|
-
|
2426
|
-
// the ambig properties have not been preserved in the clone, so reassign them
|
2427
|
-
if (input._ambigTime) {
|
2428
|
-
output._ambigTime = true;
|
2429
|
-
}
|
2430
|
-
if (input._ambigZone) {
|
2431
|
-
output._ambigZone = true;
|
2432
|
-
}
|
2760
|
+
mom = moment.apply(null, args); // clone it
|
2761
|
+
transferAmbigs(input, mom); // the ambig flags weren't transfered with the clone
|
2433
2762
|
}
|
2434
2763
|
else if (isNativeDate(input) || input === undefined) {
|
2435
|
-
|
2764
|
+
mom = moment.apply(null, args); // will be local
|
2436
2765
|
}
|
2437
2766
|
else { // "parsing" is required
|
2438
2767
|
isAmbigTime = false;
|
@@ -2458,44 +2787,44 @@ function makeMoment(args, parseAsUTC, parseZone) {
|
|
2458
2787
|
// otherwise, probably a string with a format
|
2459
2788
|
|
2460
2789
|
if (parseAsUTC) {
|
2461
|
-
|
2790
|
+
mom = moment.utc.apply(moment, args);
|
2462
2791
|
}
|
2463
2792
|
else {
|
2464
|
-
|
2793
|
+
mom = moment.apply(null, args);
|
2465
2794
|
}
|
2466
2795
|
|
2467
2796
|
if (isAmbigTime) {
|
2468
|
-
|
2469
|
-
|
2797
|
+
mom._ambigTime = true;
|
2798
|
+
mom._ambigZone = true; // ambiguous time always means ambiguous zone
|
2470
2799
|
}
|
2471
2800
|
else if (parseZone) { // let's record the inputted zone somehow
|
2472
2801
|
if (isAmbigZone) {
|
2473
|
-
|
2802
|
+
mom._ambigZone = true;
|
2474
2803
|
}
|
2475
2804
|
else if (isSingleString) {
|
2476
|
-
|
2805
|
+
mom.zone(input); // if not a valid zone, will assign UTC
|
2477
2806
|
}
|
2478
2807
|
}
|
2479
2808
|
}
|
2480
2809
|
|
2481
|
-
|
2482
|
-
}
|
2810
|
+
mom._fullCalendar = true; // flag for extended functionality
|
2483
2811
|
|
2484
|
-
|
2485
|
-
// Accepts an object with the internal Moment properties that should be copied over to
|
2486
|
-
// `this` object (most likely another Moment object). The values in this data must not
|
2487
|
-
// be referenced by anything else (two moments sharing a Date object for example).
|
2488
|
-
function FCMoment(internalData) {
|
2489
|
-
extend(this, internalData);
|
2812
|
+
return mom;
|
2490
2813
|
}
|
2491
2814
|
|
2492
|
-
// Chain the prototype to Moment's
|
2493
|
-
FCMoment.prototype = createObject(moment.fn);
|
2494
2815
|
|
2495
|
-
//
|
2496
|
-
//
|
2497
|
-
|
2498
|
-
|
2816
|
+
// A clone method that works with the flags related to our enhanced functionality.
|
2817
|
+
// In the future, use moment.momentProperties
|
2818
|
+
newMomentProto.clone = function() {
|
2819
|
+
var mom = oldMomentProto.clone.apply(this, arguments);
|
2820
|
+
|
2821
|
+
// these flags weren't transfered with the clone
|
2822
|
+
transferAmbigs(this, mom);
|
2823
|
+
if (this._fullCalendar) {
|
2824
|
+
mom._fullCalendar = true;
|
2825
|
+
}
|
2826
|
+
|
2827
|
+
return mom;
|
2499
2828
|
};
|
2500
2829
|
|
2501
2830
|
|
@@ -2509,7 +2838,14 @@ FCMoment.prototype.clone = function() {
|
|
2509
2838
|
// SETTER
|
2510
2839
|
// You can supply a Duration, a Moment, or a Duration-like argument.
|
2511
2840
|
// When setting the time, and the moment has an ambiguous time, it then becomes unambiguous.
|
2512
|
-
|
2841
|
+
newMomentProto.time = function(time) {
|
2842
|
+
|
2843
|
+
// Fallback to the original method (if there is one) if this moment wasn't created via FullCalendar.
|
2844
|
+
// `time` is a generic enough method name where this precaution is necessary to avoid collisions w/ other plugins.
|
2845
|
+
if (!this._fullCalendar) {
|
2846
|
+
return oldMomentProto.time.apply(this, arguments);
|
2847
|
+
}
|
2848
|
+
|
2513
2849
|
if (time == null) { // getter
|
2514
2850
|
return moment.duration({
|
2515
2851
|
hours: this.hours(),
|
@@ -2520,7 +2856,7 @@ FCMoment.prototype.time = function(time) {
|
|
2520
2856
|
}
|
2521
2857
|
else { // setter
|
2522
2858
|
|
2523
|
-
|
2859
|
+
this._ambigTime = false; // mark that the moment now has a time
|
2524
2860
|
|
2525
2861
|
if (!moment.isDuration(time) && !moment.isMoment(time)) {
|
2526
2862
|
time = moment.duration(time);
|
@@ -2545,22 +2881,14 @@ FCMoment.prototype.time = function(time) {
|
|
2545
2881
|
// Converts the moment to UTC, stripping out its time-of-day and timezone offset,
|
2546
2882
|
// but preserving its YMD. A moment with a stripped time will display no time
|
2547
2883
|
// nor timezone offset when .format() is called.
|
2548
|
-
|
2884
|
+
newMomentProto.stripTime = function() {
|
2549
2885
|
var a = this.toArray(); // year,month,date,hours,minutes,seconds as an array
|
2550
2886
|
|
2551
|
-
// set the internal UTC flag
|
2552
|
-
|
2887
|
+
this.utc(); // set the internal UTC flag (will clear the ambig flags)
|
2888
|
+
setUTCValues(this, a.slice(0, 3)); // set the year/month/date. time will be zero
|
2553
2889
|
|
2554
|
-
|
2555
|
-
|
2556
|
-
.date(a[2])
|
2557
|
-
.hours(0)
|
2558
|
-
.minutes(0)
|
2559
|
-
.seconds(0)
|
2560
|
-
.milliseconds(0);
|
2561
|
-
|
2562
|
-
// Mark the time as ambiguous. This needs to happen after the .utc() call, which calls .zone(), which
|
2563
|
-
// clears all ambig flags. Same concept with the .year/month/date calls in the case of moment-timezone.
|
2890
|
+
// Mark the time as ambiguous. This needs to happen after the .utc() call, which calls .zone(),
|
2891
|
+
// which clears all ambig flags. Same with setUTCValues with moment-timezone.
|
2564
2892
|
this._ambigTime = true;
|
2565
2893
|
this._ambigZone = true; // if ambiguous time, also ambiguous timezone offset
|
2566
2894
|
|
@@ -2568,7 +2896,7 @@ FCMoment.prototype.stripTime = function() {
|
|
2568
2896
|
};
|
2569
2897
|
|
2570
2898
|
// Returns if the moment has a non-ambiguous time (boolean)
|
2571
|
-
|
2899
|
+
newMomentProto.hasTime = function() {
|
2572
2900
|
return !this._ambigTime;
|
2573
2901
|
};
|
2574
2902
|
|
@@ -2579,111 +2907,84 @@ FCMoment.prototype.hasTime = function() {
|
|
2579
2907
|
// Converts the moment to UTC, stripping out its timezone offset, but preserving its
|
2580
2908
|
// YMD and time-of-day. A moment with a stripped timezone offset will display no
|
2581
2909
|
// timezone offset when .format() is called.
|
2582
|
-
|
2910
|
+
newMomentProto.stripZone = function() {
|
2583
2911
|
var a = this.toArray(); // year,month,date,hours,minutes,seconds as an array
|
2584
2912
|
var wasAmbigTime = this._ambigTime;
|
2585
2913
|
|
2586
|
-
|
2587
|
-
|
2588
|
-
this.year(a[0]) // TODO: find a way to do this in one shot
|
2589
|
-
.month(a[1])
|
2590
|
-
.date(a[2])
|
2591
|
-
.hours(a[3])
|
2592
|
-
.minutes(a[4])
|
2593
|
-
.seconds(a[5])
|
2594
|
-
.milliseconds(a[6]);
|
2914
|
+
this.utc(); // set the internal UTC flag (will clear the ambig flags)
|
2915
|
+
setUTCValues(this, a); // will set the year/month/date/hours/minutes/seconds/ms
|
2595
2916
|
|
2596
2917
|
if (wasAmbigTime) {
|
2597
2918
|
// the above call to .utc()/.zone() unfortunately clears the ambig flags, so reassign
|
2598
2919
|
this._ambigTime = true;
|
2599
2920
|
}
|
2600
2921
|
|
2601
|
-
// Mark the zone as ambiguous. This needs to happen after the .utc() call, which calls .zone(),
|
2602
|
-
// clears all ambig flags. Same
|
2922
|
+
// Mark the zone as ambiguous. This needs to happen after the .utc() call, which calls .zone(),
|
2923
|
+
// which clears all ambig flags. Same with setUTCValues with moment-timezone.
|
2603
2924
|
this._ambigZone = true;
|
2604
2925
|
|
2605
2926
|
return this; // for chaining
|
2606
2927
|
};
|
2607
2928
|
|
2608
2929
|
// Returns of the moment has a non-ambiguous timezone offset (boolean)
|
2609
|
-
|
2930
|
+
newMomentProto.hasZone = function() {
|
2610
2931
|
return !this._ambigZone;
|
2611
2932
|
};
|
2612
2933
|
|
2613
|
-
// this method implicitly marks a zone
|
2614
|
-
|
2934
|
+
// this method implicitly marks a zone (will get called upon .utc() and .local())
|
2935
|
+
newMomentProto.zone = function(tzo) {
|
2615
2936
|
|
2616
|
-
if (tzo != null) {
|
2617
|
-
//
|
2618
|
-
//
|
2619
|
-
|
2620
|
-
|
2937
|
+
if (tzo != null) { // setter
|
2938
|
+
// these assignments needs to happen before the original zone method is called.
|
2939
|
+
// I forget why, something to do with a browser crash.
|
2940
|
+
this._ambigTime = false;
|
2941
|
+
this._ambigZone = false;
|
2621
2942
|
}
|
2622
2943
|
|
2623
|
-
return
|
2944
|
+
return oldMomentProto.zone.apply(this, arguments);
|
2624
2945
|
};
|
2625
2946
|
|
2626
2947
|
// this method implicitly marks a zone
|
2627
|
-
|
2628
|
-
var a = this.toArray(); // year,month,date,hours,minutes,seconds as an array
|
2948
|
+
newMomentProto.local = function() {
|
2949
|
+
var a = this.toArray(); // year,month,date,hours,minutes,seconds,ms as an array
|
2629
2950
|
var wasAmbigZone = this._ambigZone;
|
2630
2951
|
|
2631
|
-
|
2632
|
-
delete this._ambigTime;
|
2633
|
-
delete this._ambigZone;
|
2634
|
-
|
2635
|
-
moment.fn.local.apply(this, arguments);
|
2952
|
+
oldMomentProto.local.apply(this, arguments); // will clear ambig flags
|
2636
2953
|
|
2637
2954
|
if (wasAmbigZone) {
|
2638
2955
|
// If the moment was ambiguously zoned, the date fields were stored as UTC.
|
2639
2956
|
// We want to preserve these, but in local time.
|
2640
|
-
this
|
2641
|
-
.month(a[1])
|
2642
|
-
.date(a[2])
|
2643
|
-
.hours(a[3])
|
2644
|
-
.minutes(a[4])
|
2645
|
-
.seconds(a[5])
|
2646
|
-
.milliseconds(a[6]);
|
2957
|
+
setLocalValues(this, a);
|
2647
2958
|
}
|
2648
2959
|
|
2649
2960
|
return this; // for chaining
|
2650
2961
|
};
|
2651
2962
|
|
2652
|
-
// this method implicitly marks a zone
|
2653
|
-
FCMoment.prototype.utc = function() {
|
2654
|
-
|
2655
|
-
// will happen anyway via .local()/.zone(), but don't want to rely on internal implementation
|
2656
|
-
delete this._ambigTime;
|
2657
|
-
delete this._ambigZone;
|
2658
|
-
|
2659
|
-
return moment.fn.utc.apply(this, arguments);
|
2660
|
-
};
|
2661
|
-
|
2662
2963
|
|
2663
2964
|
// Formatting
|
2664
2965
|
// -------------------------------------------------------------------------------------------------
|
2665
2966
|
|
2666
|
-
|
2667
|
-
if (arguments[0]) {
|
2967
|
+
newMomentProto.format = function() {
|
2968
|
+
if (this._fullCalendar && arguments[0]) { // an enhanced moment? and a format string provided?
|
2668
2969
|
return formatDate(this, arguments[0]); // our extended formatting
|
2669
2970
|
}
|
2670
2971
|
if (this._ambigTime) {
|
2671
|
-
return
|
2972
|
+
return oldMomentFormat(this, 'YYYY-MM-DD');
|
2672
2973
|
}
|
2673
2974
|
if (this._ambigZone) {
|
2674
|
-
return
|
2975
|
+
return oldMomentFormat(this, 'YYYY-MM-DD[T]HH:mm:ss');
|
2675
2976
|
}
|
2676
|
-
return
|
2977
|
+
return oldMomentProto.format.apply(this, arguments);
|
2677
2978
|
};
|
2678
2979
|
|
2679
|
-
|
2980
|
+
newMomentProto.toISOString = function() {
|
2680
2981
|
if (this._ambigTime) {
|
2681
|
-
return
|
2982
|
+
return oldMomentFormat(this, 'YYYY-MM-DD');
|
2682
2983
|
}
|
2683
2984
|
if (this._ambigZone) {
|
2684
|
-
return
|
2985
|
+
return oldMomentFormat(this, 'YYYY-MM-DD[T]HH:mm:ss');
|
2685
2986
|
}
|
2686
|
-
return
|
2987
|
+
return oldMomentProto.toISOString.apply(this, arguments);
|
2687
2988
|
};
|
2688
2989
|
|
2689
2990
|
|
@@ -2691,23 +2992,29 @@ FCMoment.prototype.toISOString = function() {
|
|
2691
2992
|
// -------------------------------------------------------------------------------------------------
|
2692
2993
|
|
2693
2994
|
// Is the moment within the specified range? `end` is exclusive.
|
2694
|
-
|
2995
|
+
// FYI, this method is not a standard Moment method, so always do our enhanced logic.
|
2996
|
+
newMomentProto.isWithin = function(start, end) {
|
2695
2997
|
var a = commonlyAmbiguate([ this, start, end ]);
|
2696
2998
|
return a[0] >= a[1] && a[0] < a[2];
|
2697
2999
|
};
|
2698
3000
|
|
2699
3001
|
// When isSame is called with units, timezone ambiguity is normalized before the comparison happens.
|
2700
|
-
// If no units
|
2701
|
-
|
3002
|
+
// If no units specified, the two moments must be identically the same, with matching ambig flags.
|
3003
|
+
newMomentProto.isSame = function(input, units) {
|
2702
3004
|
var a;
|
2703
3005
|
|
3006
|
+
// only do custom logic if this is an enhanced moment
|
3007
|
+
if (!this._fullCalendar) {
|
3008
|
+
return oldMomentProto.isSame.apply(this, arguments);
|
3009
|
+
}
|
3010
|
+
|
2704
3011
|
if (units) {
|
2705
3012
|
a = commonlyAmbiguate([ this, input ], true); // normalize timezones but don't erase times
|
2706
|
-
return
|
3013
|
+
return oldMomentProto.isSame.call(a[0], a[1], units);
|
2707
3014
|
}
|
2708
3015
|
else {
|
2709
3016
|
input = fc.moment.parseZone(input); // normalize input
|
2710
|
-
return
|
3017
|
+
return oldMomentProto.isSame.call(this, input) &&
|
2711
3018
|
Boolean(this._ambigTime) === Boolean(input._ambigTime) &&
|
2712
3019
|
Boolean(this._ambigZone) === Boolean(input._ambigZone);
|
2713
3020
|
}
|
@@ -2718,9 +3025,16 @@ $.each([
|
|
2718
3025
|
'isBefore',
|
2719
3026
|
'isAfter'
|
2720
3027
|
], function(i, methodName) {
|
2721
|
-
|
2722
|
-
var a
|
2723
|
-
|
3028
|
+
newMomentProto[methodName] = function(input, units) {
|
3029
|
+
var a;
|
3030
|
+
|
3031
|
+
// only do custom logic if this is an enhanced moment
|
3032
|
+
if (!this._fullCalendar) {
|
3033
|
+
return oldMomentProto[methodName].apply(this, arguments);
|
3034
|
+
}
|
3035
|
+
|
3036
|
+
a = commonlyAmbiguate([ this, input ]);
|
3037
|
+
return oldMomentProto[methodName].call(a[0], a[1], units);
|
2724
3038
|
};
|
2725
3039
|
});
|
2726
3040
|
|
@@ -2755,6 +3069,63 @@ function commonlyAmbiguate(inputs, preserveTime) {
|
|
2755
3069
|
return outputs;
|
2756
3070
|
}
|
2757
3071
|
|
3072
|
+
// Transfers all the flags related to ambiguous time/zone from the `src` moment to the `dest` moment
|
3073
|
+
function transferAmbigs(src, dest) {
|
3074
|
+
if (src._ambigTime) {
|
3075
|
+
dest._ambigTime = true;
|
3076
|
+
}
|
3077
|
+
else if (dest._ambigTime) {
|
3078
|
+
dest._ambigTime = false;
|
3079
|
+
}
|
3080
|
+
|
3081
|
+
if (src._ambigZone) {
|
3082
|
+
dest._ambigZone = true;
|
3083
|
+
}
|
3084
|
+
else if (dest._ambigZone) {
|
3085
|
+
dest._ambigZone = false;
|
3086
|
+
}
|
3087
|
+
}
|
3088
|
+
|
3089
|
+
|
3090
|
+
// Sets the year/month/date/etc values of the moment from the given array.
|
3091
|
+
// Inefficient because it calls each individual setter.
|
3092
|
+
function setMomentValues(mom, a) {
|
3093
|
+
mom.year(a[0] || 0)
|
3094
|
+
.month(a[1] || 0)
|
3095
|
+
.date(a[2] || 0)
|
3096
|
+
.hours(a[3] || 0)
|
3097
|
+
.minutes(a[4] || 0)
|
3098
|
+
.seconds(a[5] || 0)
|
3099
|
+
.milliseconds(a[6] || 0);
|
3100
|
+
}
|
3101
|
+
|
3102
|
+
// Can we set the moment's internal date directly?
|
3103
|
+
allowValueOptimization = '_d' in moment() && 'updateOffset' in moment;
|
3104
|
+
|
3105
|
+
// Utility function. Accepts a moment and an array of the UTC year/month/date/etc values to set.
|
3106
|
+
// Assumes the given moment is already in UTC mode.
|
3107
|
+
setUTCValues = allowValueOptimization ? function(mom, a) {
|
3108
|
+
// simlate what moment's accessors do
|
3109
|
+
mom._d.setTime(Date.UTC.apply(Date, a));
|
3110
|
+
moment.updateOffset(mom, false); // keepTime=false
|
3111
|
+
} : setMomentValues;
|
3112
|
+
|
3113
|
+
// Utility function. Accepts a moment and an array of the local year/month/date/etc values to set.
|
3114
|
+
// Assumes the given moment is already in local mode.
|
3115
|
+
setLocalValues = allowValueOptimization ? function(mom, a) {
|
3116
|
+
// simlate what moment's accessors do
|
3117
|
+
mom._d.setTime(+new Date( // FYI, there is now way to apply an array of args to a constructor
|
3118
|
+
a[0] || 0,
|
3119
|
+
a[1] || 0,
|
3120
|
+
a[2] || 0,
|
3121
|
+
a[3] || 0,
|
3122
|
+
a[4] || 0,
|
3123
|
+
a[5] || 0,
|
3124
|
+
a[6] || 0
|
3125
|
+
));
|
3126
|
+
moment.updateOffset(mom, false); // keepTime=false
|
3127
|
+
} : setMomentValues;
|
3128
|
+
|
2758
3129
|
;;
|
2759
3130
|
|
2760
3131
|
// Single Date Formatting
|
@@ -2762,8 +3133,8 @@ function commonlyAmbiguate(inputs, preserveTime) {
|
|
2762
3133
|
|
2763
3134
|
|
2764
3135
|
// call this if you want Moment's original format method to be used
|
2765
|
-
function
|
2766
|
-
return
|
3136
|
+
function oldMomentFormat(mom, formatStr) {
|
3137
|
+
return oldMomentProto.format.call(mom, formatStr); // oldMomentProto defined in moment-ext.js
|
2767
3138
|
}
|
2768
3139
|
|
2769
3140
|
|
@@ -2789,10 +3160,10 @@ function formatDateWithChunks(date, chunks) {
|
|
2789
3160
|
// addition formatting tokens we want recognized
|
2790
3161
|
var tokenOverrides = {
|
2791
3162
|
t: function(date) { // "a" or "p"
|
2792
|
-
return
|
3163
|
+
return oldMomentFormat(date, 'a').charAt(0);
|
2793
3164
|
},
|
2794
3165
|
T: function(date) { // "A" or "P"
|
2795
|
-
return
|
3166
|
+
return oldMomentFormat(date, 'A').charAt(0);
|
2796
3167
|
}
|
2797
3168
|
};
|
2798
3169
|
|
@@ -2808,7 +3179,7 @@ function formatDateWithChunk(date, chunk) {
|
|
2808
3179
|
if (tokenOverrides[token]) {
|
2809
3180
|
return tokenOverrides[token](date); // use our custom token
|
2810
3181
|
}
|
2811
|
-
return
|
3182
|
+
return oldMomentFormat(date, token);
|
2812
3183
|
}
|
2813
3184
|
else if (chunk.maybe) { // a grouping of other chunks that must be non-zero
|
2814
3185
|
maybeStr = formatDateWithChunks(date, chunk.maybe);
|
@@ -2936,7 +3307,7 @@ function formatSimilarChunk(date1, date2, chunk) {
|
|
2936
3307
|
unit = similarUnitMap[token.charAt(0)];
|
2937
3308
|
// are the dates the same for this unit of measurement?
|
2938
3309
|
if (unit && date1.isSame(date2, unit)) {
|
2939
|
-
return
|
3310
|
+
return oldMomentFormat(date1, token); // would be the same if we used `date2`
|
2940
3311
|
// BTW, don't support custom tokens
|
2941
3312
|
}
|
2942
3313
|
}
|
@@ -3302,7 +3673,7 @@ ComboCoordMap.prototype = {
|
|
3302
3673
|
|
3303
3674
|
/* Tracks mouse movements over a CoordMap and raises events about which cell the mouse is over.
|
3304
3675
|
----------------------------------------------------------------------------------------------------------------------*/
|
3305
|
-
// TODO:
|
3676
|
+
// TODO: very useful to have a handler that gets called upon cellOut OR when dragging stops (for cleanup)
|
3306
3677
|
|
3307
3678
|
function DragListener(coordMap, options) {
|
3308
3679
|
this.coordMap = coordMap;
|
@@ -4007,7 +4378,7 @@ RowRenderer.prototype = {
|
|
4007
4378
|
}
|
4008
4379
|
|
4009
4380
|
if (typeof renderer === 'function') {
|
4010
|
-
return function(
|
4381
|
+
return function() {
|
4011
4382
|
return renderer.apply(provider, arguments) || ''; // use correct `this` and always return a string
|
4012
4383
|
};
|
4013
4384
|
}
|
@@ -4028,6 +4399,7 @@ RowRenderer.prototype = {
|
|
4028
4399
|
function Grid(view) {
|
4029
4400
|
RowRenderer.call(this, view); // call the super-constructor
|
4030
4401
|
this.coordMap = new GridCoordMap(this);
|
4402
|
+
this.elsByFill = {};
|
4031
4403
|
}
|
4032
4404
|
|
4033
4405
|
|
@@ -4037,6 +4409,7 @@ $.extend(Grid.prototype, {
|
|
4037
4409
|
el: null, // the containing element
|
4038
4410
|
coordMap: null, // a GridCoordMap that converts pixel values to datetimes
|
4039
4411
|
cellDuration: null, // a cell's duration. subclasses must assign this ASAP
|
4412
|
+
elsByFill: null, // a hash of jQuery element sets used for rendering each fill. Keyed by fill name.
|
4040
4413
|
|
4041
4414
|
|
4042
4415
|
// Renders the grid into the `el` element.
|
@@ -4107,6 +4480,7 @@ $.extend(Grid.prototype, {
|
|
4107
4480
|
dayMousedown: function(ev) {
|
4108
4481
|
var _this = this;
|
4109
4482
|
var view = this.view;
|
4483
|
+
var calendar = view.calendar;
|
4110
4484
|
var isSelectable = view.opt('selectable');
|
4111
4485
|
var dates = null; // the inclusive dates of the selection. will be null if no selection
|
4112
4486
|
var start; // the inclusive start of the selection
|
@@ -4132,13 +4506,20 @@ $.extend(Grid.prototype, {
|
|
4132
4506
|
end = dates[1].clone().add(_this.cellDuration);
|
4133
4507
|
|
4134
4508
|
if (isSelectable) {
|
4135
|
-
|
4509
|
+
if (calendar.isSelectionAllowedInRange(start, end)) { // allowed to select within this range?
|
4510
|
+
_this.renderSelection(start, end);
|
4511
|
+
}
|
4512
|
+
else {
|
4513
|
+
dates = null; // flag for an invalid selection
|
4514
|
+
disableCursor();
|
4515
|
+
}
|
4136
4516
|
}
|
4137
4517
|
}
|
4138
4518
|
},
|
4139
4519
|
cellOut: function(cell, date) {
|
4140
4520
|
dates = null;
|
4141
4521
|
_this.destroySelection();
|
4522
|
+
enableCursor();
|
4142
4523
|
},
|
4143
4524
|
listenStop: function(ev) {
|
4144
4525
|
if (dates) { // started and ended on a cell?
|
@@ -4150,6 +4531,7 @@ $.extend(Grid.prototype, {
|
|
4150
4531
|
view.reportSelection(start, end, ev);
|
4151
4532
|
}
|
4152
4533
|
}
|
4534
|
+
enableCursor();
|
4153
4535
|
}
|
4154
4536
|
});
|
4155
4537
|
|
@@ -4257,18 +4639,108 @@ $.extend(Grid.prototype, {
|
|
4257
4639
|
------------------------------------------------------------------------------------------------------------------*/
|
4258
4640
|
|
4259
4641
|
|
4260
|
-
//
|
4642
|
+
// Renders an emphasis on the given date range. `start` is inclusive. `end` is exclusive.
|
4261
4643
|
renderHighlight: function(start, end) {
|
4262
|
-
|
4644
|
+
this.renderFill('highlight', this.rangeToSegs(start, end));
|
4263
4645
|
},
|
4264
4646
|
|
4265
4647
|
|
4266
|
-
//
|
4648
|
+
// Unrenders the emphasis on a date range
|
4267
4649
|
destroyHighlight: function() {
|
4268
|
-
|
4650
|
+
this.destroyFill('highlight');
|
4651
|
+
},
|
4652
|
+
|
4653
|
+
|
4654
|
+
// Generates an array of classNames for rendering the highlight. Used by the fill system.
|
4655
|
+
highlightSegClasses: function() {
|
4656
|
+
return [ 'fc-highlight' ];
|
4657
|
+
},
|
4658
|
+
|
4659
|
+
|
4660
|
+
/* Fill System (highlight, background events, business hours)
|
4661
|
+
------------------------------------------------------------------------------------------------------------------*/
|
4662
|
+
|
4663
|
+
|
4664
|
+
// Renders a set of rectangles over the given segments of time.
|
4665
|
+
// Returns a subset of segs, the segs that were actually rendered.
|
4666
|
+
// Responsible for populating this.elsByFill
|
4667
|
+
renderFill: function(type, segs) {
|
4668
|
+
// subclasses must implement
|
4669
|
+
},
|
4670
|
+
|
4671
|
+
|
4672
|
+
// Unrenders a specific type of fill that is currently rendered on the grid
|
4673
|
+
destroyFill: function(type) {
|
4674
|
+
var el = this.elsByFill[type];
|
4675
|
+
|
4676
|
+
if (el) {
|
4677
|
+
el.remove();
|
4678
|
+
delete this.elsByFill[type];
|
4679
|
+
}
|
4680
|
+
},
|
4681
|
+
|
4682
|
+
|
4683
|
+
// Renders and assigns an `el` property for each fill segment. Generic enough to work with different types.
|
4684
|
+
// Only returns segments that successfully rendered.
|
4685
|
+
// To be harnessed by renderFill (implemented by subclasses).
|
4686
|
+
// Analagous to renderFgSegEls.
|
4687
|
+
renderFillSegEls: function(type, segs) {
|
4688
|
+
var _this = this;
|
4689
|
+
var segElMethod = this[type + 'SegEl'];
|
4690
|
+
var html = '';
|
4691
|
+
var renderedSegs = [];
|
4692
|
+
var i;
|
4693
|
+
|
4694
|
+
if (segs.length) {
|
4695
|
+
|
4696
|
+
// build a large concatenation of segment HTML
|
4697
|
+
for (i = 0; i < segs.length; i++) {
|
4698
|
+
html += this.fillSegHtml(type, segs[i]);
|
4699
|
+
}
|
4700
|
+
|
4701
|
+
// Grab individual elements from the combined HTML string. Use each as the default rendering.
|
4702
|
+
// Then, compute the 'el' for each segment.
|
4703
|
+
$(html).each(function(i, node) {
|
4704
|
+
var seg = segs[i];
|
4705
|
+
var el = $(node);
|
4706
|
+
|
4707
|
+
// allow custom filter methods per-type
|
4708
|
+
if (segElMethod) {
|
4709
|
+
el = segElMethod.call(_this, seg, el);
|
4710
|
+
}
|
4711
|
+
|
4712
|
+
if (el) { // custom filters did not cancel the render
|
4713
|
+
el = $(el); // allow custom filter to return raw DOM node
|
4714
|
+
|
4715
|
+
// correct element type? (would be bad if a non-TD were inserted into a table for example)
|
4716
|
+
if (el.is(_this.fillSegTag)) {
|
4717
|
+
seg.el = el;
|
4718
|
+
renderedSegs.push(seg);
|
4719
|
+
}
|
4720
|
+
}
|
4721
|
+
});
|
4722
|
+
}
|
4723
|
+
|
4724
|
+
return renderedSegs;
|
4269
4725
|
},
|
4270
4726
|
|
4271
4727
|
|
4728
|
+
fillSegTag: 'div', // subclasses can override
|
4729
|
+
|
4730
|
+
|
4731
|
+
// Builds the HTML needed for one fill segment. Generic enought o work with different types.
|
4732
|
+
fillSegHtml: function(type, seg) {
|
4733
|
+
var classesMethod = this[type + 'SegClasses']; // custom hooks per-type
|
4734
|
+
var stylesMethod = this[type + 'SegStyles']; //
|
4735
|
+
var classes = classesMethod ? classesMethod.call(this, seg) : [];
|
4736
|
+
var styles = stylesMethod ? stylesMethod.call(this, seg) : ''; // a semi-colon separated CSS property string
|
4737
|
+
|
4738
|
+
return '<' + this.fillSegTag +
|
4739
|
+
(classes.length ? ' class="' + classes.join(' ') + '"' : '') +
|
4740
|
+
(styles ? ' style="' + styles + '"' : '') +
|
4741
|
+
' />';
|
4742
|
+
},
|
4743
|
+
|
4272
4744
|
|
4273
4745
|
/* Generic rendering utilities for subclasses
|
4274
4746
|
------------------------------------------------------------------------------------------------------------------*/
|
@@ -4352,96 +4824,174 @@ $.extend(Grid.prototype, {
|
|
4352
4824
|
mousedOverSeg: null, // the segment object the user's mouse is over. null if over nothing
|
4353
4825
|
isDraggingSeg: false, // is a segment being dragged? boolean
|
4354
4826
|
isResizingSeg: false, // is a segment being resized? boolean
|
4827
|
+
segs: null, // the event segments currently rendered in the grid
|
4355
4828
|
|
4356
4829
|
|
4357
4830
|
// Renders the given events onto the grid
|
4358
4831
|
renderEvents: function(events) {
|
4359
|
-
|
4832
|
+
var segs = this.eventsToSegs(events);
|
4833
|
+
var bgSegs = [];
|
4834
|
+
var fgSegs = [];
|
4835
|
+
var i, seg;
|
4836
|
+
|
4837
|
+
for (i = 0; i < segs.length; i++) {
|
4838
|
+
seg = segs[i];
|
4839
|
+
|
4840
|
+
if (isBgEvent(seg.event)) {
|
4841
|
+
bgSegs.push(seg);
|
4842
|
+
}
|
4843
|
+
else {
|
4844
|
+
fgSegs.push(seg);
|
4845
|
+
}
|
4846
|
+
}
|
4847
|
+
|
4848
|
+
// Render each different type of segment.
|
4849
|
+
// Each function may return a subset of the segs, segs that were actually rendered.
|
4850
|
+
bgSegs = this.renderBgSegs(bgSegs) || bgSegs;
|
4851
|
+
fgSegs = this.renderFgSegs(fgSegs) || fgSegs;
|
4852
|
+
|
4853
|
+
this.segs = bgSegs.concat(fgSegs);
|
4360
4854
|
},
|
4361
4855
|
|
4362
4856
|
|
4363
|
-
//
|
4857
|
+
// Unrenders all events currently rendered on the grid
|
4858
|
+
destroyEvents: function() {
|
4859
|
+
this.triggerSegMouseout(); // trigger an eventMouseout if user's mouse is over an event
|
4860
|
+
|
4861
|
+
this.destroyFgSegs();
|
4862
|
+
this.destroyBgSegs();
|
4863
|
+
|
4864
|
+
this.segs = null;
|
4865
|
+
},
|
4866
|
+
|
4867
|
+
|
4868
|
+
// Retrieves all rendered segment objects currently rendered on the grid
|
4364
4869
|
getSegs: function() {
|
4870
|
+
return this.segs || [];
|
4871
|
+
},
|
4872
|
+
|
4873
|
+
|
4874
|
+
/* Foreground Segment Rendering
|
4875
|
+
------------------------------------------------------------------------------------------------------------------*/
|
4876
|
+
|
4877
|
+
|
4878
|
+
// Renders foreground event segments onto the grid. May return a subset of segs that were rendered.
|
4879
|
+
renderFgSegs: function(segs) {
|
4365
4880
|
// subclasses must implement
|
4366
4881
|
},
|
4367
4882
|
|
4368
4883
|
|
4369
|
-
// Unrenders all
|
4370
|
-
|
4371
|
-
|
4884
|
+
// Unrenders all currently rendered foreground segments
|
4885
|
+
destroyFgSegs: function() {
|
4886
|
+
// subclasses must implement
|
4372
4887
|
},
|
4373
4888
|
|
4374
4889
|
|
4375
|
-
// Renders
|
4376
|
-
|
4890
|
+
// Renders and assigns an `el` property for each foreground event segment.
|
4891
|
+
// Only returns segments that successfully rendered.
|
4892
|
+
// A utility that subclasses may use.
|
4893
|
+
renderFgSegEls: function(segs, disableResizing) {
|
4377
4894
|
var view = this.view;
|
4378
4895
|
var html = '';
|
4379
4896
|
var renderedSegs = [];
|
4380
4897
|
var i;
|
4381
4898
|
|
4382
|
-
|
4383
|
-
for (i = 0; i < segs.length; i++) {
|
4384
|
-
html += this.renderSegHtml(segs[i], disableResizing);
|
4385
|
-
}
|
4899
|
+
if (segs.length) { // don't build an empty html string
|
4386
4900
|
|
4387
|
-
|
4388
|
-
|
4389
|
-
|
4390
|
-
var seg = segs[i];
|
4391
|
-
var el = view.resolveEventEl(seg.event, $(node));
|
4392
|
-
if (el) {
|
4393
|
-
el.data('fc-seg', seg); // used by handlers
|
4394
|
-
seg.el = el;
|
4395
|
-
renderedSegs.push(seg);
|
4901
|
+
// build a large concatenation of event segment HTML
|
4902
|
+
for (i = 0; i < segs.length; i++) {
|
4903
|
+
html += this.fgSegHtml(segs[i], disableResizing);
|
4396
4904
|
}
|
4397
|
-
|
4905
|
+
|
4906
|
+
// Grab individual elements from the combined HTML string. Use each as the default rendering.
|
4907
|
+
// Then, compute the 'el' for each segment. An el might be null if the eventRender callback returned false.
|
4908
|
+
$(html).each(function(i, node) {
|
4909
|
+
var seg = segs[i];
|
4910
|
+
var el = view.resolveEventEl(seg.event, $(node));
|
4911
|
+
|
4912
|
+
if (el) {
|
4913
|
+
el.data('fc-seg', seg); // used by handlers
|
4914
|
+
seg.el = el;
|
4915
|
+
renderedSegs.push(seg);
|
4916
|
+
}
|
4917
|
+
});
|
4918
|
+
}
|
4398
4919
|
|
4399
4920
|
return renderedSegs;
|
4400
4921
|
},
|
4401
4922
|
|
4402
4923
|
|
4403
|
-
// Generates the HTML for the default rendering of a segment
|
4404
|
-
|
4405
|
-
// subclasses
|
4924
|
+
// Generates the HTML for the default rendering of a foreground event segment. Used by renderFgSegEls()
|
4925
|
+
fgSegHtml: function(seg, disableResizing) {
|
4926
|
+
// subclasses should implement
|
4927
|
+
},
|
4928
|
+
|
4929
|
+
|
4930
|
+
/* Background Segment Rendering
|
4931
|
+
------------------------------------------------------------------------------------------------------------------*/
|
4932
|
+
|
4933
|
+
|
4934
|
+
// Renders the given background event segments onto the grid.
|
4935
|
+
// Returns a subset of the segs that were actually rendered.
|
4936
|
+
renderBgSegs: function(segs) {
|
4937
|
+
return this.renderFill('bgEvent', segs);
|
4938
|
+
},
|
4939
|
+
|
4940
|
+
|
4941
|
+
// Unrenders all the currently rendered background event segments
|
4942
|
+
destroyBgSegs: function() {
|
4943
|
+
this.destroyFill('bgEvent');
|
4944
|
+
},
|
4945
|
+
|
4946
|
+
|
4947
|
+
// Renders a background event element, given the default rendering. Called by the fill system.
|
4948
|
+
bgEventSegEl: function(seg, el) {
|
4949
|
+
return this.view.resolveEventEl(seg.event, el); // will filter through eventRender
|
4406
4950
|
},
|
4407
4951
|
|
4408
4952
|
|
4409
|
-
//
|
4410
|
-
|
4411
|
-
|
4953
|
+
// Generates an array of classNames to be used for the default rendering of a background event.
|
4954
|
+
// Called by the fill system.
|
4955
|
+
bgEventSegClasses: function(seg) {
|
4956
|
+
var event = seg.event;
|
4957
|
+
var source = event.source || {};
|
4958
|
+
|
4959
|
+
return [ 'fc-bgevent' ].concat(
|
4960
|
+
event.className,
|
4961
|
+
source.className || []
|
4962
|
+
);
|
4963
|
+
},
|
4964
|
+
|
4965
|
+
|
4966
|
+
// Generates a semicolon-separated CSS string to be used for the default rendering of a background event.
|
4967
|
+
// Called by the fill system.
|
4968
|
+
// TODO: consolidate with getEventSkinCss?
|
4969
|
+
bgEventSegStyles: function(seg) {
|
4970
|
+
var view = this.view;
|
4971
|
+
var event = seg.event;
|
4972
|
+
var source = event.source || {};
|
4973
|
+
var eventColor = event.color;
|
4974
|
+
var sourceColor = source.color;
|
4975
|
+
var optionColor = view.opt('eventColor');
|
4976
|
+
var backgroundColor =
|
4977
|
+
event.backgroundColor ||
|
4978
|
+
eventColor ||
|
4979
|
+
source.backgroundColor ||
|
4980
|
+
sourceColor ||
|
4981
|
+
view.opt('eventBackgroundColor') ||
|
4982
|
+
optionColor;
|
4983
|
+
|
4984
|
+
if (backgroundColor) {
|
4985
|
+
return 'background-color:' + backgroundColor;
|
4986
|
+
}
|
4412
4987
|
|
4413
|
-
return
|
4414
|
-
return _this.eventToSegs(event, intervalStart, intervalEnd); // $.map flattens all returned arrays together
|
4415
|
-
});
|
4988
|
+
return '';
|
4416
4989
|
},
|
4417
4990
|
|
4418
4991
|
|
4419
|
-
//
|
4420
|
-
|
4421
|
-
|
4422
|
-
eventToSegs: function(event, intervalStart, intervalEnd) {
|
4423
|
-
var eventStart = event.start.clone().stripZone(); // normalize
|
4424
|
-
var eventEnd = this.view.calendar.getEventEnd(event).stripZone(); // compute (if necessary) and normalize
|
4425
|
-
var segs;
|
4426
|
-
var i, seg;
|
4427
|
-
|
4428
|
-
if (intervalStart && intervalEnd) {
|
4429
|
-
seg = intersectionToSeg(eventStart, eventEnd, intervalStart, intervalEnd);
|
4430
|
-
segs = seg ? [ seg ] : [];
|
4431
|
-
}
|
4432
|
-
else {
|
4433
|
-
segs = this.rangeToSegs(eventStart, eventEnd); // defined by the subclass
|
4434
|
-
}
|
4435
|
-
|
4436
|
-
// assign extra event-related properties to the segment objects
|
4437
|
-
for (i = 0; i < segs.length; i++) {
|
4438
|
-
seg = segs[i];
|
4439
|
-
seg.event = event;
|
4440
|
-
seg.eventStartMS = +eventStart;
|
4441
|
-
seg.eventDurationMS = eventEnd - eventStart;
|
4442
|
-
}
|
4443
|
-
|
4444
|
-
return segs;
|
4992
|
+
// Generates an array of classNames to be used for the rendering business hours overlay. Called by the fill system.
|
4993
|
+
businessHoursSegClasses: function(seg) {
|
4994
|
+
return [ 'fc-nonbusiness', 'fc-bgevent' ];
|
4445
4995
|
},
|
4446
4996
|
|
4447
4997
|
|
@@ -4520,6 +5070,7 @@ $.extend(Grid.prototype, {
|
|
4520
5070
|
segDragMousedown: function(seg, ev) {
|
4521
5071
|
var _this = this;
|
4522
5072
|
var view = this.view;
|
5073
|
+
var calendar = view.calendar;
|
4523
5074
|
var el = seg.el;
|
4524
5075
|
var event = seg.event;
|
4525
5076
|
var newStart, newEnd;
|
@@ -4553,17 +5104,26 @@ $.extend(Grid.prototype, {
|
|
4553
5104
|
newStart = res.start;
|
4554
5105
|
newEnd = res.end;
|
4555
5106
|
|
4556
|
-
if (
|
4557
|
-
|
5107
|
+
if (calendar.isEventAllowedInRange(event, newStart, res.visibleEnd)) { // allowed to drop here?
|
5108
|
+
if (view.renderDrag(newStart, newEnd, seg)) { // have the view render a visual indication
|
5109
|
+
mouseFollower.hide(); // if the view is already using a mock event "helper", hide our own
|
5110
|
+
}
|
5111
|
+
else {
|
5112
|
+
mouseFollower.show();
|
5113
|
+
}
|
4558
5114
|
}
|
4559
5115
|
else {
|
5116
|
+
// have the helper follow the mouse (no snapping) with a warning-style cursor
|
5117
|
+
newStart = null; // mark an invalid drop date
|
4560
5118
|
mouseFollower.show();
|
5119
|
+
disableCursor();
|
4561
5120
|
}
|
4562
5121
|
},
|
4563
5122
|
cellOut: function() { // called before mouse moves to a different cell OR moved out of all cells
|
4564
5123
|
newStart = null;
|
4565
5124
|
view.destroyDrag(); // unrender whatever was done in view.renderDrag
|
4566
5125
|
mouseFollower.show(); // show in case we are moving out of all cells
|
5126
|
+
enableCursor();
|
4567
5127
|
},
|
4568
5128
|
dragStop: function(ev) {
|
4569
5129
|
var hasChanged = newStart && !newStart.isSame(event.start);
|
@@ -4579,6 +5139,8 @@ $.extend(Grid.prototype, {
|
|
4579
5139
|
view.eventDrop(el[0], event, newStart, ev); // will rerender all events...
|
4580
5140
|
}
|
4581
5141
|
});
|
5142
|
+
|
5143
|
+
enableCursor();
|
4582
5144
|
},
|
4583
5145
|
listenStop: function() {
|
4584
5146
|
mouseFollower.stop(); // put in listenStop in case there was a mousedown but the drag never started
|
@@ -4589,7 +5151,8 @@ $.extend(Grid.prototype, {
|
|
4589
5151
|
},
|
4590
5152
|
|
4591
5153
|
|
4592
|
-
// Given a segment, the dates where a drag began and ended, calculates the Event Object's new start and end dates
|
5154
|
+
// Given a segment, the dates where a drag began and ended, calculates the Event Object's new start and end dates.
|
5155
|
+
// Might return a `null` end (even when forceEventDuration is on).
|
4593
5156
|
computeDraggedEventDates: function(seg, dragStartDate, dropDate) {
|
4594
5157
|
var view = this.view;
|
4595
5158
|
var event = seg.event;
|
@@ -4598,6 +5161,8 @@ $.extend(Grid.prototype, {
|
|
4598
5161
|
var delta;
|
4599
5162
|
var newStart;
|
4600
5163
|
var newEnd;
|
5164
|
+
var newAllDay;
|
5165
|
+
var visibleEnd;
|
4601
5166
|
|
4602
5167
|
if (dropDate.hasTime() === dragStartDate.hasTime()) {
|
4603
5168
|
delta = dayishDiff(dropDate, dragStartDate);
|
@@ -4608,14 +5173,19 @@ $.extend(Grid.prototype, {
|
|
4608
5173
|
else {
|
4609
5174
|
newEnd = end.clone().add(delta);
|
4610
5175
|
}
|
5176
|
+
newAllDay = event.allDay; // keep it the same
|
4611
5177
|
}
|
4612
5178
|
else {
|
4613
5179
|
// if switching from day <-> timed, start should be reset to the dropped date, and the end cleared
|
4614
5180
|
newStart = dropDate;
|
4615
5181
|
newEnd = null; // end should be cleared
|
5182
|
+
newAllDay = !dropDate.hasTime();
|
4616
5183
|
}
|
4617
5184
|
|
4618
|
-
|
5185
|
+
// compute what the end date will appear to be
|
5186
|
+
visibleEnd = newEnd || view.calendar.getDefaultEventEnd(newAllDay, newStart);
|
5187
|
+
|
5188
|
+
return { start: newStart, end: newEnd, visibleEnd: visibleEnd };
|
4619
5189
|
},
|
4620
5190
|
|
4621
5191
|
|
@@ -4628,6 +5198,7 @@ $.extend(Grid.prototype, {
|
|
4628
5198
|
segResizeMousedown: function(seg, ev) {
|
4629
5199
|
var _this = this;
|
4630
5200
|
var view = this.view;
|
5201
|
+
var calendar = view.calendar;
|
4631
5202
|
var el = seg.el;
|
4632
5203
|
var event = seg.event;
|
4633
5204
|
var start = event.start;
|
@@ -4635,7 +5206,7 @@ $.extend(Grid.prototype, {
|
|
4635
5206
|
var newEnd = null;
|
4636
5207
|
var dragListener;
|
4637
5208
|
|
4638
|
-
function destroy() { // resets the rendering
|
5209
|
+
function destroy() { // resets the rendering to show the original event
|
4639
5210
|
_this.destroyResize();
|
4640
5211
|
view.showEvent(event);
|
4641
5212
|
}
|
@@ -4656,22 +5227,31 @@ $.extend(Grid.prototype, {
|
|
4656
5227
|
}
|
4657
5228
|
newEnd = date.clone().add(_this.cellDuration); // make it an exclusive end
|
4658
5229
|
|
4659
|
-
if (
|
4660
|
-
newEnd
|
4661
|
-
|
5230
|
+
if (calendar.isEventAllowedInRange(event, start, newEnd)) { // allowed to be resized here?
|
5231
|
+
if (newEnd.isSame(end)) {
|
5232
|
+
newEnd = null; // mark an invalid resize
|
5233
|
+
destroy();
|
5234
|
+
}
|
5235
|
+
else {
|
5236
|
+
_this.renderResize(start, newEnd, seg);
|
5237
|
+
view.hideEvent(event);
|
5238
|
+
}
|
4662
5239
|
}
|
4663
5240
|
else {
|
4664
|
-
|
4665
|
-
|
5241
|
+
newEnd = null; // mark an invalid resize
|
5242
|
+
destroy();
|
5243
|
+
disableCursor();
|
4666
5244
|
}
|
4667
5245
|
},
|
4668
5246
|
cellOut: function() { // called before mouse moves to a different cell OR moved out of all cells
|
4669
5247
|
newEnd = null;
|
4670
5248
|
destroy();
|
5249
|
+
enableCursor();
|
4671
5250
|
},
|
4672
5251
|
dragStop: function(ev) {
|
4673
5252
|
_this.isResizingSeg = false;
|
4674
5253
|
destroy();
|
5254
|
+
enableCursor();
|
4675
5255
|
view.trigger('eventResizeStop', el[0], event, ev, {}); // last argument is jqui dummy
|
4676
5256
|
|
4677
5257
|
if (newEnd) {
|
@@ -4747,16 +5327,193 @@ $.extend(Grid.prototype, {
|
|
4747
5327
|
statements.push('color:' + textColor);
|
4748
5328
|
}
|
4749
5329
|
return statements.join(';');
|
5330
|
+
},
|
5331
|
+
|
5332
|
+
|
5333
|
+
/* Converting events -> ranges -> segs
|
5334
|
+
------------------------------------------------------------------------------------------------------------------*/
|
5335
|
+
|
5336
|
+
|
5337
|
+
// Converts an array of event objects into an array of event segment objects.
|
5338
|
+
// A custom `rangeToSegsFunc` may be given for arbitrarily slicing up events.
|
5339
|
+
eventsToSegs: function(events, rangeToSegsFunc) {
|
5340
|
+
var eventRanges = this.eventsToRanges(events);
|
5341
|
+
var segs = [];
|
5342
|
+
var i;
|
5343
|
+
|
5344
|
+
for (i = 0; i < eventRanges.length; i++) {
|
5345
|
+
segs.push.apply(
|
5346
|
+
segs,
|
5347
|
+
this.eventRangeToSegs(eventRanges[i], rangeToSegsFunc)
|
5348
|
+
);
|
5349
|
+
}
|
5350
|
+
|
5351
|
+
return segs;
|
5352
|
+
},
|
5353
|
+
|
5354
|
+
|
5355
|
+
// Converts an array of events into an array of "range" objects.
|
5356
|
+
// A "range" object is a plain object with start/end properties denoting the time it covers. Also an event property.
|
5357
|
+
// For "normal" events, this will be identical to the event's start/end, but for "inverse-background" events,
|
5358
|
+
// will create an array of ranges that span the time *not* covered by the given event.
|
5359
|
+
eventsToRanges: function(events) {
|
5360
|
+
var _this = this;
|
5361
|
+
var eventsById = groupEventsById(events);
|
5362
|
+
var ranges = [];
|
5363
|
+
|
5364
|
+
// group by ID so that related inverse-background events can be rendered together
|
5365
|
+
$.each(eventsById, function(id, eventGroup) {
|
5366
|
+
if (eventGroup.length) {
|
5367
|
+
ranges.push.apply(
|
5368
|
+
ranges,
|
5369
|
+
isInverseBgEvent(eventGroup[0]) ?
|
5370
|
+
_this.eventsToInverseRanges(eventGroup) :
|
5371
|
+
_this.eventsToNormalRanges(eventGroup)
|
5372
|
+
);
|
5373
|
+
}
|
5374
|
+
});
|
5375
|
+
|
5376
|
+
return ranges;
|
5377
|
+
},
|
5378
|
+
|
5379
|
+
|
5380
|
+
// Converts an array of "normal" events (not inverted rendering) into a parallel array of ranges
|
5381
|
+
eventsToNormalRanges: function(events) {
|
5382
|
+
var calendar = this.view.calendar;
|
5383
|
+
var ranges = [];
|
5384
|
+
var i, event;
|
5385
|
+
var eventStart, eventEnd;
|
5386
|
+
|
5387
|
+
for (i = 0; i < events.length; i++) {
|
5388
|
+
event = events[i];
|
5389
|
+
|
5390
|
+
// make copies and normalize by stripping timezone
|
5391
|
+
eventStart = event.start.clone().stripZone();
|
5392
|
+
eventEnd = calendar.getEventEnd(event).stripZone();
|
5393
|
+
|
5394
|
+
ranges.push({
|
5395
|
+
event: event,
|
5396
|
+
start: eventStart,
|
5397
|
+
end: eventEnd,
|
5398
|
+
eventStartMS: +eventStart,
|
5399
|
+
eventDurationMS: eventEnd - eventStart
|
5400
|
+
});
|
5401
|
+
}
|
5402
|
+
|
5403
|
+
return ranges;
|
5404
|
+
},
|
5405
|
+
|
5406
|
+
|
5407
|
+
// Converts an array of events, with inverse-background rendering, into an array of range objects.
|
5408
|
+
// The range objects will cover all the time NOT covered by the events.
|
5409
|
+
eventsToInverseRanges: function(events) {
|
5410
|
+
var view = this.view;
|
5411
|
+
var viewStart = view.start.clone().stripZone(); // normalize timezone
|
5412
|
+
var viewEnd = view.end.clone().stripZone(); // normalize timezone
|
5413
|
+
var normalRanges = this.eventsToNormalRanges(events); // will give us normalized dates we can use w/o copies
|
5414
|
+
var inverseRanges = [];
|
5415
|
+
var event0 = events[0]; // assign this to each range's `.event`
|
5416
|
+
var start = viewStart; // the end of the previous range. the start of the new range
|
5417
|
+
var i, normalRange;
|
5418
|
+
|
5419
|
+
// ranges need to be in order. required for our date-walking algorithm
|
5420
|
+
normalRanges.sort(compareNormalRanges);
|
5421
|
+
|
5422
|
+
for (i = 0; i < normalRanges.length; i++) {
|
5423
|
+
normalRange = normalRanges[i];
|
5424
|
+
|
5425
|
+
// add the span of time before the event (if there is any)
|
5426
|
+
if (normalRange.start > start) { // compare millisecond time (skip any ambig logic)
|
5427
|
+
inverseRanges.push({
|
5428
|
+
event: event0,
|
5429
|
+
start: start,
|
5430
|
+
end: normalRange.start
|
5431
|
+
});
|
5432
|
+
}
|
5433
|
+
|
5434
|
+
start = normalRange.end;
|
5435
|
+
}
|
5436
|
+
|
5437
|
+
// add the span of time after the last event (if there is any)
|
5438
|
+
if (start < viewEnd) { // compare millisecond time (skip any ambig logic)
|
5439
|
+
inverseRanges.push({
|
5440
|
+
event: event0,
|
5441
|
+
start: start,
|
5442
|
+
end: viewEnd
|
5443
|
+
});
|
5444
|
+
}
|
5445
|
+
|
5446
|
+
return inverseRanges;
|
5447
|
+
},
|
5448
|
+
|
5449
|
+
|
5450
|
+
// Slices the given event range into one or more segment objects.
|
5451
|
+
// A `rangeToSegsFunc` custom slicing function can be given.
|
5452
|
+
eventRangeToSegs: function(eventRange, rangeToSegsFunc) {
|
5453
|
+
var segs;
|
5454
|
+
var i, seg;
|
5455
|
+
|
5456
|
+
if (rangeToSegsFunc) {
|
5457
|
+
segs = rangeToSegsFunc(eventRange.start, eventRange.end);
|
5458
|
+
}
|
5459
|
+
else {
|
5460
|
+
segs = this.rangeToSegs(eventRange.start, eventRange.end); // defined by the subclass
|
5461
|
+
}
|
5462
|
+
|
5463
|
+
for (i = 0; i < segs.length; i++) {
|
5464
|
+
seg = segs[i];
|
5465
|
+
seg.event = eventRange.event;
|
5466
|
+
seg.eventStartMS = eventRange.eventStartMS;
|
5467
|
+
seg.eventDurationMS = eventRange.eventDurationMS;
|
5468
|
+
}
|
5469
|
+
|
5470
|
+
return segs;
|
4750
5471
|
}
|
4751
5472
|
|
4752
5473
|
});
|
4753
5474
|
|
4754
5475
|
|
4755
|
-
/*
|
5476
|
+
/* Utilities
|
4756
5477
|
----------------------------------------------------------------------------------------------------------------------*/
|
4757
5478
|
|
4758
5479
|
|
5480
|
+
function isBgEvent(event) { // returns true if background OR inverse-background
|
5481
|
+
var rendering = getEventRendering(event);
|
5482
|
+
return rendering === 'background' || rendering === 'inverse-background';
|
5483
|
+
}
|
5484
|
+
|
5485
|
+
|
5486
|
+
function isInverseBgEvent(event) {
|
5487
|
+
return getEventRendering(event) === 'inverse-background';
|
5488
|
+
}
|
5489
|
+
|
5490
|
+
|
5491
|
+
function getEventRendering(event) {
|
5492
|
+
return firstDefined((event.source || {}).rendering, event.rendering);
|
5493
|
+
}
|
5494
|
+
|
5495
|
+
|
5496
|
+
function groupEventsById(events) {
|
5497
|
+
var eventsById = {};
|
5498
|
+
var i, event;
|
5499
|
+
|
5500
|
+
for (i = 0; i < events.length; i++) {
|
5501
|
+
event = events[i];
|
5502
|
+
(eventsById[event._id] || (eventsById[event._id] = [])).push(event);
|
5503
|
+
}
|
5504
|
+
|
5505
|
+
return eventsById;
|
5506
|
+
}
|
5507
|
+
|
5508
|
+
|
5509
|
+
// A cmp function for determining which non-inverted "ranges" (see above) happen earlier
|
5510
|
+
function compareNormalRanges(range1, range2) {
|
5511
|
+
return range1.eventStartMS - range2.eventStartMS; // earlier ranges go first
|
5512
|
+
}
|
5513
|
+
|
5514
|
+
|
4759
5515
|
// A cmp function for determining which segments should take visual priority
|
5516
|
+
// DOES NOT WORK ON INVERTED BACKGROUND EVENTS because they have no eventStartMS/eventDurationMS
|
4760
5517
|
function compareSegs(seg1, seg2) {
|
4761
5518
|
return seg1.eventStartMS - seg2.eventStartMS || // earlier events go first
|
4762
5519
|
seg2.eventDurationMS - seg1.eventDurationMS || // tie? longer events go first
|
@@ -4785,7 +5542,6 @@ $.extend(DayGrid.prototype, {
|
|
4785
5542
|
rowEls: null, // set of fake row elements
|
4786
5543
|
dayEls: null, // set of whole-day elements comprising the row's background
|
4787
5544
|
helperEls: null, // set of cell skeleton elements for rendering the mock event "helper"
|
4788
|
-
highlightEls: null, // set of cell skeleton elements for rendering the highlight
|
4789
5545
|
|
4790
5546
|
|
4791
5547
|
// Renders the rows and columns into the component's `this.el`, which should already be assigned.
|
@@ -4971,7 +5727,11 @@ $.extend(DayGrid.prototype, {
|
|
4971
5727
|
// Renders a mock "helper" event. `sourceSeg` is the associated internal segment object. It can be null.
|
4972
5728
|
renderHelper: function(event, sourceSeg) {
|
4973
5729
|
var helperNodes = [];
|
4974
|
-
var
|
5730
|
+
var segs = this.eventsToSegs([ event ]);
|
5731
|
+
var rowStructs;
|
5732
|
+
|
5733
|
+
segs = this.renderFgSegEls(segs); // assigns each seg's el and returns a subset of segs that were rendered
|
5734
|
+
rowStructs = this.renderSegRows(segs);
|
4975
5735
|
|
4976
5736
|
// inject each new event skeleton into each associated row
|
4977
5737
|
this.rowEls.each(function(row, rowNode) {
|
@@ -5008,65 +5768,65 @@ $.extend(DayGrid.prototype, {
|
|
5008
5768
|
},
|
5009
5769
|
|
5010
5770
|
|
5011
|
-
/*
|
5771
|
+
/* Fill System (highlight, background events, business hours)
|
5012
5772
|
------------------------------------------------------------------------------------------------------------------*/
|
5013
5773
|
|
5014
5774
|
|
5015
|
-
|
5016
|
-
|
5017
|
-
|
5018
|
-
|
5775
|
+
fillSegTag: 'td', // override the default tag name
|
5776
|
+
|
5777
|
+
|
5778
|
+
// Renders a set of rectangles over the given segments of days.
|
5779
|
+
// Only returns segments that successfully rendered.
|
5780
|
+
renderFill: function(type, segs) {
|
5781
|
+
var nodes = [];
|
5019
5782
|
var i, seg;
|
5020
|
-
var
|
5783
|
+
var skeletonEl;
|
5784
|
+
|
5785
|
+
segs = this.renderFillSegEls(type, segs); // assignes `.el` to each seg. returns successfully rendered segs
|
5021
5786
|
|
5022
|
-
// build an event skeleton for each row that needs it
|
5023
5787
|
for (i = 0; i < segs.length; i++) {
|
5024
5788
|
seg = segs[i];
|
5025
|
-
|
5026
|
-
|
5027
|
-
);
|
5028
|
-
el.appendTo(this.rowEls[seg.row]);
|
5029
|
-
highlightNodes.push(el[0]);
|
5789
|
+
skeletonEl = this.renderFillRow(type, seg);
|
5790
|
+
this.rowEls.eq(seg.row).append(skeletonEl);
|
5791
|
+
nodes.push(skeletonEl[0]);
|
5030
5792
|
}
|
5031
5793
|
|
5032
|
-
this.
|
5033
|
-
},
|
5034
|
-
|
5794
|
+
this.elsByFill[type] = $(nodes);
|
5035
5795
|
|
5036
|
-
|
5037
|
-
destroyHighlight: function() {
|
5038
|
-
if (this.highlightEls) {
|
5039
|
-
this.highlightEls.remove();
|
5040
|
-
this.highlightEls = null;
|
5041
|
-
}
|
5796
|
+
return segs;
|
5042
5797
|
},
|
5043
5798
|
|
5044
5799
|
|
5045
|
-
// Generates the HTML
|
5046
|
-
|
5800
|
+
// Generates the HTML needed for one row of a fill. Requires the seg's el to be rendered.
|
5801
|
+
renderFillRow: function(type, seg) {
|
5047
5802
|
var colCnt = this.view.colCnt;
|
5048
|
-
var
|
5803
|
+
var startCol = seg.leftCol;
|
5804
|
+
var endCol = seg.rightCol + 1;
|
5805
|
+
var skeletonEl;
|
5806
|
+
var trEl;
|
5807
|
+
|
5808
|
+
skeletonEl = $(
|
5809
|
+
'<div class="fc-' + type.toLowerCase() + '-skeleton">' +
|
5810
|
+
'<table><tr/></table>' +
|
5811
|
+
'</div>'
|
5812
|
+
);
|
5813
|
+
trEl = skeletonEl.find('tr');
|
5049
5814
|
|
5050
5815
|
if (startCol > 0) {
|
5051
|
-
|
5052
|
-
}
|
5053
|
-
if (endCol > startCol) {
|
5054
|
-
cellHtml += '<td colspan="' + (endCol - startCol) + '" class="fc-highlight" />';
|
5816
|
+
trEl.append('<td colspan="' + startCol + '"/>');
|
5055
5817
|
}
|
5056
|
-
|
5057
|
-
|
5818
|
+
|
5819
|
+
trEl.append(
|
5820
|
+
seg.el.attr('colspan', endCol - startCol)
|
5821
|
+
);
|
5822
|
+
|
5823
|
+
if (endCol < colCnt) {
|
5824
|
+
trEl.append('<td colspan="' + (colCnt - endCol) + '"/>');
|
5058
5825
|
}
|
5059
5826
|
|
5060
|
-
|
5827
|
+
this.bookendCells(trEl, type);
|
5061
5828
|
|
5062
|
-
return
|
5063
|
-
'<div class="fc-highlight-skeleton">' +
|
5064
|
-
'<table>' +
|
5065
|
-
'<tr>' +
|
5066
|
-
cellHtml +
|
5067
|
-
'</tr>' +
|
5068
|
-
'</table>' +
|
5069
|
-
'</div>';
|
5829
|
+
return skeletonEl;
|
5070
5830
|
}
|
5071
5831
|
|
5072
5832
|
});
|
@@ -5078,67 +5838,83 @@ $.extend(DayGrid.prototype, {
|
|
5078
5838
|
|
5079
5839
|
$.extend(DayGrid.prototype, {
|
5080
5840
|
|
5081
|
-
|
5082
|
-
rowStructs: null, // an array of objects, each holding information about a row's event-rendering
|
5841
|
+
rowStructs: null, // an array of objects, each holding information about a row's foreground event-rendering
|
5083
5842
|
|
5084
5843
|
|
5085
|
-
//
|
5086
|
-
|
5087
|
-
|
5088
|
-
|
5844
|
+
// Unrenders all events currently rendered on the grid
|
5845
|
+
destroyEvents: function() {
|
5846
|
+
this.destroySegPopover(); // removes the "more.." events popover
|
5847
|
+
Grid.prototype.destroyEvents.apply(this, arguments); // calls the super-method
|
5848
|
+
},
|
5849
|
+
|
5850
|
+
|
5851
|
+
// Retrieves all rendered segment objects currently rendered on the grid
|
5852
|
+
getSegs: function() {
|
5853
|
+
return Grid.prototype.getSegs.call(this) // get the segments from the super-method
|
5854
|
+
.concat(this.popoverSegs || []); // append the segments from the "more..." popover
|
5855
|
+
},
|
5856
|
+
|
5857
|
+
|
5858
|
+
// Renders the given background event segments onto the grid
|
5859
|
+
renderBgSegs: function(segs) {
|
5860
|
+
|
5861
|
+
// don't render timed background events
|
5862
|
+
var allDaySegs = $.grep(segs, function(seg) {
|
5863
|
+
return seg.event.allDay;
|
5864
|
+
});
|
5865
|
+
|
5866
|
+
return Grid.prototype.renderBgSegs.call(this, allDaySegs); // call the super-method
|
5867
|
+
},
|
5868
|
+
|
5869
|
+
|
5870
|
+
// Renders the given foreground event segments onto the grid
|
5871
|
+
renderFgSegs: function(segs) {
|
5872
|
+
var rowStructs;
|
5873
|
+
|
5874
|
+
// render an `.el` on each seg
|
5875
|
+
// returns a subset of the segs. segs that were actually rendered
|
5876
|
+
segs = this.renderFgSegEls(segs);
|
5877
|
+
|
5878
|
+
rowStructs = this.rowStructs = this.renderSegRows(segs);
|
5089
5879
|
|
5090
5880
|
// append to each row's content skeleton
|
5091
5881
|
this.rowEls.each(function(i, rowNode) {
|
5092
5882
|
$(rowNode).find('.fc-content-skeleton > table').append(
|
5093
5883
|
rowStructs[i].tbodyEl
|
5094
5884
|
);
|
5095
|
-
segs.push.apply(segs, rowStructs[i].segs);
|
5096
5885
|
});
|
5097
5886
|
|
5098
|
-
|
5099
|
-
},
|
5100
|
-
|
5101
|
-
|
5102
|
-
// Retrieves all segment objects that have been rendered
|
5103
|
-
getSegs: function() {
|
5104
|
-
return (this.segs || []).concat(
|
5105
|
-
this.popoverSegs || [] // segs rendered in the "more" events popover
|
5106
|
-
);
|
5887
|
+
return segs; // return only the segs that were actually rendered
|
5107
5888
|
},
|
5108
5889
|
|
5109
5890
|
|
5110
|
-
//
|
5111
|
-
|
5112
|
-
var rowStructs;
|
5891
|
+
// Unrenders all currently rendered foreground event segments
|
5892
|
+
destroyFgSegs: function() {
|
5893
|
+
var rowStructs = this.rowStructs || [];
|
5113
5894
|
var rowStruct;
|
5114
5895
|
|
5115
|
-
Grid.prototype.destroyEvents.call(this); // call the super-method
|
5116
|
-
|
5117
|
-
rowStructs = this.rowStructs || [];
|
5118
5896
|
while ((rowStruct = rowStructs.pop())) {
|
5119
5897
|
rowStruct.tbodyEl.remove();
|
5120
5898
|
}
|
5121
5899
|
|
5122
|
-
this.
|
5123
|
-
this.destroySegPopover(); // removes the "more.." events popover
|
5900
|
+
this.rowStructs = null;
|
5124
5901
|
},
|
5125
5902
|
|
5126
5903
|
|
5127
5904
|
// Uses the given events array to generate <tbody> elements that should be appended to each row's content skeleton.
|
5128
|
-
// Returns an array of rowStruct objects (see the bottom of `
|
5129
|
-
|
5130
|
-
|
5905
|
+
// Returns an array of rowStruct objects (see the bottom of `renderSegRow`).
|
5906
|
+
// PRECONDITION: each segment shoud already have a rendered and assigned `.el`
|
5907
|
+
renderSegRows: function(segs) {
|
5131
5908
|
var rowStructs = [];
|
5132
5909
|
var segRows;
|
5133
5910
|
var row;
|
5134
5911
|
|
5135
|
-
segs = this.renderSegs(segs); // returns a new array with only visible segments
|
5136
5912
|
segRows = this.groupSegRows(segs); // group into nested arrays
|
5137
5913
|
|
5138
5914
|
// iterate each row of segment groupings
|
5139
5915
|
for (row = 0; row < segRows.length; row++) {
|
5140
5916
|
rowStructs.push(
|
5141
|
-
this.
|
5917
|
+
this.renderSegRow(row, segRows[row])
|
5142
5918
|
);
|
5143
5919
|
}
|
5144
5920
|
|
@@ -5147,7 +5923,7 @@ $.extend(DayGrid.prototype, {
|
|
5147
5923
|
|
5148
5924
|
|
5149
5925
|
// Builds the HTML to be used for the default element for an individual segment
|
5150
|
-
|
5926
|
+
fgSegHtml: function(seg, disableResizing) {
|
5151
5927
|
var view = this.view;
|
5152
5928
|
var isRTL = view.opt('isRTL');
|
5153
5929
|
var event = seg.event;
|
@@ -5196,7 +5972,7 @@ $.extend(DayGrid.prototype, {
|
|
5196
5972
|
|
5197
5973
|
// Given a row # and an array of segments all in the same row, render a <tbody> element, a skeleton that contains
|
5198
5974
|
// the segments. Returns object with a bunch of internal data about how the render was calculated.
|
5199
|
-
|
5975
|
+
renderSegRow: function(row, rowSegs) {
|
5200
5976
|
var view = this.view;
|
5201
5977
|
var colCnt = view.colCnt;
|
5202
5978
|
var segLevels = this.buildSegLevels(rowSegs); // group into sub-arrays of levels
|
@@ -5367,6 +6143,7 @@ function compareDaySegCols(a, b) {
|
|
5367
6143
|
|
5368
6144
|
/* Methods relate to limiting the number events for a given day on a DayGrid
|
5369
6145
|
----------------------------------------------------------------------------------------------------------------------*/
|
6146
|
+
// NOTE: all the segs being passed around in here are foreground segs
|
5370
6147
|
|
5371
6148
|
$.extend(DayGrid.prototype, {
|
5372
6149
|
|
@@ -5646,7 +6423,7 @@ $.extend(DayGrid.prototype, {
|
|
5646
6423
|
var i;
|
5647
6424
|
|
5648
6425
|
// render each seg's `el` and only return the visible segs
|
5649
|
-
segs = this.
|
6426
|
+
segs = this.renderFgSegEls(segs, true); // disableResizing=true
|
5650
6427
|
this.popoverSegs = segs;
|
5651
6428
|
|
5652
6429
|
for (i = 0; i < segs.length; i++) {
|
@@ -5664,13 +6441,23 @@ $.extend(DayGrid.prototype, {
|
|
5664
6441
|
|
5665
6442
|
// Given the events within an array of segment objects, reslice them to be in a single day
|
5666
6443
|
resliceDaySegs: function(segs, dayDate) {
|
6444
|
+
|
6445
|
+
// build an array of the original events
|
5667
6446
|
var events = $.map(segs, function(seg) {
|
5668
6447
|
return seg.event;
|
5669
6448
|
});
|
6449
|
+
|
5670
6450
|
var dayStart = dayDate.clone().stripTime();
|
5671
6451
|
var dayEnd = dayStart.clone().add(1, 'days');
|
5672
6452
|
|
5673
|
-
|
6453
|
+
// slice the events with a custom slicing function
|
6454
|
+
return this.eventsToSegs(
|
6455
|
+
events,
|
6456
|
+
function(rangeStart, rangeEnd) {
|
6457
|
+
var seg = intersectionToSeg(rangeStart, rangeEnd, dayStart, dayEnd); // if no intersection, undefined
|
6458
|
+
return seg ? [ seg ] : []; // must return an array of segments
|
6459
|
+
}
|
6460
|
+
);
|
5674
6461
|
},
|
5675
6462
|
|
5676
6463
|
|
@@ -5733,9 +6520,10 @@ $.extend(TimeGrid.prototype, {
|
|
5733
6520
|
|
5734
6521
|
slatTops: null, // an array of top positions, relative to the container. last item holds bottom of last slot
|
5735
6522
|
|
5736
|
-
highlightEl: null, // cell skeleton element for rendering the highlight
|
5737
6523
|
helperEl: null, // cell skeleton element for rendering the mock event "helper"
|
5738
6524
|
|
6525
|
+
businessHourSegs: null,
|
6526
|
+
|
5739
6527
|
|
5740
6528
|
// Renders the time grid into `this.el`, which should already be assigned.
|
5741
6529
|
// Relies on the view's colCnt. In the future, this component should probably be self-sufficient.
|
@@ -5749,10 +6537,18 @@ $.extend(TimeGrid.prototype, {
|
|
5749
6537
|
|
5750
6538
|
this.computeSlatTops();
|
5751
6539
|
|
6540
|
+
this.renderBusinessHours();
|
6541
|
+
|
5752
6542
|
Grid.prototype.render.call(this); // call the super-method
|
5753
6543
|
},
|
5754
6544
|
|
5755
6545
|
|
6546
|
+
renderBusinessHours: function() {
|
6547
|
+
var events = this.view.calendar.getBusinessHoursEvents();
|
6548
|
+
this.businessHourSegs = this.renderFill('businessHours', this.eventsToSegs(events), 'bgevent');
|
6549
|
+
},
|
6550
|
+
|
6551
|
+
|
5756
6552
|
// Renders the basic HTML skeleton for the grid
|
5757
6553
|
renderHtml: function() {
|
5758
6554
|
return '' +
|
@@ -6038,12 +6834,14 @@ $.extend(TimeGrid.prototype, {
|
|
6038
6834
|
|
6039
6835
|
// Renders a mock "helper" event. `sourceSeg` is the original segment object and might be null (an external drag)
|
6040
6836
|
renderHelper: function(event, sourceSeg) {
|
6041
|
-
var
|
6042
|
-
var tableEl
|
6043
|
-
var segs = res.segs;
|
6837
|
+
var segs = this.eventsToSegs([ event ]);
|
6838
|
+
var tableEl;
|
6044
6839
|
var i, seg;
|
6045
6840
|
var sourceEl;
|
6046
6841
|
|
6842
|
+
segs = this.renderFgSegEls(segs); // assigns each seg's el and returns a subset of segs that were rendered
|
6843
|
+
tableEl = this.renderSegTable(segs);
|
6844
|
+
|
6047
6845
|
// Try to make the segment that is in the same row as sourceSeg look the same
|
6048
6846
|
for (i = 0; i < segs.length; i++) {
|
6049
6847
|
seg = segs[i];
|
@@ -6095,79 +6893,63 @@ $.extend(TimeGrid.prototype, {
|
|
6095
6893
|
},
|
6096
6894
|
|
6097
6895
|
|
6098
|
-
/*
|
6896
|
+
/* Fill System (highlight, background events, business hours)
|
6099
6897
|
------------------------------------------------------------------------------------------------------------------*/
|
6100
6898
|
|
6101
6899
|
|
6102
|
-
// Renders
|
6103
|
-
|
6104
|
-
|
6105
|
-
this.highlightSkeletonHtml(start, end)
|
6106
|
-
).appendTo(this.el);
|
6107
|
-
},
|
6108
|
-
|
6109
|
-
|
6110
|
-
// Unrenders the emphasis on a date range
|
6111
|
-
destroyHighlight: function() {
|
6112
|
-
if (this.highlightEl) {
|
6113
|
-
this.highlightEl.remove();
|
6114
|
-
this.highlightEl = null;
|
6115
|
-
}
|
6116
|
-
},
|
6117
|
-
|
6118
|
-
|
6119
|
-
// Generates HTML for a table element with containers in each column, responsible for absolutely positioning the
|
6120
|
-
// highlight elements to cover the highlighted slots.
|
6121
|
-
highlightSkeletonHtml: function(start, end) {
|
6900
|
+
// Renders a set of rectangles over the given time segments.
|
6901
|
+
// Only returns segments that successfully rendered.
|
6902
|
+
renderFill: function(type, segs, className) {
|
6122
6903
|
var view = this.view;
|
6123
|
-
var
|
6124
|
-
var
|
6125
|
-
var
|
6126
|
-
var
|
6904
|
+
var segCols;
|
6905
|
+
var skeletonEl;
|
6906
|
+
var trEl;
|
6907
|
+
var col, colSegs;
|
6908
|
+
var tdEl;
|
6909
|
+
var containerEl;
|
6127
6910
|
var dayDate;
|
6128
|
-
var
|
6129
|
-
|
6130
|
-
for (i = 0; i < segs.length; i++) { // loop through the segments. one per column
|
6131
|
-
seg = segs[i];
|
6911
|
+
var i, seg;
|
6132
6912
|
|
6133
|
-
|
6134
|
-
if (col < seg.col) {
|
6135
|
-
cellHtml += '<td colspan="' + (seg.col - col) + '"/>';
|
6136
|
-
col = seg.col;
|
6137
|
-
}
|
6913
|
+
if (segs.length) {
|
6138
6914
|
|
6139
|
-
//
|
6140
|
-
|
6141
|
-
top = this.computeDateTop(seg.start, dayDate);
|
6142
|
-
bottom = this.computeDateTop(seg.end, dayDate); // the y position of the bottom edge
|
6915
|
+
segs = this.renderFillSegEls(type, segs); // assignes `.el` to each seg. returns successfully rendered segs
|
6916
|
+
segCols = this.groupSegCols(segs); // group into sub-arrays, and assigns 'col' to each seg
|
6143
6917
|
|
6144
|
-
|
6145
|
-
|
6146
|
-
|
6147
|
-
|
6148
|
-
|
6149
|
-
|
6150
|
-
|
6151
|
-
|
6918
|
+
className = className || type.toLowerCase();
|
6919
|
+
skeletonEl = $(
|
6920
|
+
'<div class="fc-' + className + '-skeleton">' +
|
6921
|
+
'<table><tr/></table>' +
|
6922
|
+
'</div>'
|
6923
|
+
);
|
6924
|
+
trEl = skeletonEl.find('tr');
|
6925
|
+
|
6926
|
+
for (col = 0; col < segCols.length; col++) {
|
6927
|
+
colSegs = segCols[col];
|
6928
|
+
tdEl = $('<td/>').appendTo(trEl);
|
6929
|
+
|
6930
|
+
if (colSegs.length) {
|
6931
|
+
containerEl = $('<div class="fc-' + className + '-container"/>').appendTo(tdEl);
|
6932
|
+
dayDate = view.cellToDate(0, col);
|
6933
|
+
|
6934
|
+
for (i = 0; i < colSegs.length; i++) {
|
6935
|
+
seg = colSegs[i];
|
6936
|
+
containerEl.append(
|
6937
|
+
seg.el.css({
|
6938
|
+
top: this.computeDateTop(seg.start, dayDate),
|
6939
|
+
bottom: -this.computeDateTop(seg.end, dayDate) // the y position of the bottom edge
|
6940
|
+
})
|
6941
|
+
);
|
6942
|
+
}
|
6943
|
+
}
|
6944
|
+
}
|
6152
6945
|
|
6153
|
-
|
6154
|
-
}
|
6946
|
+
this.bookendCells(trEl, type);
|
6155
6947
|
|
6156
|
-
|
6157
|
-
|
6158
|
-
cellHtml += '<td colspan="' + (view.colCnt - col) + '"/>';
|
6948
|
+
this.el.append(skeletonEl);
|
6949
|
+
this.elsByFill[type] = skeletonEl;
|
6159
6950
|
}
|
6160
6951
|
|
6161
|
-
|
6162
|
-
|
6163
|
-
return '' +
|
6164
|
-
'<div class="fc-highlight-skeleton">' +
|
6165
|
-
'<table>' +
|
6166
|
-
'<tr>' +
|
6167
|
-
cellHtml +
|
6168
|
-
'</tr>' +
|
6169
|
-
'</table>' +
|
6170
|
-
'</div>';
|
6952
|
+
return segs;
|
6171
6953
|
}
|
6172
6954
|
|
6173
6955
|
});
|
@@ -6179,52 +6961,41 @@ $.extend(TimeGrid.prototype, {
|
|
6179
6961
|
|
6180
6962
|
$.extend(TimeGrid.prototype, {
|
6181
6963
|
|
6182
|
-
segs: null, // segment objects rendered in the component. null of events haven't been rendered yet
|
6183
6964
|
eventSkeletonEl: null, // has cells with event-containers, which contain absolutely positioned event elements
|
6184
6965
|
|
6185
6966
|
|
6186
|
-
// Renders the
|
6187
|
-
|
6188
|
-
|
6189
|
-
|
6190
|
-
this.eventSkeletonEl = $('<div class="fc-content-skeleton"/>').append(res.tableEl);
|
6191
|
-
this.el.append(this.eventSkeletonEl);
|
6192
|
-
|
6193
|
-
this.segs = res.segs;
|
6194
|
-
},
|
6967
|
+
// Renders the given foreground event segments onto the grid
|
6968
|
+
renderFgSegs: function(segs) {
|
6969
|
+
segs = this.renderFgSegEls(segs); // returns a subset of the segs. segs that were actually rendered
|
6195
6970
|
|
6971
|
+
this.el.append(
|
6972
|
+
this.eventSkeletonEl = $('<div class="fc-content-skeleton"/>')
|
6973
|
+
.append(this.renderSegTable(segs))
|
6974
|
+
);
|
6196
6975
|
|
6197
|
-
|
6198
|
-
getSegs: function() {
|
6199
|
-
return this.segs || [];
|
6976
|
+
return segs; // return only the segs that were actually rendered
|
6200
6977
|
},
|
6201
6978
|
|
6202
6979
|
|
6203
|
-
//
|
6204
|
-
|
6205
|
-
Grid.prototype.destroyEvents.call(this); // call the super-method
|
6206
|
-
|
6980
|
+
// Unrenders all currently rendered foreground event segments
|
6981
|
+
destroyFgSegs: function(segs) {
|
6207
6982
|
if (this.eventSkeletonEl) {
|
6208
6983
|
this.eventSkeletonEl.remove();
|
6209
6984
|
this.eventSkeletonEl = null;
|
6210
6985
|
}
|
6211
|
-
|
6212
|
-
this.segs = null;
|
6213
6986
|
},
|
6214
6987
|
|
6215
6988
|
|
6216
6989
|
// Renders and returns the <table> portion of the event-skeleton.
|
6217
6990
|
// Returns an object with properties 'tbodyEl' and 'segs'.
|
6218
|
-
|
6991
|
+
renderSegTable: function(segs) {
|
6219
6992
|
var tableEl = $('<table><tr/></table>');
|
6220
6993
|
var trEl = tableEl.find('tr');
|
6221
|
-
var segs = this.eventsToSegs(events);
|
6222
6994
|
var segCols;
|
6223
6995
|
var i, seg;
|
6224
6996
|
var col, colSegs;
|
6225
6997
|
var containerEl;
|
6226
6998
|
|
6227
|
-
segs = this.renderSegs(segs); // returns only the visible segs
|
6228
6999
|
segCols = this.groupSegCols(segs); // group into sub-arrays, and assigns 'col' to each seg
|
6229
7000
|
|
6230
7001
|
this.computeSegVerticals(segs); // compute and assign top/bottom
|
@@ -6253,26 +7024,22 @@ $.extend(TimeGrid.prototype, {
|
|
6253
7024
|
|
6254
7025
|
this.bookendCells(trEl, 'eventSkeleton');
|
6255
7026
|
|
6256
|
-
return
|
6257
|
-
tableEl: tableEl,
|
6258
|
-
segs: segs
|
6259
|
-
};
|
7027
|
+
return tableEl;
|
6260
7028
|
},
|
6261
7029
|
|
6262
7030
|
|
6263
7031
|
// Refreshes the CSS top/bottom coordinates for each segment element. Probably after a window resize/zoom.
|
7032
|
+
// Repositions business hours segs too, so not just for events. Maybe shouldn't be here.
|
6264
7033
|
updateSegVerticals: function() {
|
6265
|
-
var
|
7034
|
+
var allSegs = (this.segs || []).concat(this.businessHourSegs || []);
|
6266
7035
|
var i;
|
6267
7036
|
|
6268
|
-
|
6269
|
-
this.computeSegVerticals(segs);
|
7037
|
+
this.computeSegVerticals(allSegs);
|
6270
7038
|
|
6271
|
-
|
6272
|
-
|
6273
|
-
|
6274
|
-
|
6275
|
-
}
|
7039
|
+
for (i = 0; i < allSegs.length; i++) {
|
7040
|
+
allSegs[i].el.css(
|
7041
|
+
this.generateSegVerticalCss(allSegs[i])
|
7042
|
+
);
|
6276
7043
|
}
|
6277
7044
|
},
|
6278
7045
|
|
@@ -6290,7 +7057,7 @@ $.extend(TimeGrid.prototype, {
|
|
6290
7057
|
|
6291
7058
|
|
6292
7059
|
// Renders the HTML for a single event segment's default rendering
|
6293
|
-
|
7060
|
+
fgSegHtml: function(seg, disableResizing) {
|
6294
7061
|
var view = this.view;
|
6295
7062
|
var event = seg.event;
|
6296
7063
|
var isDraggable = view.isEventDraggable(event);
|
@@ -6858,32 +7625,81 @@ View.prototype = {
|
|
6858
7625
|
// Gets called when jqui's 'dragstart' is fired.
|
6859
7626
|
documentDragStart: function(ev, ui) {
|
6860
7627
|
var _this = this;
|
6861
|
-
var
|
7628
|
+
var calendar = this.calendar;
|
7629
|
+
var eventStart = null; // a null value signals an unsuccessful drag
|
7630
|
+
var eventEnd = null;
|
7631
|
+
var visibleEnd = null; // will be calculated event when no eventEnd
|
7632
|
+
var el;
|
7633
|
+
var accept;
|
7634
|
+
var meta;
|
7635
|
+
var eventProps; // if an object, signals an event should be created upon drop
|
6862
7636
|
var dragListener;
|
6863
7637
|
|
6864
7638
|
if (this.opt('droppable')) { // only listen if this setting is on
|
7639
|
+
el = $(ev.target);
|
7640
|
+
|
7641
|
+
// Test that the dragged element passes the dropAccept selector or filter function.
|
7642
|
+
// FYI, the default is "*" (matches all)
|
7643
|
+
accept = this.opt('dropAccept');
|
7644
|
+
if ($.isFunction(accept) ? accept.call(el[0], el) : el.is(accept)) {
|
7645
|
+
|
7646
|
+
meta = getDraggedElMeta(el); // data for possibly creating an event
|
7647
|
+
eventProps = meta.eventProps;
|
7648
|
+
|
7649
|
+
// listener that tracks mouse movement over date-associated pixel regions
|
7650
|
+
dragListener = new DragListener(this.coordMap, {
|
7651
|
+
cellOver: function(cell, cellDate) {
|
7652
|
+
eventStart = cellDate;
|
7653
|
+
eventEnd = meta.duration ? eventStart.clone().add(meta.duration) : null;
|
7654
|
+
visibleEnd = eventEnd || calendar.getDefaultEventEnd(!eventStart.hasTime(), eventStart);
|
7655
|
+
|
7656
|
+
// keep the start/end up to date when dragging
|
7657
|
+
if (eventProps) {
|
7658
|
+
$.extend(eventProps, { start: eventStart, end: eventEnd });
|
7659
|
+
}
|
7660
|
+
|
7661
|
+
if (calendar.isExternalDragAllowedInRange(eventStart, visibleEnd, eventProps)) {
|
7662
|
+
_this.renderDrag(eventStart, visibleEnd);
|
7663
|
+
}
|
7664
|
+
else {
|
7665
|
+
eventStart = null; // signal unsuccessful
|
7666
|
+
disableCursor();
|
7667
|
+
}
|
7668
|
+
},
|
7669
|
+
cellOut: function() {
|
7670
|
+
eventStart = null;
|
7671
|
+
_this.destroyDrag();
|
7672
|
+
enableCursor();
|
7673
|
+
}
|
7674
|
+
});
|
7675
|
+
|
7676
|
+
// gets called, only once, when jqui drag is finished
|
7677
|
+
$(document).one('dragstop', function(ev, ui) {
|
7678
|
+
var renderedEvents;
|
6865
7679
|
|
6866
|
-
// listener that tracks mouse movement over date-associated pixel regions
|
6867
|
-
dragListener = new DragListener(this.coordMap, {
|
6868
|
-
cellOver: function(cell, date) {
|
6869
|
-
dropDate = date;
|
6870
|
-
_this.renderDrag(date);
|
6871
|
-
},
|
6872
|
-
cellOut: function() {
|
6873
|
-
dropDate = null;
|
6874
7680
|
_this.destroyDrag();
|
6875
|
-
|
6876
|
-
});
|
7681
|
+
enableCursor();
|
6877
7682
|
|
6878
|
-
|
6879
|
-
|
6880
|
-
|
6881
|
-
|
6882
|
-
|
6883
|
-
|
6884
|
-
|
7683
|
+
if (eventStart) { // element was dropped on a valid date/time cell
|
7684
|
+
|
7685
|
+
// if dropped on an all-day cell, and element's metadata specified a time, set it
|
7686
|
+
if (meta.startTime && !eventStart.hasTime()) {
|
7687
|
+
eventStart.time(meta.startTime);
|
7688
|
+
}
|
7689
|
+
|
7690
|
+
// trigger 'drop' regardless of whether element represents an event
|
7691
|
+
_this.trigger('drop', el[0], eventStart, ev, ui);
|
7692
|
+
|
7693
|
+
// create an event from the given properties and the latest dates
|
7694
|
+
if (eventProps) {
|
7695
|
+
renderedEvents = calendar.renderEvent(eventProps, meta.stick);
|
7696
|
+
_this.trigger('eventReceive', null, renderedEvents[0]); // signal an external event landed
|
7697
|
+
}
|
7698
|
+
}
|
7699
|
+
});
|
6885
7700
|
|
6886
|
-
|
7701
|
+
dragListener.startDrag(ev); // start listening immediately
|
7702
|
+
}
|
6887
7703
|
}
|
6888
7704
|
},
|
6889
7705
|
|
@@ -7414,6 +8230,60 @@ function View(calendar) {
|
|
7414
8230
|
|
7415
8231
|
}
|
7416
8232
|
|
8233
|
+
|
8234
|
+
/* Utils
|
8235
|
+
----------------------------------------------------------------------------------------------------------------------*/
|
8236
|
+
|
8237
|
+
// Require all HTML5 data-* attributes used by FullCalendar to have this prefix.
|
8238
|
+
// A value of '' will query attributes like data-event. A value of 'fc' will query attributes like data-fc-event.
|
8239
|
+
fc.dataAttrPrefix = '';
|
8240
|
+
|
8241
|
+
// Given a jQuery element that might represent a dragged FullCalendar event, returns an intermediate data structure
|
8242
|
+
// to be used for Event Object creation.
|
8243
|
+
// A defined `.eventProps`, even when empty, indicates that an event should be created.
|
8244
|
+
function getDraggedElMeta(el) {
|
8245
|
+
var prefix = fc.dataAttrPrefix;
|
8246
|
+
var eventProps; // properties for creating the event, not related to date/time
|
8247
|
+
var startTime; // a Duration
|
8248
|
+
var duration;
|
8249
|
+
var stick;
|
8250
|
+
|
8251
|
+
if (prefix) { prefix += '-'; }
|
8252
|
+
eventProps = el.data(prefix + 'event') || null;
|
8253
|
+
|
8254
|
+
if (eventProps) {
|
8255
|
+
if (typeof eventProps === 'object') {
|
8256
|
+
eventProps = $.extend({}, eventProps); // make a copy
|
8257
|
+
}
|
8258
|
+
else { // something like 1 or true. still signal event creation
|
8259
|
+
eventProps = {};
|
8260
|
+
}
|
8261
|
+
|
8262
|
+
// pluck special-cased date/time properties
|
8263
|
+
startTime = eventProps.start;
|
8264
|
+
if (startTime == null) { startTime = eventProps.time; } // accept 'time' as well
|
8265
|
+
duration = eventProps.duration;
|
8266
|
+
stick = eventProps.stick;
|
8267
|
+
delete eventProps.start;
|
8268
|
+
delete eventProps.time;
|
8269
|
+
delete eventProps.duration;
|
8270
|
+
delete eventProps.stick;
|
8271
|
+
}
|
8272
|
+
|
8273
|
+
// fallback to standalone attribute values for each of the date/time properties
|
8274
|
+
if (startTime == null) { startTime = el.data(prefix + 'start'); }
|
8275
|
+
if (startTime == null) { startTime = el.data(prefix + 'time'); } // accept 'time' as well
|
8276
|
+
if (duration == null) { duration = el.data(prefix + 'duration'); }
|
8277
|
+
if (stick == null) { stick = el.data(prefix + 'stick'); }
|
8278
|
+
|
8279
|
+
// massage into correct data types
|
8280
|
+
startTime = startTime != null ? moment.duration(startTime) : null;
|
8281
|
+
duration = duration != null ? moment.duration(duration) : null;
|
8282
|
+
stick = Boolean(stick);
|
8283
|
+
|
8284
|
+
return { eventProps: eventProps, startTime: startTime, duration: duration, stick: stick };
|
8285
|
+
}
|
8286
|
+
|
7417
8287
|
;;
|
7418
8288
|
|
7419
8289
|
/* An abstract class for the "basic" views, as well as month view. Renders one or more rows of day cells.
|