fullcalendar.io-rails 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/fullcalendar.io/rails/version.rb +1 -1
- data/vendor/assets/javascripts/fullcalendar.js +1143 -489
- data/vendor/assets/javascripts/fullcalendar/gcal.js +1 -1
- data/vendor/assets/stylesheets/fullcalendar.css +10 -1
- data/vendor/assets/stylesheets/fullcalendar.print.css +1 -1
- metadata +99 -645
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fff0c0d29fd03d1936961495bfeca8ee0954dbb7
|
4
|
+
data.tar.gz: f126510b73657173511b0d60ef8733f76cc5678e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c15e22c3fffaea4c7c253fff9090216f5954000c18cc5668f37e1450c0db57ed281e4354fbf08ade8552cf26f232f55023609e1bec77f45a32c92a734b04c21d
|
7
|
+
data.tar.gz: bbf9a890a7af974c9f87978cfdf0438a7f53e198b057cdc7fb9b24fa5be55b0a42f74c965bbe049d0090b7b4b15671478b43ac32c0729991378fc70b3dd0d23e
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*!
|
2
|
-
* FullCalendar v3.
|
2
|
+
* FullCalendar v3.3.0
|
3
3
|
* Docs & License: https://fullcalendar.io/
|
4
4
|
* (c) 2017 Adam Shaw
|
5
5
|
*/
|
@@ -19,7 +19,7 @@
|
|
19
19
|
;;
|
20
20
|
|
21
21
|
var FC = $.fullCalendar = {
|
22
|
-
version: "3.
|
22
|
+
version: "3.3.0",
|
23
23
|
// When introducing internal API incompatibilities (where fullcalendar plugins would break),
|
24
24
|
// the minor version of the calendar should be upped (ex: 2.7.2 -> 2.8.0)
|
25
25
|
// and the below integer should be incremented.
|
@@ -623,14 +623,14 @@ function intersectRanges(subjectRange, constraintRange) {
|
|
623
623
|
/* Date Utilities
|
624
624
|
----------------------------------------------------------------------------------------------------------------------*/
|
625
625
|
|
626
|
-
FC.
|
626
|
+
FC.computeGreatestUnit = computeGreatestUnit;
|
627
627
|
FC.divideRangeByDuration = divideRangeByDuration;
|
628
628
|
FC.divideDurationByDuration = divideDurationByDuration;
|
629
629
|
FC.multiplyDuration = multiplyDuration;
|
630
630
|
FC.durationHasTime = durationHasTime;
|
631
631
|
|
632
632
|
var dayIDs = [ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ];
|
633
|
-
var
|
633
|
+
var unitsDesc = [ 'year', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond' ]; // descending
|
634
634
|
|
635
635
|
|
636
636
|
// Diffs the two moments into a Duration where full-days are recorded first, then the remaining time.
|
@@ -663,12 +663,12 @@ function diffByUnit(a, b, unit) {
|
|
663
663
|
// Computes the unit name of the largest whole-unit period of time.
|
664
664
|
// For example, 48 hours will be "days" whereas 49 hours will be "hours".
|
665
665
|
// Accepts start/end, a range object, or an original duration object.
|
666
|
-
function
|
666
|
+
function computeGreatestUnit(start, end) {
|
667
667
|
var i, unit;
|
668
668
|
var val;
|
669
669
|
|
670
|
-
for (i = 0; i <
|
671
|
-
unit =
|
670
|
+
for (i = 0; i < unitsDesc.length; i++) {
|
671
|
+
unit = unitsDesc[i];
|
672
672
|
val = computeRangeAs(unit, start, end);
|
673
673
|
|
674
674
|
if (val >= 1 && isInt(val)) {
|
@@ -747,6 +747,88 @@ function multiplyDuration(dur, n) {
|
|
747
747
|
}
|
748
748
|
|
749
749
|
|
750
|
+
function cloneRange(range) {
|
751
|
+
return {
|
752
|
+
start: range.start.clone(),
|
753
|
+
end: range.end.clone()
|
754
|
+
};
|
755
|
+
}
|
756
|
+
|
757
|
+
|
758
|
+
// Trims the beginning and end of inner range to be completely within outerRange.
|
759
|
+
// Returns a new range object.
|
760
|
+
function constrainRange(innerRange, outerRange) {
|
761
|
+
innerRange = cloneRange(innerRange);
|
762
|
+
|
763
|
+
if (outerRange.start) {
|
764
|
+
// needs to be inclusively before outerRange's end
|
765
|
+
innerRange.start = constrainDate(innerRange.start, outerRange);
|
766
|
+
}
|
767
|
+
|
768
|
+
if (outerRange.end) {
|
769
|
+
innerRange.end = minMoment(innerRange.end, outerRange.end);
|
770
|
+
}
|
771
|
+
|
772
|
+
return innerRange;
|
773
|
+
}
|
774
|
+
|
775
|
+
|
776
|
+
// If the given date is not within the given range, move it inside.
|
777
|
+
// (If it's past the end, make it one millisecond before the end).
|
778
|
+
// Always returns a new moment.
|
779
|
+
function constrainDate(date, range) {
|
780
|
+
date = date.clone();
|
781
|
+
|
782
|
+
if (range.start) {
|
783
|
+
date = maxMoment(date, range.start);
|
784
|
+
}
|
785
|
+
|
786
|
+
if (range.end && date >= range.end) {
|
787
|
+
date = range.end.clone().subtract(1);
|
788
|
+
}
|
789
|
+
|
790
|
+
return date;
|
791
|
+
}
|
792
|
+
|
793
|
+
|
794
|
+
function isDateWithinRange(date, range) {
|
795
|
+
return (!range.start || date >= range.start) &&
|
796
|
+
(!range.end || date < range.end);
|
797
|
+
}
|
798
|
+
|
799
|
+
|
800
|
+
// TODO: deal with repeat code in intersectRanges
|
801
|
+
// constraintRange can have unspecified start/end, an open-ended range.
|
802
|
+
function doRangesIntersect(subjectRange, constraintRange) {
|
803
|
+
return (!constraintRange.start || subjectRange.end >= constraintRange.start) &&
|
804
|
+
(!constraintRange.end || subjectRange.start < constraintRange.end);
|
805
|
+
}
|
806
|
+
|
807
|
+
|
808
|
+
function isRangeWithinRange(innerRange, outerRange) {
|
809
|
+
return (!outerRange.start || innerRange.start >= outerRange.start) &&
|
810
|
+
(!outerRange.end || innerRange.end <= outerRange.end);
|
811
|
+
}
|
812
|
+
|
813
|
+
|
814
|
+
function isRangesEqual(range0, range1) {
|
815
|
+
return ((range0.start && range1.start && range0.start.isSame(range1.start)) || (!range0.start && !range1.start)) &&
|
816
|
+
((range0.end && range1.end && range0.end.isSame(range1.end)) || (!range0.end && !range1.end));
|
817
|
+
}
|
818
|
+
|
819
|
+
|
820
|
+
// Returns the moment that's earlier in time. Always a copy.
|
821
|
+
function minMoment(mom1, mom2) {
|
822
|
+
return (mom1.isBefore(mom2) ? mom1 : mom2).clone();
|
823
|
+
}
|
824
|
+
|
825
|
+
|
826
|
+
// Returns the moment that's later in time. Always a copy.
|
827
|
+
function maxMoment(mom1, mom2) {
|
828
|
+
return (mom1.isAfter(mom2) ? mom1 : mom2).clone();
|
829
|
+
}
|
830
|
+
|
831
|
+
|
750
832
|
// Returns a boolean about whether the given duration has any time parts (hours/minutes/seconds/ms)
|
751
833
|
function durationHasTime(dur) {
|
752
834
|
return Boolean(dur.hours() || dur.minutes() || dur.seconds() || dur.milliseconds());
|
@@ -2586,6 +2668,7 @@ var DragListener = FC.DragListener = Class.extend(ListenerMixin, {
|
|
2586
2668
|
isDelayEnded: false,
|
2587
2669
|
isDragging: false,
|
2588
2670
|
isTouch: false,
|
2671
|
+
isGeneric: false, // initiated by 'dragstart' (jqui)
|
2589
2672
|
|
2590
2673
|
delay: null,
|
2591
2674
|
delayTimeoutId: null,
|
@@ -2605,7 +2688,6 @@ var DragListener = FC.DragListener = Class.extend(ListenerMixin, {
|
|
2605
2688
|
|
2606
2689
|
|
2607
2690
|
startInteraction: function(ev, extraOptions) {
|
2608
|
-
var isTouch = getEvIsTouch(ev);
|
2609
2691
|
|
2610
2692
|
if (ev.type === 'mousedown') {
|
2611
2693
|
if (GlobalEmitter.get().shouldIgnoreMouse()) {
|
@@ -2630,7 +2712,8 @@ var DragListener = FC.DragListener = Class.extend(ListenerMixin, {
|
|
2630
2712
|
preventSelection($('body'));
|
2631
2713
|
|
2632
2714
|
this.isInteracting = true;
|
2633
|
-
this.isTouch =
|
2715
|
+
this.isTouch = getEvIsTouch(ev);
|
2716
|
+
this.isGeneric = ev.type === 'dragstart';
|
2634
2717
|
this.isDelayEnded = false;
|
2635
2718
|
this.isDistanceSurpassed = false;
|
2636
2719
|
|
@@ -2689,7 +2772,13 @@ var DragListener = FC.DragListener = Class.extend(ListenerMixin, {
|
|
2689
2772
|
// so listen to the GlobalEmitter singleton, which is always bound, instead of the document directly.
|
2690
2773
|
var globalEmitter = GlobalEmitter.get();
|
2691
2774
|
|
2692
|
-
if (this.
|
2775
|
+
if (this.isGeneric) {
|
2776
|
+
this.listenTo($(document), { // might only work on iOS because of GlobalEmitter's bind :(
|
2777
|
+
drag: this.handleMove,
|
2778
|
+
dragstop: this.endInteraction
|
2779
|
+
});
|
2780
|
+
}
|
2781
|
+
else if (this.isTouch) {
|
2693
2782
|
this.listenTo(globalEmitter, {
|
2694
2783
|
touchmove: this.handleTouchMove,
|
2695
2784
|
touchend: this.endInteraction,
|
@@ -2712,6 +2801,7 @@ var DragListener = FC.DragListener = Class.extend(ListenerMixin, {
|
|
2712
2801
|
|
2713
2802
|
unbindHandlers: function() {
|
2714
2803
|
this.stopListeningTo(GlobalEmitter.get());
|
2804
|
+
this.stopListeningTo($(document)); // for isGeneric
|
2715
2805
|
},
|
2716
2806
|
|
2717
2807
|
|
@@ -3856,6 +3946,18 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
|
|
3856
3946
|
},
|
3857
3947
|
|
3858
3948
|
|
3949
|
+
// like getHitSpan, but returns null if the resulting span's range is invalid
|
3950
|
+
getSafeHitSpan: function(hit) {
|
3951
|
+
var hitSpan = this.getHitSpan(hit);
|
3952
|
+
|
3953
|
+
if (!isRangeWithinRange(hitSpan, this.view.activeRange)) {
|
3954
|
+
return null;
|
3955
|
+
}
|
3956
|
+
|
3957
|
+
return hitSpan;
|
3958
|
+
},
|
3959
|
+
|
3960
|
+
|
3859
3961
|
// Given position-level information about a date-related area within the grid,
|
3860
3962
|
// should return an object with at least a start/end date. Can provide other information as well.
|
3861
3963
|
getHitSpan: function(hit) {
|
@@ -3966,9 +4068,9 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
|
|
3966
4068
|
dayMousedown: function(ev) {
|
3967
4069
|
var view = this.view;
|
3968
4070
|
|
3969
|
-
//
|
3970
|
-
//
|
3971
|
-
if (
|
4071
|
+
// HACK
|
4072
|
+
// This will still work even though bindDayHandler doesn't use GlobalEmitter.
|
4073
|
+
if (GlobalEmitter.get().shouldIgnoreMouse()) {
|
3972
4074
|
return;
|
3973
4075
|
}
|
3974
4076
|
|
@@ -3986,8 +4088,10 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
|
|
3986
4088
|
var view = this.view;
|
3987
4089
|
var selectLongPressDelay;
|
3988
4090
|
|
3989
|
-
//
|
3990
|
-
//
|
4091
|
+
// On iOS (and Android?) when a new selection is initiated overtop another selection,
|
4092
|
+
// the touchend never fires because the elements gets removed mid-touch-interaction (my theory).
|
4093
|
+
// HACK: simply don't allow this to happen.
|
4094
|
+
// ALSO: prevent selection when an *event* is already raised.
|
3991
4095
|
if (view.isSelected || view.selectedEvent) {
|
3992
4096
|
return;
|
3993
4097
|
}
|
@@ -4028,12 +4132,14 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
|
|
4028
4132
|
dayClickHit = null;
|
4029
4133
|
},
|
4030
4134
|
interactionEnd: function(ev, isCancelled) {
|
4135
|
+
var hitSpan;
|
4136
|
+
|
4031
4137
|
if (!isCancelled && dayClickHit) {
|
4032
|
-
|
4033
|
-
|
4034
|
-
|
4035
|
-
ev
|
4036
|
-
|
4138
|
+
hitSpan = _this.getSafeHitSpan(dayClickHit);
|
4139
|
+
|
4140
|
+
if (hitSpan) {
|
4141
|
+
view.triggerDayClick(hitSpan, _this.getHitEl(dayClickHit), ev);
|
4142
|
+
}
|
4037
4143
|
}
|
4038
4144
|
}
|
4039
4145
|
});
|
@@ -4063,12 +4169,20 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
|
|
4063
4169
|
view.unselect(); // since we could be rendering a new selection, we want to clear any old one
|
4064
4170
|
},
|
4065
4171
|
hitOver: function(hit, isOrig, origHit) {
|
4172
|
+
var origHitSpan;
|
4173
|
+
var hitSpan;
|
4174
|
+
|
4066
4175
|
if (origHit) { // click needs to have started on a hit
|
4067
4176
|
|
4068
|
-
|
4069
|
-
|
4070
|
-
|
4071
|
-
)
|
4177
|
+
origHitSpan = _this.getSafeHitSpan(origHit);
|
4178
|
+
hitSpan = _this.getSafeHitSpan(hit);
|
4179
|
+
|
4180
|
+
if (origHitSpan && hitSpan) {
|
4181
|
+
selectionSpan = _this.computeSelection(origHitSpan, hitSpan);
|
4182
|
+
}
|
4183
|
+
else {
|
4184
|
+
selectionSpan = null;
|
4185
|
+
}
|
4072
4186
|
|
4073
4187
|
if (selectionSpan) {
|
4074
4188
|
_this.renderSelection(selectionSpan);
|
@@ -4357,28 +4471,37 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
|
|
4357
4471
|
// Computes HTML classNames for a single-day element
|
4358
4472
|
getDayClasses: function(date, noThemeHighlight) {
|
4359
4473
|
var view = this.view;
|
4360
|
-
var
|
4361
|
-
var
|
4474
|
+
var classes = [];
|
4475
|
+
var today;
|
4362
4476
|
|
4363
|
-
if (
|
4364
|
-
|
4365
|
-
date.month() != view.intervalStart.month()
|
4366
|
-
) {
|
4367
|
-
classes.push('fc-other-month');
|
4477
|
+
if (!isDateWithinRange(date, view.activeRange)) {
|
4478
|
+
classes.push('fc-disabled-day'); // TODO: jQuery UI theme?
|
4368
4479
|
}
|
4480
|
+
else {
|
4481
|
+
classes.push('fc-' + dayIDs[date.day()]);
|
4482
|
+
|
4483
|
+
if (
|
4484
|
+
view.currentRangeAs('months') == 1 && // TODO: somehow get into MonthView
|
4485
|
+
date.month() != view.currentRange.start.month()
|
4486
|
+
) {
|
4487
|
+
classes.push('fc-other-month');
|
4488
|
+
}
|
4489
|
+
|
4490
|
+
today = view.calendar.getNow();
|
4369
4491
|
|
4370
|
-
|
4371
|
-
|
4492
|
+
if (date.isSame(today, 'day')) {
|
4493
|
+
classes.push('fc-today');
|
4372
4494
|
|
4373
|
-
|
4374
|
-
|
4495
|
+
if (noThemeHighlight !== true) {
|
4496
|
+
classes.push(view.highlightStateClass);
|
4497
|
+
}
|
4498
|
+
}
|
4499
|
+
else if (date < today) {
|
4500
|
+
classes.push('fc-past');
|
4501
|
+
}
|
4502
|
+
else {
|
4503
|
+
classes.push('fc-future');
|
4375
4504
|
}
|
4376
|
-
}
|
4377
|
-
else if (date < today) {
|
4378
|
-
classes.push('fc-past');
|
4379
|
-
}
|
4380
|
-
else {
|
4381
|
-
classes.push('fc-future');
|
4382
4505
|
}
|
4383
4506
|
|
4384
4507
|
return classes;
|
@@ -4389,7 +4512,17 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, {
|
|
4389
4512
|
;;
|
4390
4513
|
|
4391
4514
|
/* Event-rendering and event-interaction methods for the abstract Grid class
|
4392
|
-
|
4515
|
+
----------------------------------------------------------------------------------------------------------------------
|
4516
|
+
|
4517
|
+
Data Types:
|
4518
|
+
event - { title, id, start, (end), whatever }
|
4519
|
+
location - { start, (end), allDay }
|
4520
|
+
rawEventRange - { start, end }
|
4521
|
+
eventRange - { start, end, isStart, isEnd }
|
4522
|
+
eventSpan - { start, end, isStart, isEnd, whatever }
|
4523
|
+
eventSeg - { event, whatever }
|
4524
|
+
seg - { whatever }
|
4525
|
+
*/
|
4393
4526
|
|
4394
4527
|
Grid.mixin({
|
4395
4528
|
|
@@ -4597,8 +4730,8 @@ Grid.mixin({
|
|
4597
4730
|
if (!events.length && businessHours) {
|
4598
4731
|
events = [
|
4599
4732
|
$.extend({}, BUSINESS_HOUR_EVENT_DEFAULTS, {
|
4600
|
-
start: this.view.end, // guaranteed out-of-range
|
4601
|
-
end: this.view.end, // "
|
4733
|
+
start: this.view.activeRange.end, // guaranteed out-of-range
|
4734
|
+
end: this.view.activeRange.end, // "
|
4602
4735
|
dow: null
|
4603
4736
|
})
|
4604
4737
|
];
|
@@ -4752,7 +4885,6 @@ Grid.mixin({
|
|
4752
4885
|
buildSegDragListener: function(seg) {
|
4753
4886
|
var _this = this;
|
4754
4887
|
var view = this.view;
|
4755
|
-
var calendar = view.calendar;
|
4756
4888
|
var el = seg.el;
|
4757
4889
|
var event = seg.event;
|
4758
4890
|
var isDragging;
|
@@ -4793,6 +4925,9 @@ Grid.mixin({
|
|
4793
4925
|
view.hideEvent(event); // hide all event segments. our mouseFollower will take over
|
4794
4926
|
},
|
4795
4927
|
hitOver: function(hit, isOrig, origHit) {
|
4928
|
+
var isAllowed = true;
|
4929
|
+
var origHitSpan;
|
4930
|
+
var hitSpan;
|
4796
4931
|
var dragHelperEls;
|
4797
4932
|
|
4798
4933
|
// starting hit could be forced (DayGrid.limit)
|
@@ -4800,16 +4935,21 @@ Grid.mixin({
|
|
4800
4935
|
origHit = seg.hit;
|
4801
4936
|
}
|
4802
4937
|
|
4803
|
-
//
|
4804
|
-
|
4805
|
-
|
4806
|
-
hit.component.getHitSpan(hit),
|
4807
|
-
event
|
4808
|
-
);
|
4938
|
+
// hit might not belong to this grid, so query origin grid
|
4939
|
+
origHitSpan = origHit.component.getSafeHitSpan(origHit);
|
4940
|
+
hitSpan = hit.component.getSafeHitSpan(hit);
|
4809
4941
|
|
4810
|
-
if (
|
4811
|
-
|
4942
|
+
if (origHitSpan && hitSpan) {
|
4943
|
+
dropLocation = _this.computeEventDrop(origHitSpan, hitSpan, event);
|
4944
|
+
isAllowed = dropLocation && _this.isEventLocationAllowed(dropLocation, event);
|
4945
|
+
}
|
4946
|
+
else {
|
4947
|
+
isAllowed = false;
|
4948
|
+
}
|
4949
|
+
|
4950
|
+
if (!isAllowed) {
|
4812
4951
|
dropLocation = null;
|
4952
|
+
disableCursor();
|
4813
4953
|
}
|
4814
4954
|
|
4815
4955
|
// if a valid drop location, have the subclass render a visual indication
|
@@ -4991,7 +5131,7 @@ Grid.mixin({
|
|
4991
5131
|
// Called when a jQuery UI drag starts and it needs to be monitored for dropping
|
4992
5132
|
listenToExternalDrag: function(el, ev, ui) {
|
4993
5133
|
var _this = this;
|
4994
|
-
var
|
5134
|
+
var view = this.view;
|
4995
5135
|
var meta = getDraggedElMeta(el); // extra data about event drop, including possible event to create
|
4996
5136
|
var dropLocation; // a null value signals an unsuccessful drag
|
4997
5137
|
|
@@ -5001,17 +5141,20 @@ Grid.mixin({
|
|
5001
5141
|
_this.isDraggingExternal = true;
|
5002
5142
|
},
|
5003
5143
|
hitOver: function(hit) {
|
5004
|
-
|
5005
|
-
|
5006
|
-
meta
|
5007
|
-
);
|
5144
|
+
var isAllowed = true;
|
5145
|
+
var hitSpan = hit.component.getSafeHitSpan(hit); // hit might not belong to this grid
|
5008
5146
|
|
5009
|
-
if (
|
5010
|
-
dropLocation
|
5011
|
-
|
5012
|
-
|
5013
|
-
|
5147
|
+
if (hitSpan) {
|
5148
|
+
dropLocation = _this.computeExternalDrop(hitSpan, meta);
|
5149
|
+
isAllowed = dropLocation && _this.isExternalLocationAllowed(dropLocation, meta.eventProps);
|
5150
|
+
}
|
5151
|
+
else {
|
5152
|
+
isAllowed = false;
|
5153
|
+
}
|
5154
|
+
|
5155
|
+
if (!isAllowed) {
|
5014
5156
|
dropLocation = null;
|
5157
|
+
disableCursor();
|
5015
5158
|
}
|
5016
5159
|
|
5017
5160
|
if (dropLocation) {
|
@@ -5027,7 +5170,7 @@ Grid.mixin({
|
|
5027
5170
|
},
|
5028
5171
|
interactionEnd: function(ev) {
|
5029
5172
|
if (dropLocation) { // element was dropped on a valid hit
|
5030
|
-
|
5173
|
+
view.reportExternalDrop(meta, dropLocation, el, ev, ui);
|
5031
5174
|
}
|
5032
5175
|
_this.isDraggingExternal = false;
|
5033
5176
|
_this.externalDragListener = null;
|
@@ -5112,23 +5255,31 @@ Grid.mixin({
|
|
5112
5255
|
_this.segResizeStart(seg, ev);
|
5113
5256
|
},
|
5114
5257
|
hitOver: function(hit, isOrig, origHit) {
|
5115
|
-
var
|
5116
|
-
var
|
5258
|
+
var isAllowed = true;
|
5259
|
+
var origHitSpan = _this.getSafeHitSpan(origHit);
|
5260
|
+
var hitSpan = _this.getSafeHitSpan(hit);
|
5117
5261
|
|
5118
|
-
|
5119
|
-
|
5120
|
-
|
5262
|
+
if (origHitSpan && hitSpan) {
|
5263
|
+
resizeLocation = isStart ?
|
5264
|
+
_this.computeEventStartResize(origHitSpan, hitSpan, event) :
|
5265
|
+
_this.computeEventEndResize(origHitSpan, hitSpan, event);
|
5121
5266
|
|
5122
|
-
|
5123
|
-
|
5124
|
-
|
5125
|
-
|
5126
|
-
|
5127
|
-
|
5128
|
-
|
5267
|
+
isAllowed = resizeLocation && _this.isEventLocationAllowed(resizeLocation, event);
|
5268
|
+
}
|
5269
|
+
else {
|
5270
|
+
isAllowed = false;
|
5271
|
+
}
|
5272
|
+
|
5273
|
+
if (!isAllowed) {
|
5274
|
+
resizeLocation = null;
|
5275
|
+
disableCursor();
|
5276
|
+
}
|
5277
|
+
else {
|
5278
|
+
if (
|
5129
5279
|
resizeLocation.start.isSame(event.start.clone().stripZone()) &&
|
5130
5280
|
resizeLocation.end.isSame(eventEnd.clone().stripZone())
|
5131
5281
|
) {
|
5282
|
+
// no change. (FYI, event dates might have zones)
|
5132
5283
|
resizeLocation = null;
|
5133
5284
|
}
|
5134
5285
|
}
|
@@ -5380,6 +5531,60 @@ Grid.mixin({
|
|
5380
5531
|
},
|
5381
5532
|
|
5382
5533
|
|
5534
|
+
/* Event Location Validation
|
5535
|
+
------------------------------------------------------------------------------------------------------------------*/
|
5536
|
+
|
5537
|
+
|
5538
|
+
isEventLocationAllowed: function(eventLocation, event) {
|
5539
|
+
if (this.isEventLocationInRange(eventLocation)) {
|
5540
|
+
var calendar = this.view.calendar;
|
5541
|
+
var eventSpans = this.eventToSpans(eventLocation);
|
5542
|
+
var i;
|
5543
|
+
|
5544
|
+
if (eventSpans.length) {
|
5545
|
+
for (i = 0; i < eventSpans.length; i++) {
|
5546
|
+
if (!calendar.isEventSpanAllowed(eventSpans[i], event)) {
|
5547
|
+
return false;
|
5548
|
+
}
|
5549
|
+
}
|
5550
|
+
|
5551
|
+
return true;
|
5552
|
+
}
|
5553
|
+
}
|
5554
|
+
|
5555
|
+
return false;
|
5556
|
+
},
|
5557
|
+
|
5558
|
+
|
5559
|
+
isExternalLocationAllowed: function(eventLocation, metaProps) { // FOR the external element
|
5560
|
+
if (this.isEventLocationInRange(eventLocation)) {
|
5561
|
+
var calendar = this.view.calendar;
|
5562
|
+
var eventSpans = this.eventToSpans(eventLocation);
|
5563
|
+
var i;
|
5564
|
+
|
5565
|
+
if (eventSpans.length) {
|
5566
|
+
for (i = 0; i < eventSpans.length; i++) {
|
5567
|
+
if (!calendar.isExternalSpanAllowed(eventSpans[i], eventLocation, metaProps)) {
|
5568
|
+
return false;
|
5569
|
+
}
|
5570
|
+
}
|
5571
|
+
|
5572
|
+
return true;
|
5573
|
+
}
|
5574
|
+
}
|
5575
|
+
|
5576
|
+
return false;
|
5577
|
+
},
|
5578
|
+
|
5579
|
+
|
5580
|
+
isEventLocationInRange: function(eventLocation) {
|
5581
|
+
return isRangeWithinRange(
|
5582
|
+
this.eventToRawRange(eventLocation),
|
5583
|
+
this.view.validRange
|
5584
|
+
);
|
5585
|
+
},
|
5586
|
+
|
5587
|
+
|
5383
5588
|
/* Converting events -> eventRange -> eventSpan -> eventSegs
|
5384
5589
|
------------------------------------------------------------------------------------------------------------------*/
|
5385
5590
|
|
@@ -5391,17 +5596,18 @@ Grid.mixin({
|
|
5391
5596
|
},
|
5392
5597
|
|
5393
5598
|
|
5394
|
-
eventToSpan: function(event) {
|
5395
|
-
return this.eventToSpans(event)[0];
|
5396
|
-
},
|
5397
|
-
|
5398
|
-
|
5399
5599
|
// Generates spans (always unzoned) for the given event.
|
5400
5600
|
// Does not do any inverting for inverse-background events.
|
5401
5601
|
// Can accept an event "location" as well (which only has start/end and no allDay)
|
5402
5602
|
eventToSpans: function(event) {
|
5403
|
-
var
|
5404
|
-
|
5603
|
+
var eventRange = this.eventToRange(event); // { start, end, isStart, isEnd }
|
5604
|
+
|
5605
|
+
if (eventRange) {
|
5606
|
+
return this.eventRangeToSpans(eventRange, event);
|
5607
|
+
}
|
5608
|
+
else { // out of view's valid range
|
5609
|
+
return [];
|
5610
|
+
}
|
5405
5611
|
},
|
5406
5612
|
|
5407
5613
|
|
@@ -5415,27 +5621,36 @@ Grid.mixin({
|
|
5415
5621
|
var segs = [];
|
5416
5622
|
|
5417
5623
|
$.each(eventsById, function(id, events) {
|
5418
|
-
var
|
5624
|
+
var visibleEvents = [];
|
5625
|
+
var eventRanges = [];
|
5626
|
+
var eventRange; // { start, end, isStart, isEnd }
|
5419
5627
|
var i;
|
5420
5628
|
|
5421
5629
|
for (i = 0; i < events.length; i++) {
|
5422
|
-
|
5630
|
+
eventRange = _this.eventToRange(events[i]); // might be null if completely out of range
|
5631
|
+
|
5632
|
+
if (eventRange) {
|
5633
|
+
eventRanges.push(eventRange);
|
5634
|
+
visibleEvents.push(events[i]);
|
5635
|
+
}
|
5423
5636
|
}
|
5424
5637
|
|
5425
5638
|
// inverse-background events (utilize only the first event in calculations)
|
5426
5639
|
if (isInverseBgEvent(events[0])) {
|
5427
|
-
|
5640
|
+
eventRanges = _this.invertRanges(eventRanges); // will lose isStart/isEnd
|
5428
5641
|
|
5429
|
-
for (i = 0; i <
|
5642
|
+
for (i = 0; i < eventRanges.length; i++) {
|
5430
5643
|
segs.push.apply(segs, // append to
|
5431
|
-
_this.eventRangeToSegs(
|
5644
|
+
_this.eventRangeToSegs(eventRanges[i], events[0], segSliceFunc)
|
5645
|
+
);
|
5432
5646
|
}
|
5433
5647
|
}
|
5434
5648
|
// normal event ranges
|
5435
5649
|
else {
|
5436
|
-
for (i = 0; i <
|
5650
|
+
for (i = 0; i < eventRanges.length; i++) {
|
5437
5651
|
segs.push.apply(segs, // append to
|
5438
|
-
_this.eventRangeToSegs(
|
5652
|
+
_this.eventRangeToSegs(eventRanges[i], visibleEvents[i], segSliceFunc)
|
5653
|
+
);
|
5439
5654
|
}
|
5440
5655
|
}
|
5441
5656
|
});
|
@@ -5446,7 +5661,36 @@ Grid.mixin({
|
|
5446
5661
|
|
5447
5662
|
// Generates the unzoned start/end dates an event appears to occupy
|
5448
5663
|
// Can accept an event "location" as well (which only has start/end and no allDay)
|
5664
|
+
// returns { start, end, isStart, isEnd }
|
5665
|
+
// If the event is completely outside of the grid's valid range, will return undefined.
|
5449
5666
|
eventToRange: function(event) {
|
5667
|
+
return this.refineRawEventRange(
|
5668
|
+
this.eventToRawRange(event)
|
5669
|
+
);
|
5670
|
+
},
|
5671
|
+
|
5672
|
+
|
5673
|
+
// Ensures the given range is within the view's activeRange and is correctly localized.
|
5674
|
+
// Always returns a result
|
5675
|
+
refineRawEventRange: function(rawRange) {
|
5676
|
+
var view = this.view;
|
5677
|
+
var calendar = view.calendar;
|
5678
|
+
var range = intersectRanges(rawRange, view.activeRange);
|
5679
|
+
|
5680
|
+
if (range) { // otherwise, event doesn't have valid range
|
5681
|
+
|
5682
|
+
// hack: dynamic locale change forgets to upate stored event localed
|
5683
|
+
calendar.localizeMoment(range.start);
|
5684
|
+
calendar.localizeMoment(range.end);
|
5685
|
+
|
5686
|
+
return range;
|
5687
|
+
}
|
5688
|
+
},
|
5689
|
+
|
5690
|
+
|
5691
|
+
// not constrained to valid dates
|
5692
|
+
// not given localizeMoment hack
|
5693
|
+
eventToRawRange: function(event) {
|
5450
5694
|
var calendar = this.view.calendar;
|
5451
5695
|
var start = event.start.clone().stripZone();
|
5452
5696
|
var end = (
|
@@ -5461,48 +5705,58 @@ Grid.mixin({
|
|
5461
5705
|
)
|
5462
5706
|
).stripZone();
|
5463
5707
|
|
5464
|
-
// hack: dynamic locale change forgets to upate stored event localed
|
5465
|
-
calendar.localizeMoment(start);
|
5466
|
-
calendar.localizeMoment(end);
|
5467
|
-
|
5468
5708
|
return { start: start, end: end };
|
5469
5709
|
},
|
5470
5710
|
|
5471
5711
|
|
5472
5712
|
// Given an event's range (unzoned start/end), and the event itself,
|
5473
5713
|
// slice into segments (using the segSliceFunc function if specified)
|
5474
|
-
|
5475
|
-
|
5714
|
+
// eventRange - { start, end, isStart, isEnd }
|
5715
|
+
eventRangeToSegs: function(eventRange, event, segSliceFunc) {
|
5716
|
+
var eventSpans = this.eventRangeToSpans(eventRange, event);
|
5476
5717
|
var segs = [];
|
5477
5718
|
var i;
|
5478
5719
|
|
5479
|
-
for (i = 0; i <
|
5720
|
+
for (i = 0; i < eventSpans.length; i++) {
|
5480
5721
|
segs.push.apply(segs, // append to
|
5481
|
-
this.eventSpanToSegs(
|
5722
|
+
this.eventSpanToSegs(eventSpans[i], event, segSliceFunc)
|
5723
|
+
);
|
5482
5724
|
}
|
5483
5725
|
|
5484
5726
|
return segs;
|
5485
5727
|
},
|
5486
5728
|
|
5487
5729
|
|
5488
|
-
// Given an event's unzoned date range, return an array of
|
5730
|
+
// Given an event's unzoned date range, return an array of eventSpan objects.
|
5731
|
+
// eventSpan - { start, end, isStart, isEnd, otherthings... }
|
5489
5732
|
// Subclasses can override.
|
5490
|
-
|
5491
|
-
|
5733
|
+
// Subclasses are obligated to forward eventRange.isStart/isEnd to the resulting spans.
|
5734
|
+
eventRangeToSpans: function(eventRange, event) {
|
5735
|
+
return [ $.extend({}, eventRange) ]; // copy into a single-item array
|
5492
5736
|
},
|
5493
5737
|
|
5494
5738
|
|
5495
5739
|
// Given an event's span (unzoned start/end and other misc data), and the event itself,
|
5496
5740
|
// slices into segments and attaches event-derived properties to them.
|
5497
|
-
|
5498
|
-
|
5741
|
+
// eventSpan - { start, end, isStart, isEnd, otherthings... }
|
5742
|
+
eventSpanToSegs: function(eventSpan, event, segSliceFunc) {
|
5743
|
+
var segs = segSliceFunc ? segSliceFunc(eventSpan) : this.spanToSegs(eventSpan);
|
5499
5744
|
var i, seg;
|
5500
5745
|
|
5501
5746
|
for (i = 0; i < segs.length; i++) {
|
5502
5747
|
seg = segs[i];
|
5748
|
+
|
5749
|
+
// the eventSpan's isStart/isEnd takes precedence over the seg's
|
5750
|
+
if (!eventSpan.isStart) {
|
5751
|
+
seg.isStart = false;
|
5752
|
+
}
|
5753
|
+
if (!eventSpan.isEnd) {
|
5754
|
+
seg.isEnd = false;
|
5755
|
+
}
|
5756
|
+
|
5503
5757
|
seg.event = event;
|
5504
|
-
seg.eventStartMS = +
|
5505
|
-
seg.eventDurationMS =
|
5758
|
+
seg.eventStartMS = +eventSpan.start; // TODO: not the best name after making spans unzoned
|
5759
|
+
seg.eventDurationMS = eventSpan.end - eventSpan.start;
|
5506
5760
|
}
|
5507
5761
|
|
5508
5762
|
return segs;
|
@@ -5513,8 +5767,8 @@ Grid.mixin({
|
|
5513
5767
|
// SIDE EFFECT: will mutate the given array and will use its date references.
|
5514
5768
|
invertRanges: function(ranges) {
|
5515
5769
|
var view = this.view;
|
5516
|
-
var viewStart = view.start.clone(); // need a copy
|
5517
|
-
var viewEnd = view.end.clone(); // need a copy
|
5770
|
+
var viewStart = view.activeRange.start.clone(); // need a copy
|
5771
|
+
var viewEnd = view.activeRange.end.clone(); // need a copy
|
5518
5772
|
var inverseRanges = [];
|
5519
5773
|
var start = viewStart; // the end of the previous range. the start of the new range
|
5520
5774
|
var i, range;
|
@@ -5965,10 +6219,12 @@ var DayTableMixin = FC.DayTableMixin = {
|
|
5965
6219
|
// (colspan should be no different)
|
5966
6220
|
renderHeadDateCellHtml: function(date, colspan, otherAttrs) {
|
5967
6221
|
var view = this.view;
|
6222
|
+
var isDateValid = isDateWithinRange(date, view.activeRange); // TODO: called too frequently. cache somehow.
|
5968
6223
|
var classNames = [
|
5969
6224
|
'fc-day-header',
|
5970
6225
|
view.widgetHeaderClass
|
5971
6226
|
];
|
6227
|
+
var innerHtml = htmlEscape(date.format(this.colHeadFormat));
|
5972
6228
|
|
5973
6229
|
// if only one row of days, the classNames on the header can represent the specific days beneath
|
5974
6230
|
if (this.rowCnt === 1) {
|
@@ -5984,7 +6240,7 @@ var DayTableMixin = FC.DayTableMixin = {
|
|
5984
6240
|
|
5985
6241
|
return '' +
|
5986
6242
|
'<th class="' + classNames.join(' ') + '"' +
|
5987
|
-
(this.rowCnt === 1 ?
|
6243
|
+
((isDateValid && this.rowCnt) === 1 ?
|
5988
6244
|
' data-date="' + date.format('YYYY-MM-DD') + '"' :
|
5989
6245
|
'') +
|
5990
6246
|
(colspan > 1 ?
|
@@ -5994,10 +6250,14 @@ var DayTableMixin = FC.DayTableMixin = {
|
|
5994
6250
|
' ' + otherAttrs :
|
5995
6251
|
'') +
|
5996
6252
|
'>' +
|
5997
|
-
|
5998
|
-
|
5999
|
-
|
6000
|
-
|
6253
|
+
(isDateValid ?
|
6254
|
+
// don't make a link if the heading could represent multiple days, or if there's only one day (forceOff)
|
6255
|
+
view.buildGotoAnchorHtml(
|
6256
|
+
{ date: date, forceOff: this.rowCnt > 1 || this.colCnt === 1 },
|
6257
|
+
innerHtml
|
6258
|
+
) :
|
6259
|
+
// if not valid, display text, but no link
|
6260
|
+
innerHtml
|
6001
6261
|
) +
|
6002
6262
|
'</th>';
|
6003
6263
|
},
|
@@ -6037,12 +6297,15 @@ var DayTableMixin = FC.DayTableMixin = {
|
|
6037
6297
|
|
6038
6298
|
renderBgCellHtml: function(date, otherAttrs) {
|
6039
6299
|
var view = this.view;
|
6300
|
+
var isDateValid = isDateWithinRange(date, view.activeRange); // TODO: called too frequently. cache somehow.
|
6040
6301
|
var classes = this.getDayClasses(date);
|
6041
6302
|
|
6042
6303
|
classes.unshift('fc-day', view.widgetContentClass);
|
6043
6304
|
|
6044
6305
|
return '<td class="' + classes.join(' ') + '"' +
|
6045
|
-
|
6306
|
+
(isDateValid ?
|
6307
|
+
' data-date="' + date.format('YYYY-MM-DD') + '"' : // if date has a time, won't format it
|
6308
|
+
'') +
|
6046
6309
|
(otherAttrs ?
|
6047
6310
|
' ' + otherAttrs :
|
6048
6311
|
'') +
|
@@ -6120,7 +6383,7 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
6120
6383
|
this.el.html(html);
|
6121
6384
|
|
6122
6385
|
this.rowEls = this.el.find('.fc-row');
|
6123
|
-
this.cellEls = this.el.find('.fc-day');
|
6386
|
+
this.cellEls = this.el.find('.fc-day, .fc-disabled-day');
|
6124
6387
|
|
6125
6388
|
this.rowCoordCache = new CoordCache({
|
6126
6389
|
els: this.rowEls,
|
@@ -6227,11 +6490,14 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
6227
6490
|
// Generates the HTML for the <td>s of the "number" row in the DayGrid's content skeleton.
|
6228
6491
|
// The number row will only exist if either day numbers or week numbers are turned on.
|
6229
6492
|
renderNumberCellHtml: function(date) {
|
6493
|
+
var view = this.view;
|
6230
6494
|
var html = '';
|
6495
|
+
var isDateValid = isDateWithinRange(date, view.activeRange); // TODO: called too frequently. cache somehow.
|
6496
|
+
var isDayNumberVisible = view.dayNumbersVisible && isDateValid;
|
6231
6497
|
var classes;
|
6232
6498
|
var weekCalcFirstDoW;
|
6233
6499
|
|
6234
|
-
if (!
|
6500
|
+
if (!isDayNumberVisible && !view.cellWeekNumbersVisible) {
|
6235
6501
|
// no numbers in day cell (week number must be along the side)
|
6236
6502
|
return '<td/>'; // will create an empty space above events :(
|
6237
6503
|
}
|
@@ -6239,7 +6505,7 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
6239
6505
|
classes = this.getDayClasses(date);
|
6240
6506
|
classes.unshift('fc-day-top');
|
6241
6507
|
|
6242
|
-
if (
|
6508
|
+
if (view.cellWeekNumbersVisible) {
|
6243
6509
|
// To determine the day of week number change under ISO, we cannot
|
6244
6510
|
// rely on moment.js methods such as firstDayOfWeek() or weekday(),
|
6245
6511
|
// because they rely on the locale's dow (possibly overridden by
|
@@ -6253,18 +6519,23 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
6253
6519
|
}
|
6254
6520
|
}
|
6255
6521
|
|
6256
|
-
html += '<td class="' + classes.join(' ') + '"
|
6522
|
+
html += '<td class="' + classes.join(' ') + '"' +
|
6523
|
+
(isDateValid ?
|
6524
|
+
' data-date="' + date.format() + '"' :
|
6525
|
+
''
|
6526
|
+
) +
|
6527
|
+
'>';
|
6257
6528
|
|
6258
|
-
if (
|
6259
|
-
html +=
|
6529
|
+
if (view.cellWeekNumbersVisible && (date.day() == weekCalcFirstDoW)) {
|
6530
|
+
html += view.buildGotoAnchorHtml(
|
6260
6531
|
{ date: date, type: 'week' },
|
6261
6532
|
{ 'class': 'fc-week-number' },
|
6262
6533
|
date.format('w') // inner HTML
|
6263
6534
|
);
|
6264
6535
|
}
|
6265
6536
|
|
6266
|
-
if (
|
6267
|
-
html +=
|
6537
|
+
if (isDayNumberVisible) {
|
6538
|
+
html += view.buildGotoAnchorHtml(
|
6268
6539
|
date,
|
6269
6540
|
{ 'class': 'fc-day-number' },
|
6270
6541
|
date.date() // inner HTML
|
@@ -6393,9 +6664,13 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
6393
6664
|
// Renders a visual indication of an event or external element being dragged.
|
6394
6665
|
// `eventLocation` has zoned start and end (optional)
|
6395
6666
|
renderDrag: function(eventLocation, seg) {
|
6667
|
+
var eventSpans = this.eventToSpans(eventLocation);
|
6668
|
+
var i;
|
6396
6669
|
|
6397
6670
|
// always render a highlight underneath
|
6398
|
-
|
6671
|
+
for (i = 0; i < eventSpans.length; i++) {
|
6672
|
+
this.renderHighlight(eventSpans[i]);
|
6673
|
+
}
|
6399
6674
|
|
6400
6675
|
// if a segment from the same calendar but another component is being dragged, render a helper event
|
6401
6676
|
if (seg && seg.component !== this) {
|
@@ -6417,7 +6692,13 @@ var DayGrid = FC.DayGrid = Grid.extend(DayTableMixin, {
|
|
6417
6692
|
|
6418
6693
|
// Renders a visual indication of an event being resized
|
6419
6694
|
renderEventResize: function(eventLocation, seg) {
|
6420
|
-
this.
|
6695
|
+
var eventSpans = this.eventToSpans(eventLocation);
|
6696
|
+
var i;
|
6697
|
+
|
6698
|
+
for (i = 0; i < eventSpans.length; i++) {
|
6699
|
+
this.renderHighlight(eventSpans[i]);
|
6700
|
+
}
|
6701
|
+
|
6421
6702
|
return this.renderEventLocationHelper(eventLocation, seg); // returns mock event elements
|
6422
6703
|
},
|
6423
6704
|
|
@@ -7258,8 +7539,6 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
7258
7539
|
slotDuration: null, // duration of a "slot", a distinct time segment on given day, visualized by lines
|
7259
7540
|
snapDuration: null, // granularity of time for dragging and selecting
|
7260
7541
|
snapsPerSlot: null,
|
7261
|
-
minTime: null, // Duration object that denotes the first visible time of any given day
|
7262
|
-
maxTime: null, // Duration object that denotes the exclusive visible end time of any given day
|
7263
7542
|
labelFormat: null, // formatting string for times running along vertical axis
|
7264
7543
|
labelInterval: null, // duration of how often a label should be displayed for a slot
|
7265
7544
|
|
@@ -7283,7 +7562,7 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
7283
7562
|
// Relies on the view's colCnt. In the future, this component should probably be self-sufficient.
|
7284
7563
|
renderDates: function() {
|
7285
7564
|
this.el.html(this.renderHtml());
|
7286
|
-
this.colEls = this.el.find('.fc-day');
|
7565
|
+
this.colEls = this.el.find('.fc-day, .fc-disabled-day');
|
7287
7566
|
this.slatContainerEl = this.el.find('.fc-slats');
|
7288
7567
|
this.slatEls = this.slatContainerEl.find('tr');
|
7289
7568
|
|
@@ -7321,13 +7600,13 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
7321
7600
|
var view = this.view;
|
7322
7601
|
var isRTL = this.isRTL;
|
7323
7602
|
var html = '';
|
7324
|
-
var slotTime = moment.duration(+this.minTime); // wish there was .clone() for durations
|
7603
|
+
var slotTime = moment.duration(+this.view.minTime); // wish there was .clone() for durations
|
7325
7604
|
var slotDate; // will be on the view's first day, but we only care about its time
|
7326
7605
|
var isLabeled;
|
7327
7606
|
var axisHtml;
|
7328
7607
|
|
7329
7608
|
// Calculate the time for each slot
|
7330
|
-
while (slotTime < this.maxTime) {
|
7609
|
+
while (slotTime < this.view.maxTime) {
|
7331
7610
|
slotDate = this.start.clone().time(slotTime);
|
7332
7611
|
isLabeled = isInt(divideDurationByDuration(slotTime, this.labelInterval));
|
7333
7612
|
|
@@ -7377,9 +7656,6 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
7377
7656
|
|
7378
7657
|
this.minResizeDuration = snapDuration; // hack
|
7379
7658
|
|
7380
|
-
this.minTime = moment.duration(view.opt('minTime'));
|
7381
|
-
this.maxTime = moment.duration(view.opt('maxTime'));
|
7382
|
-
|
7383
7659
|
// might be an array value (for TimelineView).
|
7384
7660
|
// if so, getting the most granular entry (the last one probably).
|
7385
7661
|
input = view.opt('slotLabelFormat');
|
@@ -7505,7 +7781,7 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
7505
7781
|
|
7506
7782
|
// Given a row number of the grid, representing a "snap", returns a time (Duration) from its start-of-day
|
7507
7783
|
computeSnapTime: function(snapIndex) {
|
7508
|
-
return moment.duration(this.minTime + this.snapDuration * snapIndex);
|
7784
|
+
return moment.duration(this.view.minTime + this.snapDuration * snapIndex);
|
7509
7785
|
},
|
7510
7786
|
|
7511
7787
|
|
@@ -7535,10 +7811,10 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
7535
7811
|
var dayRange;
|
7536
7812
|
|
7537
7813
|
for (dayIndex = 0; dayIndex < this.daysPerRow; dayIndex++) {
|
7538
|
-
dayDate = this.dayDates[dayIndex].clone(); // TODO: better API for this?
|
7814
|
+
dayDate = this.dayDates[dayIndex].clone().time(0); // TODO: better API for this?
|
7539
7815
|
dayRange = {
|
7540
|
-
start: dayDate.clone().
|
7541
|
-
end: dayDate.clone().
|
7816
|
+
start: dayDate.clone().add(this.view.minTime), // don't use .time() because it sux with negatives
|
7817
|
+
end: dayDate.clone().add(this.view.maxTime)
|
7542
7818
|
};
|
7543
7819
|
seg = intersectRanges(range, dayRange); // both will be ambig timezone
|
7544
7820
|
if (seg) {
|
@@ -7585,7 +7861,7 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
7585
7861
|
// Computes the top coordinate, relative to the bounds of the grid, of the given time (a Duration).
|
7586
7862
|
computeTimeTop: function(time) {
|
7587
7863
|
var len = this.slatEls.length;
|
7588
|
-
var slatCoverage = (time - this.minTime) / this.slotDuration; // floating-point value of # of slots covered
|
7864
|
+
var slatCoverage = (time - this.view.minTime) / this.slotDuration; // floating-point value of # of slots covered
|
7589
7865
|
var slatIndex;
|
7590
7866
|
var slatRemainder;
|
7591
7867
|
|
@@ -7617,6 +7893,8 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
7617
7893
|
// Renders a visual indication of an event being dragged over the specified date(s).
|
7618
7894
|
// A returned value of `true` signals that a mock "helper" event has been rendered.
|
7619
7895
|
renderDrag: function(eventLocation, seg) {
|
7896
|
+
var eventSpans;
|
7897
|
+
var i;
|
7620
7898
|
|
7621
7899
|
if (seg) { // if there is event information for this drag, render a helper event
|
7622
7900
|
|
@@ -7624,9 +7902,12 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
|
|
7624
7902
|
// signal that a helper has been rendered
|
7625
7903
|
return this.renderEventLocationHelper(eventLocation, seg);
|
7626
7904
|
}
|
7627
|
-
else {
|
7628
|
-
|
7629
|
-
|
7905
|
+
else { // otherwise, just render a highlight
|
7906
|
+
eventSpans = this.eventToSpans(eventLocation);
|
7907
|
+
|
7908
|
+
for (i = 0; i < eventSpans.length; i++) {
|
7909
|
+
this.renderHighlight(eventSpans[i]);
|
7910
|
+
}
|
7630
7911
|
}
|
7631
7912
|
},
|
7632
7913
|
|
@@ -8102,11 +8383,14 @@ TimeGrid.mixin({
|
|
8102
8383
|
// For each segment in an array, computes and assigns its top and bottom properties
|
8103
8384
|
computeSegVerticals: function(segs) {
|
8104
8385
|
var i, seg;
|
8386
|
+
var dayDate;
|
8105
8387
|
|
8106
8388
|
for (i = 0; i < segs.length; i++) {
|
8107
8389
|
seg = segs[i];
|
8108
|
-
|
8109
|
-
|
8390
|
+
dayDate = this.dayDates[seg.dayIndex];
|
8391
|
+
|
8392
|
+
seg.top = this.computeDateTop(seg.start, dayDate);
|
8393
|
+
seg.bottom = this.computeDateTop(seg.end, dayDate);
|
8110
8394
|
}
|
8111
8395
|
},
|
8112
8396
|
|
@@ -8394,6 +8678,7 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
8394
8678
|
title: null, // the text that will be displayed in the header's title
|
8395
8679
|
|
8396
8680
|
calendar: null, // owner Calendar object
|
8681
|
+
viewSpec: null,
|
8397
8682
|
options: null, // hash containing all options. already merged with view-specific-options
|
8398
8683
|
el: null, // the view's containing element. set by Calendar
|
8399
8684
|
|
@@ -8406,17 +8691,6 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
8406
8691
|
isEventsRendered: false,
|
8407
8692
|
eventRenderQueue: null,
|
8408
8693
|
|
8409
|
-
// range the view is actually displaying (moments)
|
8410
|
-
start: null,
|
8411
|
-
end: null, // exclusive
|
8412
|
-
|
8413
|
-
// range the view is formally responsible for (moments)
|
8414
|
-
// may be different from start/end. for example, a month view might have 1st-31st, excluding padded dates
|
8415
|
-
intervalStart: null,
|
8416
|
-
intervalEnd: null, // exclusive
|
8417
|
-
intervalDuration: null,
|
8418
|
-
intervalUnit: null, // name of largest unit being displayed, like "month" or "week"
|
8419
|
-
|
8420
8694
|
isRTL: false,
|
8421
8695
|
isSelected: false, // boolean whether a range of time is user-selected or not
|
8422
8696
|
selectedEvent: null,
|
@@ -8440,12 +8714,17 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
8440
8714
|
nowIndicatorIntervalID: null, // "
|
8441
8715
|
|
8442
8716
|
|
8443
|
-
constructor: function(calendar,
|
8717
|
+
constructor: function(calendar, viewSpec) {
|
8444
8718
|
|
8445
8719
|
this.calendar = calendar;
|
8446
|
-
this.
|
8447
|
-
|
8448
|
-
|
8720
|
+
this.viewSpec = viewSpec;
|
8721
|
+
|
8722
|
+
// shortcuts
|
8723
|
+
this.type = viewSpec.type;
|
8724
|
+
this.options = viewSpec.options;
|
8725
|
+
|
8726
|
+
// .name is deprecated
|
8727
|
+
this.name = this.type;
|
8449
8728
|
|
8450
8729
|
this.nextDayThreshold = moment.duration(this.opt('nextDayThreshold'));
|
8451
8730
|
this.initThemingProps();
|
@@ -8510,85 +8789,6 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
8510
8789
|
},
|
8511
8790
|
|
8512
8791
|
|
8513
|
-
/* Date Computation
|
8514
|
-
------------------------------------------------------------------------------------------------------------------*/
|
8515
|
-
|
8516
|
-
|
8517
|
-
// Updates all internal dates for displaying the given unzoned range.
|
8518
|
-
setRange: function(range) {
|
8519
|
-
$.extend(this, range); // assigns every property to this object's member variables
|
8520
|
-
this.updateTitle();
|
8521
|
-
},
|
8522
|
-
|
8523
|
-
|
8524
|
-
// Given a single current unzoned date, produce information about what range to display.
|
8525
|
-
// Subclasses can override. Must return all properties.
|
8526
|
-
computeRange: function(date) {
|
8527
|
-
var intervalUnit = computeIntervalUnit(this.intervalDuration);
|
8528
|
-
var intervalStart = date.clone().startOf(intervalUnit);
|
8529
|
-
var intervalEnd = intervalStart.clone().add(this.intervalDuration);
|
8530
|
-
var start, end;
|
8531
|
-
|
8532
|
-
// normalize the range's time-ambiguity
|
8533
|
-
if (/year|month|week|day/.test(intervalUnit)) { // whole-days?
|
8534
|
-
intervalStart.stripTime();
|
8535
|
-
intervalEnd.stripTime();
|
8536
|
-
}
|
8537
|
-
else { // needs to have a time?
|
8538
|
-
if (!intervalStart.hasTime()) {
|
8539
|
-
intervalStart = this.calendar.time(0); // give 00:00 time
|
8540
|
-
}
|
8541
|
-
if (!intervalEnd.hasTime()) {
|
8542
|
-
intervalEnd = this.calendar.time(0); // give 00:00 time
|
8543
|
-
}
|
8544
|
-
}
|
8545
|
-
|
8546
|
-
start = intervalStart.clone();
|
8547
|
-
start = this.skipHiddenDays(start);
|
8548
|
-
end = intervalEnd.clone();
|
8549
|
-
end = this.skipHiddenDays(end, -1, true); // exclusively move backwards
|
8550
|
-
|
8551
|
-
return {
|
8552
|
-
intervalUnit: intervalUnit,
|
8553
|
-
intervalStart: intervalStart,
|
8554
|
-
intervalEnd: intervalEnd,
|
8555
|
-
start: start,
|
8556
|
-
end: end
|
8557
|
-
};
|
8558
|
-
},
|
8559
|
-
|
8560
|
-
|
8561
|
-
// Computes the new date when the user hits the prev button, given the current date
|
8562
|
-
computePrevDate: function(date) {
|
8563
|
-
return this.massageCurrentDate(
|
8564
|
-
date.clone().startOf(this.intervalUnit).subtract(this.intervalDuration), -1
|
8565
|
-
);
|
8566
|
-
},
|
8567
|
-
|
8568
|
-
|
8569
|
-
// Computes the new date when the user hits the next button, given the current date
|
8570
|
-
computeNextDate: function(date) {
|
8571
|
-
return this.massageCurrentDate(
|
8572
|
-
date.clone().startOf(this.intervalUnit).add(this.intervalDuration)
|
8573
|
-
);
|
8574
|
-
},
|
8575
|
-
|
8576
|
-
|
8577
|
-
// Given an arbitrarily calculated current date of the calendar, returns a date that is ensured to be completely
|
8578
|
-
// visible. `direction` is optional and indicates which direction the current date was being
|
8579
|
-
// incremented or decremented (1 or -1).
|
8580
|
-
massageCurrentDate: function(date, direction) {
|
8581
|
-
if (this.intervalDuration.as('days') <= 1) { // if the view displays a single day or smaller
|
8582
|
-
if (this.isHiddenDay(date)) {
|
8583
|
-
date = this.skipHiddenDays(date, direction);
|
8584
|
-
date.startOf('day');
|
8585
|
-
}
|
8586
|
-
}
|
8587
|
-
|
8588
|
-
return date;
|
8589
|
-
},
|
8590
|
-
|
8591
|
-
|
8592
8792
|
/* Title and Date Formatting
|
8593
8793
|
------------------------------------------------------------------------------------------------------------------*/
|
8594
8794
|
|
@@ -8602,23 +8802,21 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
8602
8802
|
|
8603
8803
|
// Computes what the title at the top of the calendar should be for this view
|
8604
8804
|
computeTitle: function() {
|
8605
|
-
var
|
8805
|
+
var range;
|
8606
8806
|
|
8607
8807
|
// for views that span a large unit of time, show the proper interval, ignoring stray days before and after
|
8608
|
-
if (
|
8609
|
-
|
8610
|
-
end = this.intervalEnd;
|
8808
|
+
if (/^(year|month)$/.test(this.currentRangeUnit)) {
|
8809
|
+
range = this.currentRange;
|
8611
8810
|
}
|
8612
8811
|
else { // for day units or smaller, use the actual day range
|
8613
|
-
|
8614
|
-
end = this.end;
|
8812
|
+
range = this.activeRange;
|
8615
8813
|
}
|
8616
8814
|
|
8617
8815
|
return this.formatRange(
|
8618
8816
|
{
|
8619
|
-
// in case
|
8620
|
-
start: this.calendar.applyTimezone(start),
|
8621
|
-
end: this.calendar.applyTimezone(end)
|
8817
|
+
// in case currentRange has a time, make sure timezone is correct
|
8818
|
+
start: this.calendar.applyTimezone(range.start),
|
8819
|
+
end: this.calendar.applyTimezone(range.end)
|
8622
8820
|
},
|
8623
8821
|
this.opt('titleFormat') || this.computeTitleFormat(),
|
8624
8822
|
this.opt('titleRangeSeparator')
|
@@ -8629,13 +8827,13 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
8629
8827
|
// Generates the format string that should be used to generate the title for the current date range.
|
8630
8828
|
// Attempts to compute the most appropriate format if not explicitly specified with `titleFormat`.
|
8631
8829
|
computeTitleFormat: function() {
|
8632
|
-
if (this.
|
8830
|
+
if (this.currentRangeUnit == 'year') {
|
8633
8831
|
return 'YYYY';
|
8634
8832
|
}
|
8635
|
-
else if (this.
|
8833
|
+
else if (this.currentRangeUnit == 'month') {
|
8636
8834
|
return this.opt('monthYearFormat'); // like "September 2014"
|
8637
8835
|
}
|
8638
|
-
else if (this.
|
8836
|
+
else if (this.currentRangeAs('days') > 1) {
|
8639
8837
|
return 'll'; // multi-day range. shorter, like "Sep 9 - 10 2014"
|
8640
8838
|
}
|
8641
8839
|
else {
|
@@ -8785,7 +8983,7 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
8785
8983
|
|
8786
8984
|
this.unbindEvents(); // will do nothing if not already bound
|
8787
8985
|
this.requestDateRender(date).then(function() {
|
8788
|
-
// wish we could start earlier, but
|
8986
|
+
// wish we could start earlier, but setRangeFromDate needs to execute first
|
8789
8987
|
_this.bindEvents(); // will request events
|
8790
8988
|
});
|
8791
8989
|
},
|
@@ -8827,39 +9025,47 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
8827
9025
|
// if date not specified, uses current
|
8828
9026
|
executeDateRender: function(date) {
|
8829
9027
|
var _this = this;
|
9028
|
+
var rangeChanged = false;
|
8830
9029
|
|
8831
|
-
// if rendering a new date, reset scroll to initial state (scrollTime)
|
8832
9030
|
if (date) {
|
8833
|
-
|
8834
|
-
}
|
8835
|
-
else {
|
8836
|
-
this.captureScroll(); // a rerender of the current date
|
9031
|
+
rangeChanged = _this.setRangeFromDate(date);
|
8837
9032
|
}
|
8838
9033
|
|
8839
|
-
|
8840
|
-
|
8841
|
-
return this.executeDateUnrender().then(function() {
|
9034
|
+
if (!date || rangeChanged || !_this.isDateRendered) { // should render?
|
8842
9035
|
|
9036
|
+
// if rendering a new date, reset scroll to initial state (scrollTime)
|
8843
9037
|
if (date) {
|
8844
|
-
|
9038
|
+
this.captureInitialScroll();
|
8845
9039
|
}
|
8846
|
-
|
8847
|
-
|
8848
|
-
_this.render(); // TODO: deprecate
|
9040
|
+
else {
|
9041
|
+
this.captureScroll(); // a rerender of the current date
|
8849
9042
|
}
|
8850
9043
|
|
8851
|
-
|
8852
|
-
|
8853
|
-
|
8854
|
-
|
9044
|
+
this.freezeHeight();
|
9045
|
+
|
9046
|
+
// potential issue: date-unrendering will happen with the *new* range
|
9047
|
+
return this.executeDateUnrender().then(function() {
|
8855
9048
|
|
8856
|
-
|
8857
|
-
|
9049
|
+
if (_this.render) {
|
9050
|
+
_this.render(); // TODO: deprecate
|
9051
|
+
}
|
8858
9052
|
|
8859
|
-
|
8860
|
-
|
8861
|
-
|
8862
|
-
|
9053
|
+
_this.renderDates();
|
9054
|
+
_this.updateSize();
|
9055
|
+
_this.renderBusinessHours(); // might need coordinates, so should go after updateSize()
|
9056
|
+
_this.startNowIndicator();
|
9057
|
+
|
9058
|
+
_this.thawHeight();
|
9059
|
+
_this.releaseScroll();
|
9060
|
+
|
9061
|
+
_this.isDateRendered = true;
|
9062
|
+
_this.onDateRender();
|
9063
|
+
_this.trigger('dateRender');
|
9064
|
+
});
|
9065
|
+
}
|
9066
|
+
else {
|
9067
|
+
return Promise.resolve();
|
9068
|
+
}
|
8863
9069
|
},
|
8864
9070
|
|
8865
9071
|
|
@@ -9413,7 +9619,10 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
9413
9619
|
|
9414
9620
|
|
9415
9621
|
requestEvents: function() {
|
9416
|
-
return this.calendar.requestEvents(
|
9622
|
+
return this.calendar.requestEvents(
|
9623
|
+
this.activeRange.start,
|
9624
|
+
this.activeRange.end
|
9625
|
+
);
|
9417
9626
|
},
|
9418
9627
|
|
9419
9628
|
|
@@ -9787,17 +9996,425 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
9787
9996
|
------------------------------------------------------------------------------------------------------------------*/
|
9788
9997
|
|
9789
9998
|
|
9790
|
-
//
|
9791
|
-
|
9792
|
-
|
9793
|
-
var
|
9794
|
-
var
|
9795
|
-
var
|
9796
|
-
|
9797
|
-
|
9798
|
-
|
9799
|
-
|
9800
|
-
|
9999
|
+
// Returns the date range of the full days the given range visually appears to occupy.
|
10000
|
+
// Returns a new range object.
|
10001
|
+
computeDayRange: function(range) {
|
10002
|
+
var startDay = range.start.clone().stripTime(); // the beginning of the day the range starts
|
10003
|
+
var end = range.end;
|
10004
|
+
var endDay = null;
|
10005
|
+
var endTimeMS;
|
10006
|
+
|
10007
|
+
if (end) {
|
10008
|
+
endDay = end.clone().stripTime(); // the beginning of the day the range exclusively ends
|
10009
|
+
endTimeMS = +end.time(); // # of milliseconds into `endDay`
|
10010
|
+
|
10011
|
+
// If the end time is actually inclusively part of the next day and is equal to or
|
10012
|
+
// beyond the next day threshold, adjust the end to be the exclusive end of `endDay`.
|
10013
|
+
// Otherwise, leaving it as inclusive will cause it to exclude `endDay`.
|
10014
|
+
if (endTimeMS && endTimeMS >= this.nextDayThreshold) {
|
10015
|
+
endDay.add(1, 'days');
|
10016
|
+
}
|
10017
|
+
}
|
10018
|
+
|
10019
|
+
// If no end was specified, or if it is within `startDay` but not past nextDayThreshold,
|
10020
|
+
// assign the default duration of one day.
|
10021
|
+
if (!end || endDay <= startDay) {
|
10022
|
+
endDay = startDay.clone().add(1, 'days');
|
10023
|
+
}
|
10024
|
+
|
10025
|
+
return { start: startDay, end: endDay };
|
10026
|
+
},
|
10027
|
+
|
10028
|
+
|
10029
|
+
// Does the given event visually appear to occupy more than one day?
|
10030
|
+
isMultiDayEvent: function(event) {
|
10031
|
+
var range = this.computeDayRange(event); // event is range-ish
|
10032
|
+
|
10033
|
+
return range.end.diff(range.start, 'days') > 1;
|
10034
|
+
}
|
10035
|
+
|
10036
|
+
});
|
10037
|
+
|
10038
|
+
;;
|
10039
|
+
|
10040
|
+
View.mixin({
|
10041
|
+
|
10042
|
+
// range the view is formally responsible for.
|
10043
|
+
// for example, a month view might have 1st-31st, excluding padded dates
|
10044
|
+
currentRange: null,
|
10045
|
+
currentRangeUnit: null, // name of largest unit being displayed, like "month" or "week"
|
10046
|
+
|
10047
|
+
// date range with a rendered skeleton
|
10048
|
+
// includes not-active days that need some sort of DOM
|
10049
|
+
renderRange: null,
|
10050
|
+
|
10051
|
+
// dates that display events and accept drag-n-drop
|
10052
|
+
activeRange: null,
|
10053
|
+
|
10054
|
+
// constraint for where prev/next operations can go and where events can be dragged/resized to.
|
10055
|
+
// an object with optional start and end properties.
|
10056
|
+
validRange: null,
|
10057
|
+
|
10058
|
+
// how far the current date will move for a prev/next operation
|
10059
|
+
dateIncrement: null,
|
10060
|
+
|
10061
|
+
// stores the *calendar's* current date after setDate
|
10062
|
+
// TODO: entirely Calendar's responsibility
|
10063
|
+
currentDate: null,
|
10064
|
+
|
10065
|
+
minTime: null, // Duration object that denotes the first visible time of any given day
|
10066
|
+
maxTime: null, // Duration object that denotes the exclusive visible end time of any given day
|
10067
|
+
usesMinMaxTime: false, // whether minTime/maxTime will affect the activeRange. Views must opt-in.
|
10068
|
+
|
10069
|
+
// DEPRECATED
|
10070
|
+
start: null, // use activeRange.start
|
10071
|
+
end: null, // use activeRange.end
|
10072
|
+
intervalStart: null, // use currentRange.start
|
10073
|
+
intervalEnd: null, // use currentRange.end
|
10074
|
+
|
10075
|
+
|
10076
|
+
/* Date Range Computation
|
10077
|
+
------------------------------------------------------------------------------------------------------------------*/
|
10078
|
+
|
10079
|
+
|
10080
|
+
// Updates all internal dates/ranges for eventual rendering around the given date.
|
10081
|
+
// Returns a boolean about whether there was some sort of change.
|
10082
|
+
setRangeFromDate: function(date) {
|
10083
|
+
|
10084
|
+
var rangeInfo = this.buildRangeInfo(date);
|
10085
|
+
|
10086
|
+
// some sort of change? (TODO: compare other ranges too?)
|
10087
|
+
if (!this.activeRange || !isRangesEqual(this.activeRange, rangeInfo.activeRange)) {
|
10088
|
+
|
10089
|
+
this.currentRange = rangeInfo.currentRange;
|
10090
|
+
this.currentRangeUnit = rangeInfo.currentRangeUnit;
|
10091
|
+
this.renderRange = rangeInfo.renderRange;
|
10092
|
+
this.activeRange = rangeInfo.activeRange;
|
10093
|
+
this.validRange = rangeInfo.validRange;
|
10094
|
+
this.dateIncrement = rangeInfo.dateIncrement;
|
10095
|
+
this.currentDate = rangeInfo.date;
|
10096
|
+
this.minTime = rangeInfo.minTime;
|
10097
|
+
this.maxTime = rangeInfo.maxTime;
|
10098
|
+
|
10099
|
+
// DEPRECATED, but we need to keep it updated
|
10100
|
+
this.start = rangeInfo.activeRange.start;
|
10101
|
+
this.end = rangeInfo.activeRange.end;
|
10102
|
+
this.intervalStart = rangeInfo.currentRange.start;
|
10103
|
+
this.intervalEnd = rangeInfo.currentRange.end;
|
10104
|
+
|
10105
|
+
this.updateTitle();
|
10106
|
+
this.calendar.updateToolbarButtons();
|
10107
|
+
|
10108
|
+
return true;
|
10109
|
+
}
|
10110
|
+
|
10111
|
+
return false;
|
10112
|
+
},
|
10113
|
+
|
10114
|
+
|
10115
|
+
// Builds a structure with info about what the dates/ranges will be for the "prev" view.
|
10116
|
+
buildPrevRangeInfo: function(date) {
|
10117
|
+
var prevDate = date.clone().startOf(this.currentRangeUnit).subtract(this.dateIncrement);
|
10118
|
+
|
10119
|
+
return this.buildRangeInfo(prevDate, -1);
|
10120
|
+
},
|
10121
|
+
|
10122
|
+
|
10123
|
+
// Builds a structure with info about what the dates/ranges will be for the "next" view.
|
10124
|
+
buildNextRangeInfo: function(date) {
|
10125
|
+
var nextDate = date.clone().startOf(this.currentRangeUnit).add(this.dateIncrement);
|
10126
|
+
|
10127
|
+
return this.buildRangeInfo(nextDate, 1);
|
10128
|
+
},
|
10129
|
+
|
10130
|
+
|
10131
|
+
// Builds a structure holding dates/ranges for rendering around the given date.
|
10132
|
+
// Optional direction param indicates whether the date is being incremented/decremented
|
10133
|
+
// from its previous value. decremented = -1, incremented = 1 (default).
|
10134
|
+
buildRangeInfo: function(givenDate, direction) {
|
10135
|
+
var validRange = this.buildValidRange();
|
10136
|
+
var constrainedDate = constrainDate(givenDate, validRange);
|
10137
|
+
var minTime = null;
|
10138
|
+
var maxTime = null;
|
10139
|
+
var currentInfo;
|
10140
|
+
var renderRange;
|
10141
|
+
var activeRange;
|
10142
|
+
var isValid;
|
10143
|
+
|
10144
|
+
currentInfo = this.buildCurrentRangeInfo(constrainedDate, direction);
|
10145
|
+
renderRange = this.buildRenderRange(currentInfo.range, currentInfo.unit);
|
10146
|
+
activeRange = cloneRange(renderRange);
|
10147
|
+
|
10148
|
+
if (!this.opt('showNonCurrentDates')) {
|
10149
|
+
activeRange = constrainRange(activeRange, currentInfo.range);
|
10150
|
+
}
|
10151
|
+
|
10152
|
+
minTime = moment.duration(this.opt('minTime'));
|
10153
|
+
maxTime = moment.duration(this.opt('maxTime'));
|
10154
|
+
this.adjustActiveRange(activeRange, minTime, maxTime);
|
10155
|
+
|
10156
|
+
activeRange = constrainRange(activeRange, validRange);
|
10157
|
+
constrainedDate = constrainDate(constrainedDate, activeRange);
|
10158
|
+
|
10159
|
+
// it's invalid if the originally requested date is not contained,
|
10160
|
+
// or if the range is completely outside of the valid range.
|
10161
|
+
isValid = isDateWithinRange(givenDate, currentInfo.range) &&
|
10162
|
+
doRangesIntersect(currentInfo.range, validRange);
|
10163
|
+
|
10164
|
+
return {
|
10165
|
+
validRange: validRange,
|
10166
|
+
currentRange: currentInfo.range,
|
10167
|
+
currentRangeUnit: currentInfo.unit,
|
10168
|
+
activeRange: activeRange,
|
10169
|
+
renderRange: renderRange,
|
10170
|
+
minTime: minTime,
|
10171
|
+
maxTime: maxTime,
|
10172
|
+
isValid: isValid,
|
10173
|
+
date: constrainedDate,
|
10174
|
+
dateIncrement: this.buildDateIncrement(currentInfo.duration)
|
10175
|
+
// pass a fallback (might be null) ^
|
10176
|
+
};
|
10177
|
+
},
|
10178
|
+
|
10179
|
+
|
10180
|
+
// Builds an object with optional start/end properties.
|
10181
|
+
// Indicates the minimum/maximum dates to display.
|
10182
|
+
buildValidRange: function() {
|
10183
|
+
return this.getRangeOption('validRange', this.calendar.getNow()) || {};
|
10184
|
+
},
|
10185
|
+
|
10186
|
+
|
10187
|
+
// Builds a structure with info about the "current" range, the range that is
|
10188
|
+
// highlighted as being the current month for example.
|
10189
|
+
// See buildRangeInfo for a description of `direction`.
|
10190
|
+
// Guaranteed to have `range` and `unit` properties. `duration` is optional.
|
10191
|
+
buildCurrentRangeInfo: function(date, direction) {
|
10192
|
+
var duration = null;
|
10193
|
+
var unit = null;
|
10194
|
+
var range = null;
|
10195
|
+
var dayCount;
|
10196
|
+
|
10197
|
+
if (this.viewSpec.duration) {
|
10198
|
+
duration = this.viewSpec.duration;
|
10199
|
+
unit = this.viewSpec.durationUnit;
|
10200
|
+
range = this.buildRangeFromDuration(date, direction, duration, unit);
|
10201
|
+
}
|
10202
|
+
else if ((dayCount = this.opt('dayCount'))) {
|
10203
|
+
unit = 'day';
|
10204
|
+
range = this.buildRangeFromDayCount(date, direction, dayCount);
|
10205
|
+
}
|
10206
|
+
else if ((range = this.buildCustomVisibleRange(date))) {
|
10207
|
+
unit = computeGreatestUnit(range.start, range.end);
|
10208
|
+
}
|
10209
|
+
else {
|
10210
|
+
duration = this.getFallbackDuration();
|
10211
|
+
unit = computeGreatestUnit(duration);
|
10212
|
+
range = this.buildRangeFromDuration(date, direction, duration, unit);
|
10213
|
+
}
|
10214
|
+
|
10215
|
+
this.normalizeCurrentRange(range, unit); // modifies in-place
|
10216
|
+
|
10217
|
+
return { duration: duration, unit: unit, range: range };
|
10218
|
+
},
|
10219
|
+
|
10220
|
+
|
10221
|
+
getFallbackDuration: function() {
|
10222
|
+
return moment.duration({ days: 1 });
|
10223
|
+
},
|
10224
|
+
|
10225
|
+
|
10226
|
+
// If the range has day units or larger, remove times. Otherwise, ensure times.
|
10227
|
+
normalizeCurrentRange: function(range, unit) {
|
10228
|
+
|
10229
|
+
if (/^(year|month|week|day)$/.test(unit)) { // whole-days?
|
10230
|
+
range.start.stripTime();
|
10231
|
+
range.end.stripTime();
|
10232
|
+
}
|
10233
|
+
else { // needs to have a time?
|
10234
|
+
if (!range.start.hasTime()) {
|
10235
|
+
range.start.time(0); // give 00:00 time
|
10236
|
+
}
|
10237
|
+
if (!range.end.hasTime()) {
|
10238
|
+
range.end.time(0); // give 00:00 time
|
10239
|
+
}
|
10240
|
+
}
|
10241
|
+
},
|
10242
|
+
|
10243
|
+
|
10244
|
+
// Mutates the given activeRange to have time values (un-ambiguate)
|
10245
|
+
// if the minTime or maxTime causes the range to expand.
|
10246
|
+
// TODO: eventually activeRange should *always* have times.
|
10247
|
+
adjustActiveRange: function(range, minTime, maxTime) {
|
10248
|
+
var hasSpecialTimes = false;
|
10249
|
+
|
10250
|
+
if (this.usesMinMaxTime) {
|
10251
|
+
|
10252
|
+
if (minTime < 0) {
|
10253
|
+
range.start.time(0).add(minTime);
|
10254
|
+
hasSpecialTimes = true;
|
10255
|
+
}
|
10256
|
+
|
10257
|
+
if (maxTime > 24 * 60 * 60 * 1000) { // beyond 24 hours?
|
10258
|
+
range.end.time(maxTime - (24 * 60 * 60 * 1000));
|
10259
|
+
hasSpecialTimes = true;
|
10260
|
+
}
|
10261
|
+
|
10262
|
+
if (hasSpecialTimes) {
|
10263
|
+
if (!range.start.hasTime()) {
|
10264
|
+
range.start.time(0);
|
10265
|
+
}
|
10266
|
+
if (!range.end.hasTime()) {
|
10267
|
+
range.end.time(0);
|
10268
|
+
}
|
10269
|
+
}
|
10270
|
+
}
|
10271
|
+
},
|
10272
|
+
|
10273
|
+
|
10274
|
+
// Builds the "current" range when it is specified as an explicit duration.
|
10275
|
+
// `unit` is the already-computed computeGreatestUnit value of duration.
|
10276
|
+
buildRangeFromDuration: function(date, direction, duration, unit) {
|
10277
|
+
var customAlignment = this.opt('dateAlignment');
|
10278
|
+
var start = date.clone();
|
10279
|
+
var end;
|
10280
|
+
|
10281
|
+
// if the view displays a single day or smaller
|
10282
|
+
if (duration.as('days') <= 1) {
|
10283
|
+
if (this.isHiddenDay(start)) {
|
10284
|
+
start = this.skipHiddenDays(start, direction);
|
10285
|
+
start.startOf('day');
|
10286
|
+
}
|
10287
|
+
}
|
10288
|
+
|
10289
|
+
start.startOf(customAlignment || unit);
|
10290
|
+
end = start.clone().add(duration);
|
10291
|
+
|
10292
|
+
return { start: start, end: end };
|
10293
|
+
},
|
10294
|
+
|
10295
|
+
|
10296
|
+
// Builds the "current" range when a dayCount is specified.
|
10297
|
+
buildRangeFromDayCount: function(date, direction, dayCount) {
|
10298
|
+
var customAlignment = this.opt('dateAlignment');
|
10299
|
+
var runningCount = 0;
|
10300
|
+
var start = date.clone();
|
10301
|
+
var end;
|
10302
|
+
|
10303
|
+
if (customAlignment) {
|
10304
|
+
start.startOf(customAlignment);
|
10305
|
+
}
|
10306
|
+
|
10307
|
+
start.startOf('day');
|
10308
|
+
start = this.skipHiddenDays(start, direction);
|
10309
|
+
|
10310
|
+
end = start.clone();
|
10311
|
+
do {
|
10312
|
+
end.add(1, 'day');
|
10313
|
+
if (!this.isHiddenDay(end)) {
|
10314
|
+
runningCount++;
|
10315
|
+
}
|
10316
|
+
} while (runningCount < dayCount);
|
10317
|
+
|
10318
|
+
return { start: start, end: end };
|
10319
|
+
},
|
10320
|
+
|
10321
|
+
|
10322
|
+
// Builds a normalized range object for the "visible" range,
|
10323
|
+
// which is a way to define the currentRange and activeRange at the same time.
|
10324
|
+
buildCustomVisibleRange: function(date) {
|
10325
|
+
var visibleRange = this.getRangeOption(
|
10326
|
+
'visibleRange',
|
10327
|
+
this.calendar.moment(date) // correct zone. also generates new obj that avoids mutations
|
10328
|
+
);
|
10329
|
+
|
10330
|
+
if (visibleRange && (!visibleRange.start || !visibleRange.end)) {
|
10331
|
+
return null;
|
10332
|
+
}
|
10333
|
+
|
10334
|
+
return visibleRange;
|
10335
|
+
},
|
10336
|
+
|
10337
|
+
|
10338
|
+
// Computes the range that will represent the element/cells for *rendering*,
|
10339
|
+
// but which may have voided days/times.
|
10340
|
+
buildRenderRange: function(currentRange, currentRangeUnit) {
|
10341
|
+
// cut off days in the currentRange that are hidden
|
10342
|
+
return this.trimHiddenDays(currentRange);
|
10343
|
+
},
|
10344
|
+
|
10345
|
+
|
10346
|
+
// Compute the duration value that should be added/substracted to the current date
|
10347
|
+
// when a prev/next operation happens.
|
10348
|
+
buildDateIncrement: function(fallback) {
|
10349
|
+
var dateIncrementInput = this.opt('dateIncrement');
|
10350
|
+
var customAlignment;
|
10351
|
+
|
10352
|
+
if (dateIncrementInput) {
|
10353
|
+
return moment.duration(dateIncrementInput);
|
10354
|
+
}
|
10355
|
+
else if ((customAlignment = this.opt('dateAlignment'))) {
|
10356
|
+
return moment.duration(1, customAlignment);
|
10357
|
+
}
|
10358
|
+
else if (fallback) {
|
10359
|
+
return fallback;
|
10360
|
+
}
|
10361
|
+
else {
|
10362
|
+
return moment.duration({ days: 1 });
|
10363
|
+
}
|
10364
|
+
},
|
10365
|
+
|
10366
|
+
|
10367
|
+
// Remove days from the beginning and end of the range that are computed as hidden.
|
10368
|
+
trimHiddenDays: function(inputRange) {
|
10369
|
+
return {
|
10370
|
+
start: this.skipHiddenDays(inputRange.start),
|
10371
|
+
end: this.skipHiddenDays(inputRange.end, -1, true) // exclusively move backwards
|
10372
|
+
};
|
10373
|
+
},
|
10374
|
+
|
10375
|
+
|
10376
|
+
// Compute the number of the give units in the "current" range.
|
10377
|
+
// Will return a floating-point number. Won't round.
|
10378
|
+
currentRangeAs: function(unit) {
|
10379
|
+
var currentRange = this.currentRange;
|
10380
|
+
return currentRange.end.diff(currentRange.start, unit, true);
|
10381
|
+
},
|
10382
|
+
|
10383
|
+
|
10384
|
+
// Arguments after name will be forwarded to a hypothetical function value
|
10385
|
+
// WARNING: passed-in arguments will be given to generator functions as-is and can cause side-effects.
|
10386
|
+
// Always clone your objects if you fear mutation.
|
10387
|
+
getRangeOption: function(name) {
|
10388
|
+
var val = this.opt(name);
|
10389
|
+
|
10390
|
+
if (typeof val === 'function') {
|
10391
|
+
val = val.apply(
|
10392
|
+
null,
|
10393
|
+
Array.prototype.slice.call(arguments, 1)
|
10394
|
+
);
|
10395
|
+
}
|
10396
|
+
|
10397
|
+
if (val) {
|
10398
|
+
return this.calendar.parseRange(val);
|
10399
|
+
}
|
10400
|
+
},
|
10401
|
+
|
10402
|
+
|
10403
|
+
/* Hidden Days
|
10404
|
+
------------------------------------------------------------------------------------------------------------------*/
|
10405
|
+
|
10406
|
+
|
10407
|
+
// Initializes internal variables related to calculating hidden days-of-week
|
10408
|
+
initHiddenDays: function() {
|
10409
|
+
var hiddenDays = this.opt('hiddenDays') || []; // array of day-of-week indices that are hidden
|
10410
|
+
var isHiddenDayHash = []; // is the day-of-week hidden? (hash with day-of-week-index -> bool)
|
10411
|
+
var dayCnt = 0;
|
10412
|
+
var i;
|
10413
|
+
|
10414
|
+
if (this.opt('weekends') === false) {
|
10415
|
+
hiddenDays.push(0, 6); // 0=sunday, 6=saturday
|
10416
|
+
}
|
10417
|
+
|
9801
10418
|
for (i = 0; i < 7; i++) {
|
9802
10419
|
if (
|
9803
10420
|
!(isHiddenDayHash[i] = $.inArray(i, hiddenDays) !== -1)
|
@@ -9825,6 +10442,7 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
9825
10442
|
|
9826
10443
|
|
9827
10444
|
// Incrementing the current day until it is no longer a hidden day, returning a copy.
|
10445
|
+
// DOES NOT CONSIDER validRange!
|
9828
10446
|
// If the initial value of `date` is not a hidden day, don't do anything.
|
9829
10447
|
// Pass `isExclusive` as `true` if you are dealing with an end date.
|
9830
10448
|
// `inc` defaults to `1` (increment one day forward each time)
|
@@ -9837,44 +10455,6 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
|
|
9837
10455
|
out.add(inc, 'days');
|
9838
10456
|
}
|
9839
10457
|
return out;
|
9840
|
-
},
|
9841
|
-
|
9842
|
-
|
9843
|
-
// Returns the date range of the full days the given range visually appears to occupy.
|
9844
|
-
// Returns a new range object.
|
9845
|
-
computeDayRange: function(range) {
|
9846
|
-
var startDay = range.start.clone().stripTime(); // the beginning of the day the range starts
|
9847
|
-
var end = range.end;
|
9848
|
-
var endDay = null;
|
9849
|
-
var endTimeMS;
|
9850
|
-
|
9851
|
-
if (end) {
|
9852
|
-
endDay = end.clone().stripTime(); // the beginning of the day the range exclusively ends
|
9853
|
-
endTimeMS = +end.time(); // # of milliseconds into `endDay`
|
9854
|
-
|
9855
|
-
// If the end time is actually inclusively part of the next day and is equal to or
|
9856
|
-
// beyond the next day threshold, adjust the end to be the exclusive end of `endDay`.
|
9857
|
-
// Otherwise, leaving it as inclusive will cause it to exclude `endDay`.
|
9858
|
-
if (endTimeMS && endTimeMS >= this.nextDayThreshold) {
|
9859
|
-
endDay.add(1, 'days');
|
9860
|
-
}
|
9861
|
-
}
|
9862
|
-
|
9863
|
-
// If no end was specified, or if it is within `startDay` but not past nextDayThreshold,
|
9864
|
-
// assign the default duration of one day.
|
9865
|
-
if (!end || endDay <= startDay) {
|
9866
|
-
endDay = startDay.clone().add(1, 'days');
|
9867
|
-
}
|
9868
|
-
|
9869
|
-
return { start: startDay, end: endDay };
|
9870
|
-
},
|
9871
|
-
|
9872
|
-
|
9873
|
-
// Does the given event visually appear to occupy more than one day?
|
9874
|
-
isMultiDayEvent: function(event) {
|
9875
|
-
var range = this.computeDayRange(event); // event is range-ish
|
9876
|
-
|
9877
|
-
return range.end.diff(range.start, 'days') > 1;
|
9878
10458
|
}
|
9879
10459
|
|
9880
10460
|
});
|
@@ -10300,6 +10880,7 @@ var Calendar = FC.Calendar = Class.extend({
|
|
10300
10880
|
options: null, // all defaults combined with overrides
|
10301
10881
|
viewSpecCache: null, // cache of view definitions
|
10302
10882
|
view: null, // current View object
|
10883
|
+
currentDate: null, // unzoned moment. private (public API should use getDate instead)
|
10303
10884
|
header: null,
|
10304
10885
|
footer: null,
|
10305
10886
|
loadingLevel: 0, // number of simultaneous loading tasks
|
@@ -10367,7 +10948,7 @@ var Calendar = FC.Calendar = Class.extend({
|
|
10367
10948
|
var i;
|
10368
10949
|
var spec;
|
10369
10950
|
|
10370
|
-
if ($.inArray(unit,
|
10951
|
+
if ($.inArray(unit, unitsDesc) != -1) {
|
10371
10952
|
|
10372
10953
|
// put views that have buttons first. there will be duplicates, but oh well
|
10373
10954
|
viewTypes = this.header.getViewsWithButtons(); // TODO: include footer as well?
|
@@ -10396,6 +10977,7 @@ var Calendar = FC.Calendar = Class.extend({
|
|
10396
10977
|
var viewType = requestedViewType;
|
10397
10978
|
var spec; // for the view
|
10398
10979
|
var overrides; // for the view
|
10980
|
+
var durationInput;
|
10399
10981
|
var duration;
|
10400
10982
|
var unit;
|
10401
10983
|
|
@@ -10412,13 +10994,13 @@ var Calendar = FC.Calendar = Class.extend({
|
|
10412
10994
|
if (spec) {
|
10413
10995
|
specChain.unshift(spec);
|
10414
10996
|
defaultsChain.unshift(spec.defaults || {});
|
10415
|
-
|
10997
|
+
durationInput = durationInput || spec.duration;
|
10416
10998
|
viewType = viewType || spec.type;
|
10417
10999
|
}
|
10418
11000
|
|
10419
11001
|
if (overrides) {
|
10420
11002
|
overridesChain.unshift(overrides); // view-specific option hashes have options at zero-level
|
10421
|
-
|
11003
|
+
durationInput = durationInput || overrides.duration;
|
10422
11004
|
viewType = viewType || overrides.type;
|
10423
11005
|
}
|
10424
11006
|
}
|
@@ -10429,11 +11011,25 @@ var Calendar = FC.Calendar = Class.extend({
|
|
10429
11011
|
return false;
|
10430
11012
|
}
|
10431
11013
|
|
10432
|
-
|
10433
|
-
|
11014
|
+
// fall back to top-level `duration` option
|
11015
|
+
durationInput = durationInput ||
|
11016
|
+
this.dynamicOverrides.duration ||
|
11017
|
+
this.overrides.duration;
|
11018
|
+
|
11019
|
+
if (durationInput) {
|
11020
|
+
duration = moment.duration(durationInput);
|
11021
|
+
|
10434
11022
|
if (duration.valueOf()) { // valid?
|
11023
|
+
|
11024
|
+
unit = computeGreatestUnit(duration);
|
11025
|
+
|
11026
|
+
// prevent days:7 from being interpreted as a week
|
11027
|
+
if (unit === 'week' && typeof durationInput === 'object' && durationInput.days) {
|
11028
|
+
unit = 'day';
|
11029
|
+
}
|
11030
|
+
|
10435
11031
|
spec.duration = duration;
|
10436
|
-
|
11032
|
+
spec.durationUnit = unit;
|
10437
11033
|
|
10438
11034
|
// view is a single-unit duration, like "week" or "day"
|
10439
11035
|
// incorporate options for this. lowest priority
|
@@ -10504,7 +11100,7 @@ var Calendar = FC.Calendar = Class.extend({
|
|
10504
11100
|
instantiateView: function(viewType) {
|
10505
11101
|
var spec = this.getViewSpec(viewType);
|
10506
11102
|
|
10507
|
-
return new spec['class'](this,
|
11103
|
+
return new spec['class'](this, spec);
|
10508
11104
|
},
|
10509
11105
|
|
10510
11106
|
|
@@ -10545,6 +11141,123 @@ var Calendar = FC.Calendar = Class.extend({
|
|
10545
11141
|
end = start.clone().add(this.defaultAllDayEventDuration);
|
10546
11142
|
}
|
10547
11143
|
|
11144
|
+
return { start: start, end: end };
|
11145
|
+
},
|
11146
|
+
|
11147
|
+
|
11148
|
+
// Current Date
|
11149
|
+
// ------------
|
11150
|
+
|
11151
|
+
|
11152
|
+
/*
|
11153
|
+
Called before initialize()
|
11154
|
+
*/
|
11155
|
+
initCurrentDate: function() {
|
11156
|
+
// compute the initial ambig-timezone date
|
11157
|
+
if (this.options.defaultDate != null) {
|
11158
|
+
this.currentDate = this.moment(this.options.defaultDate).stripZone();
|
11159
|
+
}
|
11160
|
+
else {
|
11161
|
+
this.currentDate = this.getNow(); // getNow already returns unzoned
|
11162
|
+
}
|
11163
|
+
},
|
11164
|
+
|
11165
|
+
|
11166
|
+
changeView: function(viewName, dateOrRange) {
|
11167
|
+
|
11168
|
+
if (dateOrRange) {
|
11169
|
+
|
11170
|
+
if (dateOrRange.start && dateOrRange.end) { // a range
|
11171
|
+
this.recordOptionOverrides({ // will not rerender
|
11172
|
+
visibleRange: dateOrRange
|
11173
|
+
});
|
11174
|
+
}
|
11175
|
+
else { // a date
|
11176
|
+
this.currentDate = this.moment(dateOrRange).stripZone(); // just like gotoDate
|
11177
|
+
}
|
11178
|
+
}
|
11179
|
+
|
11180
|
+
this.renderView(viewName);
|
11181
|
+
},
|
11182
|
+
|
11183
|
+
|
11184
|
+
prev: function() {
|
11185
|
+
var prevInfo = this.view.buildPrevRangeInfo(this.currentDate);
|
11186
|
+
|
11187
|
+
if (prevInfo.isValid) {
|
11188
|
+
this.currentDate = prevInfo.date;
|
11189
|
+
this.renderView();
|
11190
|
+
}
|
11191
|
+
},
|
11192
|
+
|
11193
|
+
|
11194
|
+
next: function() {
|
11195
|
+
var nextInfo = this.view.buildNextRangeInfo(this.currentDate);
|
11196
|
+
|
11197
|
+
if (nextInfo.isValid) {
|
11198
|
+
this.currentDate = nextInfo.date;
|
11199
|
+
this.renderView();
|
11200
|
+
}
|
11201
|
+
},
|
11202
|
+
|
11203
|
+
|
11204
|
+
prevYear: function() {
|
11205
|
+
this.currentDate.add(-1, 'years');
|
11206
|
+
this.renderView();
|
11207
|
+
},
|
11208
|
+
|
11209
|
+
|
11210
|
+
nextYear: function() {
|
11211
|
+
this.currentDate.add(1, 'years');
|
11212
|
+
this.renderView();
|
11213
|
+
},
|
11214
|
+
|
11215
|
+
|
11216
|
+
today: function() {
|
11217
|
+
this.currentDate = this.getNow(); // should deny like prev/next?
|
11218
|
+
this.renderView();
|
11219
|
+
},
|
11220
|
+
|
11221
|
+
|
11222
|
+
gotoDate: function(zonedDateInput) {
|
11223
|
+
this.currentDate = this.moment(zonedDateInput).stripZone();
|
11224
|
+
this.renderView();
|
11225
|
+
},
|
11226
|
+
|
11227
|
+
|
11228
|
+
incrementDate: function(delta) {
|
11229
|
+
this.currentDate.add(moment.duration(delta));
|
11230
|
+
this.renderView();
|
11231
|
+
},
|
11232
|
+
|
11233
|
+
|
11234
|
+
// for external API
|
11235
|
+
getDate: function() {
|
11236
|
+
return this.applyTimezone(this.currentDate); // infuse the calendar's timezone
|
11237
|
+
},
|
11238
|
+
|
11239
|
+
|
11240
|
+
// will return `null` if invalid range
|
11241
|
+
parseRange: function(rangeInput) {
|
11242
|
+
var start = null;
|
11243
|
+
var end = null;
|
11244
|
+
|
11245
|
+
if (rangeInput.start) {
|
11246
|
+
start = this.moment(rangeInput.start).stripZone();
|
11247
|
+
}
|
11248
|
+
|
11249
|
+
if (rangeInput.end) {
|
11250
|
+
end = this.moment(rangeInput.end).stripZone();
|
11251
|
+
}
|
11252
|
+
|
11253
|
+
if (!start && !end) {
|
11254
|
+
return null;
|
11255
|
+
}
|
11256
|
+
|
11257
|
+
if (start && end && end.isBefore(start)) {
|
11258
|
+
return null;
|
11259
|
+
}
|
11260
|
+
|
10548
11261
|
return { start: start, end: end };
|
10549
11262
|
}
|
10550
11263
|
|
@@ -10567,21 +11280,13 @@ function Calendar_constructor(element, overrides) {
|
|
10567
11280
|
t.render = render;
|
10568
11281
|
t.destroy = destroy;
|
10569
11282
|
t.rerenderEvents = rerenderEvents;
|
10570
|
-
t.changeView = renderView; // `renderView` will switch to another view
|
10571
11283
|
t.select = select;
|
10572
11284
|
t.unselect = unselect;
|
10573
|
-
t.prev = prev;
|
10574
|
-
t.next = next;
|
10575
|
-
t.prevYear = prevYear;
|
10576
|
-
t.nextYear = nextYear;
|
10577
|
-
t.today = today;
|
10578
|
-
t.gotoDate = gotoDate;
|
10579
|
-
t.incrementDate = incrementDate;
|
10580
11285
|
t.zoomTo = zoomTo;
|
10581
|
-
t.getDate = getDate;
|
10582
11286
|
t.getCalendar = getCalendar;
|
10583
11287
|
t.getView = getView;
|
10584
11288
|
t.option = option; // getter/setter method
|
11289
|
+
t.recordOptionOverrides = recordOptionOverrides;
|
10585
11290
|
t.publiclyTrigger = publiclyTrigger;
|
10586
11291
|
|
10587
11292
|
|
@@ -10650,8 +11355,8 @@ function Calendar_constructor(element, overrides) {
|
|
10650
11355
|
|
10651
11356
|
// If the internal current date object already exists, move to new locale.
|
10652
11357
|
// We do NOT need to do this technique for event dates, because this happens when converting to "segments".
|
10653
|
-
if (
|
10654
|
-
localizeMoment(
|
11358
|
+
if (t.currentDate) {
|
11359
|
+
localizeMoment(t.currentDate); // sets to localeData
|
10655
11360
|
}
|
10656
11361
|
});
|
10657
11362
|
|
@@ -10799,23 +11504,15 @@ function Calendar_constructor(element, overrides) {
|
|
10799
11504
|
var suggestedViewHeight;
|
10800
11505
|
var windowResizeProxy; // wraps the windowResize function
|
10801
11506
|
var ignoreWindowResize = 0;
|
10802
|
-
var date; // unzoned
|
10803
11507
|
|
10804
11508
|
|
11509
|
+
this.initCurrentDate();
|
11510
|
+
|
10805
11511
|
|
10806
11512
|
// Main Rendering
|
10807
11513
|
// -----------------------------------------------------------------------------------
|
10808
11514
|
|
10809
11515
|
|
10810
|
-
// compute the initial ambig-timezone date
|
10811
|
-
if (t.options.defaultDate != null) {
|
10812
|
-
date = t.moment(t.options.defaultDate).stripZone();
|
10813
|
-
}
|
10814
|
-
else {
|
10815
|
-
date = t.getNow(); // getNow already returns unzoned
|
10816
|
-
}
|
10817
|
-
|
10818
|
-
|
10819
11516
|
function render() {
|
10820
11517
|
if (!content) {
|
10821
11518
|
initialRender();
|
@@ -10946,32 +11643,20 @@ function Calendar_constructor(element, overrides) {
|
|
10946
11643
|
|
10947
11644
|
if (currentView) {
|
10948
11645
|
|
10949
|
-
|
10950
|
-
date = currentView.massageCurrentDate(date);
|
11646
|
+
if (elementVisible()) {
|
10951
11647
|
|
10952
|
-
|
10953
|
-
|
10954
|
-
|
10955
|
-
!( // NOT within interval range signals an implicit date window change
|
10956
|
-
date >= currentView.intervalStart &&
|
10957
|
-
date < currentView.intervalEnd
|
10958
|
-
)
|
10959
|
-
) {
|
10960
|
-
if (elementVisible()) {
|
10961
|
-
|
10962
|
-
if (forcedScroll) {
|
10963
|
-
currentView.captureInitialScroll(forcedScroll);
|
10964
|
-
}
|
11648
|
+
if (forcedScroll) {
|
11649
|
+
currentView.captureInitialScroll(forcedScroll);
|
11650
|
+
}
|
10965
11651
|
|
10966
|
-
|
11652
|
+
currentView.setDate(t.currentDate);
|
10967
11653
|
|
10968
|
-
|
10969
|
-
|
10970
|
-
|
11654
|
+
// TODO: make setDate return the revised date.
|
11655
|
+
// Difficult because of the pseudo-async nature, promises.
|
11656
|
+
t.currentDate = currentView.currentDate;
|
10971
11657
|
|
10972
|
-
|
10973
|
-
|
10974
|
-
updateToolbarsTodayButton();
|
11658
|
+
if (forcedScroll) {
|
11659
|
+
currentView.releaseScroll();
|
10975
11660
|
}
|
10976
11661
|
}
|
10977
11662
|
}
|
@@ -10982,6 +11667,7 @@ function Calendar_constructor(element, overrides) {
|
|
10982
11667
|
|
10983
11668
|
ignoreWindowResize--;
|
10984
11669
|
}
|
11670
|
+
t.renderView = renderView;
|
10985
11671
|
|
10986
11672
|
|
10987
11673
|
// Unrenders the current view and reflects this change in the Header.
|
@@ -11089,7 +11775,7 @@ function Calendar_constructor(element, overrides) {
|
|
11089
11775
|
if (
|
11090
11776
|
!ignoreWindowResize &&
|
11091
11777
|
ev.target === window && // so we don't process jqui "resize" events that have bubbled up
|
11092
|
-
currentView.
|
11778
|
+
currentView.renderRange // view has already been rendered
|
11093
11779
|
) {
|
11094
11780
|
if (updateSize(true)) {
|
11095
11781
|
currentView.publiclyTrigger('windowResize', _element);
|
@@ -11164,15 +11850,33 @@ function Calendar_constructor(element, overrides) {
|
|
11164
11850
|
};
|
11165
11851
|
|
11166
11852
|
|
11167
|
-
function
|
11853
|
+
t.updateToolbarButtons = function() {
|
11168
11854
|
var now = t.getNow();
|
11169
|
-
|
11170
|
-
|
11171
|
-
|
11172
|
-
|
11173
|
-
|
11174
|
-
|
11175
|
-
|
11855
|
+
var todayInfo = currentView.buildRangeInfo(now);
|
11856
|
+
var prevInfo = currentView.buildPrevRangeInfo(t.currentDate);
|
11857
|
+
var nextInfo = currentView.buildNextRangeInfo(t.currentDate);
|
11858
|
+
|
11859
|
+
toolbarsManager.proxyCall(
|
11860
|
+
(todayInfo.isValid && !isDateWithinRange(now, currentView.currentRange)) ?
|
11861
|
+
'enableButton' :
|
11862
|
+
'disableButton',
|
11863
|
+
'today'
|
11864
|
+
);
|
11865
|
+
|
11866
|
+
toolbarsManager.proxyCall(
|
11867
|
+
prevInfo.isValid ?
|
11868
|
+
'enableButton' :
|
11869
|
+
'disableButton',
|
11870
|
+
'prev'
|
11871
|
+
);
|
11872
|
+
|
11873
|
+
toolbarsManager.proxyCall(
|
11874
|
+
nextInfo.isValid ?
|
11875
|
+
'enableButton' :
|
11876
|
+
'disableButton',
|
11877
|
+
'next'
|
11878
|
+
);
|
11879
|
+
};
|
11176
11880
|
|
11177
11881
|
|
11178
11882
|
|
@@ -11195,53 +11899,6 @@ function Calendar_constructor(element, overrides) {
|
|
11195
11899
|
}
|
11196
11900
|
|
11197
11901
|
|
11198
|
-
|
11199
|
-
/* Date
|
11200
|
-
-----------------------------------------------------------------------------*/
|
11201
|
-
|
11202
|
-
|
11203
|
-
function prev() {
|
11204
|
-
date = currentView.computePrevDate(date);
|
11205
|
-
renderView();
|
11206
|
-
}
|
11207
|
-
|
11208
|
-
|
11209
|
-
function next() {
|
11210
|
-
date = currentView.computeNextDate(date);
|
11211
|
-
renderView();
|
11212
|
-
}
|
11213
|
-
|
11214
|
-
|
11215
|
-
function prevYear() {
|
11216
|
-
date.add(-1, 'years');
|
11217
|
-
renderView();
|
11218
|
-
}
|
11219
|
-
|
11220
|
-
|
11221
|
-
function nextYear() {
|
11222
|
-
date.add(1, 'years');
|
11223
|
-
renderView();
|
11224
|
-
}
|
11225
|
-
|
11226
|
-
|
11227
|
-
function today() {
|
11228
|
-
date = t.getNow();
|
11229
|
-
renderView();
|
11230
|
-
}
|
11231
|
-
|
11232
|
-
|
11233
|
-
function gotoDate(zonedDateInput) {
|
11234
|
-
date = t.moment(zonedDateInput).stripZone();
|
11235
|
-
renderView();
|
11236
|
-
}
|
11237
|
-
|
11238
|
-
|
11239
|
-
function incrementDate(delta) {
|
11240
|
-
date.add(moment.duration(delta));
|
11241
|
-
renderView();
|
11242
|
-
}
|
11243
|
-
|
11244
|
-
|
11245
11902
|
// Forces navigation to a view for the given date.
|
11246
11903
|
// `viewType` can be a specific view name or a generic one like "week" or "day".
|
11247
11904
|
function zoomTo(newDate, viewType) {
|
@@ -11250,17 +11907,11 @@ function Calendar_constructor(element, overrides) {
|
|
11250
11907
|
viewType = viewType || 'day'; // day is default zoom
|
11251
11908
|
spec = t.getViewSpec(viewType) || t.getUnitViewSpec(viewType);
|
11252
11909
|
|
11253
|
-
|
11910
|
+
t.currentDate = newDate.clone();
|
11254
11911
|
renderView(spec ? spec.type : null);
|
11255
11912
|
}
|
11256
11913
|
|
11257
11914
|
|
11258
|
-
// for external API
|
11259
|
-
function getDate() {
|
11260
|
-
return t.applyTimezone(date); // infuse the calendar's timezone
|
11261
|
-
}
|
11262
|
-
|
11263
|
-
|
11264
11915
|
|
11265
11916
|
/* Height "Freezing"
|
11266
11917
|
-----------------------------------------------------------------------------*/
|
@@ -11332,16 +11983,9 @@ function Calendar_constructor(element, overrides) {
|
|
11332
11983
|
var optionCnt = 0;
|
11333
11984
|
var optionName;
|
11334
11985
|
|
11335
|
-
|
11336
|
-
t.dynamicOverrides[optionName] = newOptionHash[optionName];
|
11337
|
-
}
|
11338
|
-
|
11339
|
-
t.viewSpecCache = {}; // the dynamic override invalidates the options in this cache, so just clear it
|
11340
|
-
t.populateOptionsHash(); // this.options needs to be recomputed after the dynamic override
|
11986
|
+
recordOptionOverrides(newOptionHash);
|
11341
11987
|
|
11342
|
-
// trigger handlers after this.options has been updated
|
11343
11988
|
for (optionName in newOptionHash) {
|
11344
|
-
t.triggerOptionHandlers(optionName); // recall bindOption/bindOptions
|
11345
11989
|
optionCnt++;
|
11346
11990
|
}
|
11347
11991
|
|
@@ -11377,6 +12021,24 @@ function Calendar_constructor(element, overrides) {
|
|
11377
12021
|
}
|
11378
12022
|
|
11379
12023
|
|
12024
|
+
// stores the new options internally, but does not rerender anything.
|
12025
|
+
function recordOptionOverrides(newOptionHash) {
|
12026
|
+
var optionName;
|
12027
|
+
|
12028
|
+
for (optionName in newOptionHash) {
|
12029
|
+
t.dynamicOverrides[optionName] = newOptionHash[optionName];
|
12030
|
+
}
|
12031
|
+
|
12032
|
+
t.viewSpecCache = {}; // the dynamic override invalidates the options in this cache, so just clear it
|
12033
|
+
t.populateOptionsHash(); // this.options needs to be recomputed after the dynamic override
|
12034
|
+
|
12035
|
+
// trigger handlers after this.options has been updated
|
12036
|
+
for (optionName in newOptionHash) {
|
12037
|
+
t.triggerOptionHandlers(optionName); // recall bindOption/bindOptions
|
12038
|
+
}
|
12039
|
+
}
|
12040
|
+
|
12041
|
+
|
11380
12042
|
function publiclyTrigger(name, thisObj) {
|
11381
12043
|
var args = Array.prototype.slice.call(arguments, 2);
|
11382
12044
|
|
@@ -11486,6 +12148,9 @@ Calendar.defaults = {
|
|
11486
12148
|
//nowIndicator: false,
|
11487
12149
|
|
11488
12150
|
scrollTime: '06:00:00',
|
12151
|
+
minTime: '00:00:00',
|
12152
|
+
maxTime: '24:00:00',
|
12153
|
+
showNonCurrentDates: true,
|
11489
12154
|
|
11490
12155
|
// event ajax
|
11491
12156
|
lazyFetching: true,
|
@@ -13183,8 +13848,8 @@ Calendar.prototype.expandBusinessHourEvents = function(wholeDay, inputs, ignoreN
|
|
13183
13848
|
events.push.apply(events, // append
|
13184
13849
|
this.expandEvent(
|
13185
13850
|
this.buildEventFromInput(input),
|
13186
|
-
view.start,
|
13187
|
-
view.end
|
13851
|
+
view.activeRange.start,
|
13852
|
+
view.activeRange.end
|
13188
13853
|
)
|
13189
13854
|
);
|
13190
13855
|
}
|
@@ -13236,38 +13901,30 @@ var BasicView = FC.BasicView = View.extend({
|
|
13236
13901
|
},
|
13237
13902
|
|
13238
13903
|
|
13239
|
-
//
|
13240
|
-
|
13241
|
-
View.prototype.
|
13242
|
-
|
13243
|
-
this.dayGrid.breakOnWeeks = /year|month|week/.test(this.intervalUnit); // do before setRange
|
13244
|
-
this.dayGrid.setRange(range);
|
13245
|
-
},
|
13246
|
-
|
13247
|
-
|
13248
|
-
// Compute the value to feed into setRange. Overrides superclass.
|
13249
|
-
computeRange: function(date) {
|
13250
|
-
var range = View.prototype.computeRange.call(this, date); // get value from the super-method
|
13904
|
+
// Computes the date range that will be rendered.
|
13905
|
+
buildRenderRange: function(currentRange, currentRangeUnit) {
|
13906
|
+
var renderRange = View.prototype.buildRenderRange.apply(this, arguments);
|
13251
13907
|
|
13252
13908
|
// year and month views should be aligned with weeks. this is already done for week
|
13253
|
-
if (
|
13254
|
-
|
13255
|
-
range.start = this.skipHiddenDays(range.start);
|
13909
|
+
if (/^(year|month)$/.test(currentRangeUnit)) {
|
13910
|
+
renderRange.start.startOf('week');
|
13256
13911
|
|
13257
13912
|
// make end-of-week if not already
|
13258
|
-
if (
|
13259
|
-
|
13260
|
-
range.end = this.skipHiddenDays(range.end, -1, true); // exclusively move backwards
|
13913
|
+
if (renderRange.end.weekday()) {
|
13914
|
+
renderRange.end.add(1, 'week').startOf('week'); // exclusively move backwards
|
13261
13915
|
}
|
13262
13916
|
}
|
13263
13917
|
|
13264
|
-
return
|
13918
|
+
return this.trimHiddenDays(renderRange);
|
13265
13919
|
},
|
13266
13920
|
|
13267
13921
|
|
13268
13922
|
// Renders the view into `this.el`, which should already be assigned
|
13269
13923
|
renderDates: function() {
|
13270
13924
|
|
13925
|
+
this.dayGrid.breakOnWeeks = /year|month|week/.test(this.currentRangeUnit); // do before Grid::setRange
|
13926
|
+
this.dayGrid.setRange(this.renderRange);
|
13927
|
+
|
13271
13928
|
this.dayNumbersVisible = this.dayGrid.rowCnt > 1; // TODO: make grid responsible
|
13272
13929
|
if (this.opt('weekNumbers')) {
|
13273
13930
|
if (this.opt('weekNumbersWithinDays')) {
|
@@ -13632,18 +14289,21 @@ var basicDayGridMethods = {
|
|
13632
14289
|
|
13633
14290
|
var MonthView = FC.MonthView = BasicView.extend({
|
13634
14291
|
|
13635
|
-
|
13636
|
-
|
13637
|
-
|
14292
|
+
|
14293
|
+
// Computes the date range that will be rendered.
|
14294
|
+
buildRenderRange: function() {
|
14295
|
+
var renderRange = BasicView.prototype.buildRenderRange.apply(this, arguments);
|
13638
14296
|
var rowCnt;
|
13639
14297
|
|
13640
14298
|
// ensure 6 weeks
|
13641
14299
|
if (this.isFixedWeeks()) {
|
13642
|
-
rowCnt = Math.ceil(
|
13643
|
-
|
14300
|
+
rowCnt = Math.ceil( // could be partial weeks due to hiddenDays
|
14301
|
+
renderRange.end.diff(renderRange.start, 'weeks', true) // dontRound=true
|
14302
|
+
);
|
14303
|
+
renderRange.end.add(6 - rowCnt, 'weeks');
|
13644
14304
|
}
|
13645
14305
|
|
13646
|
-
return
|
14306
|
+
return renderRange;
|
13647
14307
|
},
|
13648
14308
|
|
13649
14309
|
|
@@ -13713,6 +14373,9 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
13713
14373
|
// when the time-grid isn't tall enough to occupy the given height, we render an <hr> underneath
|
13714
14374
|
bottomRuleEl: null,
|
13715
14375
|
|
14376
|
+
// indicates that minTime/maxTime affects rendering
|
14377
|
+
usesMinMaxTime: true,
|
14378
|
+
|
13716
14379
|
|
13717
14380
|
initialize: function() {
|
13718
14381
|
this.timeGrid = this.instantiateTimeGrid();
|
@@ -13748,19 +14411,14 @@ var AgendaView = FC.AgendaView = View.extend({
|
|
13748
14411
|
------------------------------------------------------------------------------------------------------------------*/
|
13749
14412
|
|
13750
14413
|
|
13751
|
-
//
|
13752
|
-
|
13753
|
-
|
14414
|
+
// Renders the view into `this.el`, which has already been assigned
|
14415
|
+
renderDates: function() {
|
14416
|
+
|
14417
|
+
this.timeGrid.setRange(this.renderRange);
|
13754
14418
|
|
13755
|
-
this.timeGrid.setRange(range);
|
13756
14419
|
if (this.dayGrid) {
|
13757
|
-
this.dayGrid.setRange(
|
14420
|
+
this.dayGrid.setRange(this.renderRange);
|
13758
14421
|
}
|
13759
|
-
},
|
13760
|
-
|
13761
|
-
|
13762
|
-
// Renders the view into `this.el`, which has already been assigned
|
13763
|
-
renderDates: function() {
|
13764
14422
|
|
13765
14423
|
this.el.addClass('fc-agenda-view').html(this.renderSkeletonHtml());
|
13766
14424
|
this.renderHead();
|
@@ -14251,8 +14909,6 @@ fcViews.agenda = {
|
|
14251
14909
|
defaults: {
|
14252
14910
|
allDaySlot: true,
|
14253
14911
|
slotDuration: '00:30:00',
|
14254
|
-
minTime: '00:00:00',
|
14255
|
-
maxTime: '24:00:00',
|
14256
14912
|
slotEventOverlap: true // a bad name. confused with overlap/constraint system
|
14257
14913
|
}
|
14258
14914
|
};
|
@@ -14284,12 +14940,6 @@ var ListView = View.extend({
|
|
14284
14940
|
});
|
14285
14941
|
},
|
14286
14942
|
|
14287
|
-
setRange: function(range) {
|
14288
|
-
View.prototype.setRange.call(this, range); // super
|
14289
|
-
|
14290
|
-
this.grid.setRange(range); // needs to process range-related options
|
14291
|
-
},
|
14292
|
-
|
14293
14943
|
renderSkeleton: function() {
|
14294
14944
|
this.el.addClass(
|
14295
14945
|
'fc-list-view ' +
|
@@ -14315,6 +14965,10 @@ var ListView = View.extend({
|
|
14315
14965
|
subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
|
14316
14966
|
},
|
14317
14967
|
|
14968
|
+
renderDates: function() {
|
14969
|
+
this.grid.setRange(this.renderRange); // needs to process range-related options
|
14970
|
+
},
|
14971
|
+
|
14318
14972
|
renderEvents: function(events) {
|
14319
14973
|
this.grid.renderEvents(events);
|
14320
14974
|
},
|
@@ -14345,12 +14999,12 @@ var ListViewGrid = Grid.extend({
|
|
14345
14999
|
// slices by day
|
14346
15000
|
spanToSegs: function(span) {
|
14347
15001
|
var view = this.view;
|
14348
|
-
var dayStart = view.start.clone().time(0); // timed, so segs get times!
|
15002
|
+
var dayStart = view.renderRange.start.clone().time(0); // timed, so segs get times!
|
14349
15003
|
var dayIndex = 0;
|
14350
15004
|
var seg;
|
14351
15005
|
var segs = [];
|
14352
15006
|
|
14353
|
-
while (dayStart < view.end) {
|
15007
|
+
while (dayStart < view.renderRange.end) {
|
14354
15008
|
|
14355
15009
|
seg = intersectRanges(span, {
|
14356
15010
|
start: dayStart,
|
@@ -14442,7 +15096,7 @@ var ListViewGrid = Grid.extend({
|
|
14442
15096
|
|
14443
15097
|
// append a day header
|
14444
15098
|
tbodyEl.append(this.dayHeaderHtml(
|
14445
|
-
this.view.start.clone().add(dayIndex, 'days')
|
15099
|
+
this.view.renderRange.start.clone().add(dayIndex, 'days')
|
14446
15100
|
));
|
14447
15101
|
|
14448
15102
|
this.sortEventSegs(daySegs);
|