fullcalendar.io-rails 3.2.0 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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);
|