fullcalendar.io-rails 2.5.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/lib/fullcalendar.io/rails/version.rb +1 -1
  3. data/vendor/assets/javascripts/fullcalendar.js +732 -337
  4. data/vendor/assets/javascripts/fullcalendar/gcal.js +1 -1
  5. data/vendor/assets/javascripts/fullcalendar/lang-all.js +4 -4
  6. data/vendor/assets/javascripts/fullcalendar/lang/ar-ma.js +1 -1
  7. data/vendor/assets/javascripts/fullcalendar/lang/ar-sa.js +1 -1
  8. data/vendor/assets/javascripts/fullcalendar/lang/ar-tn.js +1 -1
  9. data/vendor/assets/javascripts/fullcalendar/lang/ar.js +1 -1
  10. data/vendor/assets/javascripts/fullcalendar/lang/bg.js +1 -1
  11. data/vendor/assets/javascripts/fullcalendar/lang/ca.js +1 -1
  12. data/vendor/assets/javascripts/fullcalendar/lang/cs.js +1 -1
  13. data/vendor/assets/javascripts/fullcalendar/lang/da.js +1 -1
  14. data/vendor/assets/javascripts/fullcalendar/lang/de-at.js +1 -1
  15. data/vendor/assets/javascripts/fullcalendar/lang/de.js +1 -1
  16. data/vendor/assets/javascripts/fullcalendar/lang/el.js +1 -1
  17. data/vendor/assets/javascripts/fullcalendar/lang/en-au.js +1 -1
  18. data/vendor/assets/javascripts/fullcalendar/lang/en-ca.js +1 -1
  19. data/vendor/assets/javascripts/fullcalendar/lang/en-gb.js +1 -1
  20. data/vendor/assets/javascripts/fullcalendar/lang/en-ie.js +1 -0
  21. data/vendor/assets/javascripts/fullcalendar/lang/en-nz.js +1 -0
  22. data/vendor/assets/javascripts/fullcalendar/lang/es.js +1 -1
  23. data/vendor/assets/javascripts/fullcalendar/lang/fa.js +1 -1
  24. data/vendor/assets/javascripts/fullcalendar/lang/fi.js +1 -1
  25. data/vendor/assets/javascripts/fullcalendar/lang/fr-ca.js +1 -1
  26. data/vendor/assets/javascripts/fullcalendar/lang/fr-ch.js +1 -0
  27. data/vendor/assets/javascripts/fullcalendar/lang/fr.js +1 -1
  28. data/vendor/assets/javascripts/fullcalendar/lang/he.js +1 -1
  29. data/vendor/assets/javascripts/fullcalendar/lang/hi.js +1 -1
  30. data/vendor/assets/javascripts/fullcalendar/lang/hr.js +1 -1
  31. data/vendor/assets/javascripts/fullcalendar/lang/hu.js +1 -1
  32. data/vendor/assets/javascripts/fullcalendar/lang/id.js +1 -1
  33. data/vendor/assets/javascripts/fullcalendar/lang/is.js +1 -1
  34. data/vendor/assets/javascripts/fullcalendar/lang/it.js +1 -1
  35. data/vendor/assets/javascripts/fullcalendar/lang/ja.js +1 -1
  36. data/vendor/assets/javascripts/fullcalendar/lang/ko.js +1 -1
  37. data/vendor/assets/javascripts/fullcalendar/lang/lt.js +1 -1
  38. data/vendor/assets/javascripts/fullcalendar/lang/lv.js +1 -1
  39. data/vendor/assets/javascripts/fullcalendar/lang/nb.js +1 -1
  40. data/vendor/assets/javascripts/fullcalendar/lang/nl.js +1 -1
  41. data/vendor/assets/javascripts/fullcalendar/lang/pl.js +1 -1
  42. data/vendor/assets/javascripts/fullcalendar/lang/pt-br.js +1 -1
  43. data/vendor/assets/javascripts/fullcalendar/lang/pt.js +1 -1
  44. data/vendor/assets/javascripts/fullcalendar/lang/ro.js +1 -1
  45. data/vendor/assets/javascripts/fullcalendar/lang/ru.js +1 -1
  46. data/vendor/assets/javascripts/fullcalendar/lang/sk.js +1 -1
  47. data/vendor/assets/javascripts/fullcalendar/lang/sl.js +1 -1
  48. data/vendor/assets/javascripts/fullcalendar/lang/sr-cyrl.js +1 -1
  49. data/vendor/assets/javascripts/fullcalendar/lang/sr.js +1 -1
  50. data/vendor/assets/javascripts/fullcalendar/lang/sv.js +1 -1
  51. data/vendor/assets/javascripts/fullcalendar/lang/th.js +1 -1
  52. data/vendor/assets/javascripts/fullcalendar/lang/tr.js +1 -1
  53. data/vendor/assets/javascripts/fullcalendar/lang/uk.js +1 -1
  54. data/vendor/assets/javascripts/fullcalendar/lang/vi.js +1 -1
  55. data/vendor/assets/javascripts/fullcalendar/lang/zh-cn.js +1 -1
  56. data/vendor/assets/javascripts/fullcalendar/lang/zh-tw.js +1 -1
  57. data/vendor/assets/stylesheets/fullcalendar.css +66 -11
  58. data/vendor/assets/stylesheets/fullcalendar.print.css +9 -3
  59. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a8b41c47daa4cc5747c7e1dd590e46ee6803d420
4
- data.tar.gz: 9dc2a90ac34a59583135fdde034013f0747037f5
3
+ metadata.gz: 921eef27c1d797b39832dac660310d30f1922232
4
+ data.tar.gz: 55ff3c5363d6598581a43d8a644853b4eadafb79
5
5
  SHA512:
6
- metadata.gz: 94b199865b8090c27ca07b7eae55e116cb3f0e137493ff6ec0f00071ab522ddd1e09fc4e4fb3c9659884399c69ebb914502a2959ca81739d216c80446f8a2a1b
7
- data.tar.gz: 84161491503aee8b8aac3b67043696039588c00f074338182b0cd60e0f04b02c7affdfda45544f351ff300863a0b07a9264b25522e9768d5540dc7d67237fe41
6
+ metadata.gz: 3beaace52c2b46d5dbf8684c61af35b3a8df1b3b6b062e615d0c721c3778a9e2f361a1962cc46e1120fe515d38b28358d8f81920d0780f3a45679c13fb2e65aa
7
+ data.tar.gz: 8b14626bba7b6c681efa3d4ee58e93ed607ecc2902c6c917a60eea2c0454f845ffb4d65f0e1288a68dbb63f80ebd04d4784ba54ee51ab93de922d6d9e4ba459c
@@ -1,5 +1,5 @@
1
1
  module Fullcalendario
2
2
  module Rails
3
- VERSION = "2.5.0"
3
+ VERSION = "2.6.0"
4
4
  end
5
5
  end
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * FullCalendar v2.5.0
2
+ * FullCalendar v2.6.0
3
3
  * Docs & License: http://fullcalendar.io/
4
4
  * (c) 2015 Adam Shaw
5
5
  */
@@ -19,8 +19,8 @@
19
19
  ;;
20
20
 
21
21
  var FC = $.fullCalendar = {
22
- version: "2.5.0",
23
- internalApiVersion: 1
22
+ version: "2.6.0",
23
+ internalApiVersion: 2
24
24
  };
25
25
  var fcViews = FC.views = {};
26
26
 
@@ -247,7 +247,7 @@ function undistributeHeight(els) {
247
247
  function matchCellWidths(els) {
248
248
  var maxInnerWidth = 0;
249
249
 
250
- els.find('> *').each(function(i, innerEl) {
250
+ els.find('> span').each(function(i, innerEl) {
251
251
  var innerWidth = $(innerEl).outerWidth();
252
252
  if (innerWidth > maxInnerWidth) {
253
253
  maxInnerWidth = innerWidth;
@@ -3415,10 +3415,40 @@ var Grid = FC.Grid = Class.extend({
3415
3415
  },
3416
3416
 
3417
3417
 
3418
- /* Fill System (highlight, background events, business hours)
3418
+ /* Business Hours
3419
3419
  ------------------------------------------------------------------------------------------------------------------*/
3420
3420
 
3421
3421
 
3422
+ renderBusinessHours: function() {
3423
+ },
3424
+
3425
+
3426
+ unrenderBusinessHours: function() {
3427
+ },
3428
+
3429
+
3430
+ /* Now Indicator
3431
+ ------------------------------------------------------------------------------------------------------------------*/
3432
+
3433
+
3434
+ getNowIndicatorUnit: function() {
3435
+ },
3436
+
3437
+
3438
+ renderNowIndicator: function(date) {
3439
+ },
3440
+
3441
+
3442
+ unrenderNowIndicator: function() {
3443
+ },
3444
+
3445
+
3446
+ /* Fill System (highlight, background events, business hours)
3447
+ --------------------------------------------------------------------------------------------------------------------
3448
+ TODO: remove this system. like we did in TimeGrid
3449
+ */
3450
+
3451
+
3422
3452
  // Renders a set of rectangles over the given segments of time.
3423
3453
  // MUST RETURN a subset of segs, the segs that were actually rendered.
3424
3454
  // Responsible for populating this.elsByFill. TODO: better API for expressing this requirement
@@ -3511,7 +3541,7 @@ var Grid = FC.Grid = Class.extend({
3511
3541
  // Computes HTML classNames for a single-day element
3512
3542
  getDayClasses: function(date) {
3513
3543
  var view = this.view;
3514
- var today = view.calendar.getNow().stripTime();
3544
+ var today = view.calendar.getNow();
3515
3545
  var classes = [ 'fc-' + dayIDs[date.day()] ];
3516
3546
 
3517
3547
  if (
@@ -3550,7 +3580,7 @@ Grid.mixin({
3550
3580
  isDraggingSeg: false, // is a segment being dragged? boolean
3551
3581
  isResizingSeg: false, // is a segment being resized? boolean
3552
3582
  isDraggingExternal: false, // jqui-dragging an external element? boolean
3553
- segs: null, // the event segments currently rendered in the grid
3583
+ segs: null, // the *event* segments currently rendered in the grid. TODO: rename to `eventSegs`
3554
3584
 
3555
3585
 
3556
3586
  // Renders the given events onto the grid
@@ -3839,7 +3869,7 @@ Grid.mixin({
3839
3869
  event
3840
3870
  );
3841
3871
 
3842
- if (dropLocation &&!calendar.isEventSpanAllowed(_this.eventToSpan(dropLocation), event)) {
3872
+ if (dropLocation && !calendar.isEventSpanAllowed(_this.eventToSpan(dropLocation), event)) {
3843
3873
  disableCursor();
3844
3874
  dropLocation = null;
3845
3875
  }
@@ -3902,6 +3932,7 @@ Grid.mixin({
3902
3932
  // Given the spans an event drag began, and the span event was dropped, calculates the new zoned start/end/allDay
3903
3933
  // values for the event. Subclasses may override and set additional properties to be used by renderDrag.
3904
3934
  // A falsy returned value indicates an invalid drop.
3935
+ // DOES NOT consider overlap/constraint.
3905
3936
  computeEventDrop: function(startSpan, endSpan, event) {
3906
3937
  var calendar = this.view.calendar;
3907
3938
  var dragStart = startSpan.start;
@@ -3991,6 +4022,7 @@ Grid.mixin({
3991
4022
  // Called when a jQuery UI drag starts and it needs to be monitored for dropping
3992
4023
  listenToExternalDrag: function(el, ev, ui) {
3993
4024
  var _this = this;
4025
+ var calendar = this.view.calendar;
3994
4026
  var meta = getDraggedElMeta(el); // extra data about event drop, including possible event to create
3995
4027
  var dropLocation; // a null value signals an unsuccessful drag
3996
4028
 
@@ -4004,22 +4036,27 @@ Grid.mixin({
4004
4036
  hit.component.getHitSpan(hit), // since we are querying the parent view, might not belong to this grid
4005
4037
  meta
4006
4038
  );
4039
+
4040
+ if ( // invalid hit?
4041
+ dropLocation &&
4042
+ !calendar.isExternalSpanAllowed(_this.eventToSpan(dropLocation), dropLocation, meta.eventProps)
4043
+ ) {
4044
+ disableCursor();
4045
+ dropLocation = null;
4046
+ }
4047
+
4007
4048
  if (dropLocation) {
4008
4049
  _this.renderDrag(dropLocation); // called without a seg parameter
4009
4050
  }
4010
- else { // invalid hit
4011
- disableCursor();
4012
- }
4013
4051
  },
4014
4052
  hitOut: function() {
4015
4053
  dropLocation = null; // signal unsuccessful
4016
- _this.unrenderDrag();
4054
+ },
4055
+ hitDone: function() { // Called after a hitOut OR before a dragStop
4017
4056
  enableCursor();
4057
+ _this.unrenderDrag();
4018
4058
  },
4019
4059
  dragStop: function() {
4020
- _this.unrenderDrag();
4021
- enableCursor();
4022
-
4023
4060
  if (dropLocation) { // element was dropped on a valid hit
4024
4061
  _this.view.reportExternalDrop(meta, dropLocation, el, ev, ui);
4025
4062
  }
@@ -4036,6 +4073,7 @@ Grid.mixin({
4036
4073
  // Given a hit to be dropped upon, and misc data associated with the jqui drag (guaranteed to be a plain object),
4037
4074
  // returns the zoned start/end dates for the event that would result from the hypothetical drop. end might be null.
4038
4075
  // Returning a null value signals an invalid drop hit.
4076
+ // DOES NOT consider overlap/constraint.
4039
4077
  computeExternalDrop: function(span, meta) {
4040
4078
  var calendar = this.view.calendar;
4041
4079
  var dropLocation = {
@@ -4052,10 +4090,6 @@ Grid.mixin({
4052
4090
  dropLocation.end = dropLocation.start.clone().add(meta.duration);
4053
4091
  }
4054
4092
 
4055
- if (!calendar.isExternalSpanAllowed(this.eventToSpan(dropLocation), dropLocation, meta.eventProps)) {
4056
- return null;
4057
- }
4058
-
4059
4093
  return dropLocation;
4060
4094
  },
4061
4095
 
@@ -4176,7 +4210,8 @@ Grid.mixin({
4176
4210
 
4177
4211
 
4178
4212
  // Returns new zoned date information for an event segment being resized from its start OR end
4179
- // `type` is either 'start' or 'end'
4213
+ // `type` is either 'start' or 'end'.
4214
+ // DOES NOT consider overlap/constraint.
4180
4215
  computeEventResize: function(type, startSpan, endSpan, event) {
4181
4216
  var calendar = this.view.calendar;
4182
4217
  var delta = this.diffDates(endSpan[type], startSpan[type]);
@@ -4323,20 +4358,27 @@ Grid.mixin({
4323
4358
 
4324
4359
 
4325
4360
  // Generates an array of segments for the given single event
4361
+ // Can accept an event "location" as well (which only has start/end and no allDay)
4326
4362
  eventToSegs: function(event) {
4327
4363
  return this.eventsToSegs([ event ]);
4328
4364
  },
4329
4365
 
4330
4366
 
4331
- // Generates a single span (always unzoned) by using the given event's dates.
4332
- // Does not do any inverting for inverse-background events.
4333
4367
  eventToSpan: function(event) {
4368
+ return this.eventToSpans(event)[0];
4369
+ },
4370
+
4371
+
4372
+ // Generates spans (always unzoned) for the given event.
4373
+ // Does not do any inverting for inverse-background events.
4374
+ // Can accept an event "location" as well (which only has start/end and no allDay)
4375
+ eventToSpans: function(event) {
4334
4376
  var range = this.eventToRange(event);
4335
- this.transformEventSpan(range, event); // convert it to a span, in-place
4336
- return range;
4377
+ return this.eventRangeToSpans(range, event);
4337
4378
  },
4338
4379
 
4339
4380
 
4381
+
4340
4382
  // Converts an array of event objects into an array of event segment objects.
4341
4383
  // A custom `segSliceFunc` may be given for arbitrarily slicing up events.
4342
4384
  // Doesn't guarantee an order for the resulting array.
@@ -4358,13 +4400,15 @@ Grid.mixin({
4358
4400
  ranges = _this.invertRanges(ranges);
4359
4401
 
4360
4402
  for (i = 0; i < ranges.length; i++) {
4361
- _this.generateEventSegs(ranges[i], events[0], segSliceFunc, segs);
4403
+ segs.push.apply(segs, // append to
4404
+ _this.eventRangeToSegs(ranges[i], events[0], segSliceFunc));
4362
4405
  }
4363
4406
  }
4364
4407
  // normal event ranges
4365
4408
  else {
4366
4409
  for (i = 0; i < ranges.length; i++) {
4367
- _this.generateEventSegs(ranges[i], events[i], segSliceFunc, segs);
4410
+ segs.push.apply(segs, // append to
4411
+ _this.eventRangeToSegs(ranges[i], events[i], segSliceFunc));
4368
4412
  }
4369
4413
  }
4370
4414
  });
@@ -4374,44 +4418,62 @@ Grid.mixin({
4374
4418
 
4375
4419
 
4376
4420
  // Generates the unzoned start/end dates an event appears to occupy
4421
+ // Can accept an event "location" as well (which only has start/end and no allDay)
4377
4422
  eventToRange: function(event) {
4378
4423
  return {
4379
4424
  start: event.start.clone().stripZone(),
4380
- end: this.view.calendar.getEventEnd(event).stripZone()
4425
+ end: (
4426
+ event.end ?
4427
+ event.end.clone() :
4428
+ // derive the end from the start and allDay. compute allDay if necessary
4429
+ this.view.calendar.getDefaultEventEnd(
4430
+ event.allDay != null ?
4431
+ event.allDay :
4432
+ !event.start.hasTime(),
4433
+ event.start
4434
+ )
4435
+ ).stripZone()
4381
4436
  };
4382
4437
  },
4383
4438
 
4384
4439
 
4385
- // Given an event's span (unzoned start/end and other misc data), and the event itself,
4386
- // slice into segments (using the segSliceFunc function if specified) and append to the `out` array.
4387
- // SIDE EFFECT: will mutate the given `range`.
4388
- generateEventSegs: function(range, event, segSliceFunc, out) {
4389
- var segs;
4440
+ // Given an event's range (unzoned start/end), and the event itself,
4441
+ // slice into segments (using the segSliceFunc function if specified)
4442
+ eventRangeToSegs: function(range, event, segSliceFunc) {
4443
+ var spans = this.eventRangeToSpans(range, event);
4444
+ var segs = [];
4390
4445
  var i;
4391
4446
 
4392
- this.transformEventSpan(range, event); // converts the range to a span
4393
-
4394
- segs = segSliceFunc ? segSliceFunc(range) : this.spanToSegs(range);
4395
-
4396
- for (i = 0; i < segs.length; i++) {
4397
- this.transformEventSeg(segs[i], range, event);
4398
- out.push(segs[i]);
4447
+ for (i = 0; i < spans.length; i++) {
4448
+ segs.push.apply(segs, // append to
4449
+ this.eventSpanToSegs(spans[i], event, segSliceFunc));
4399
4450
  }
4451
+
4452
+ return segs;
4400
4453
  },
4401
4454
 
4402
4455
 
4403
- // Given a range (unzoned start/end) that is about to become a span,
4404
- // attach any event-derived properties to it.
4405
- transformEventSpan: function(range, event) {
4406
- // subclasses can implement
4456
+ // Given an event's unzoned date range, return an array of "span" objects.
4457
+ // Subclasses can override.
4458
+ eventRangeToSpans: function(range, event) {
4459
+ return [ $.extend({}, range) ]; // copy into a single-item array
4407
4460
  },
4408
4461
 
4409
4462
 
4410
- // Given a segment object, attach any extra properties, based off of its source span and event.
4411
- transformEventSeg: function(seg, span, event) {
4412
- seg.event = event;
4413
- seg.eventStartMS = +span.start; // TODO: not the best name after making spans unzoned
4414
- seg.eventDurationMS = span.end - span.start;
4463
+ // Given an event's span (unzoned start/end and other misc data), and the event itself,
4464
+ // slices into segments and attaches event-derived properties to them.
4465
+ eventSpanToSegs: function(span, event, segSliceFunc) {
4466
+ var segs = segSliceFunc ? segSliceFunc(span) : this.spanToSegs(span);
4467
+ var i, seg;
4468
+
4469
+ for (i = 0; i < segs.length; i++) {
4470
+ seg = segs[i];
4471
+ seg.event = event;
4472
+ seg.eventStartMS = +span.start; // TODO: not the best name after making spans unzoned
4473
+ seg.eventDurationMS = span.end - span.start;
4474
+ }
4475
+
4476
+ return segs;
4415
4477
  },
4416
4478
 
4417
4479
 
@@ -4478,6 +4540,7 @@ function isBgEvent(event) { // returns true if background OR inverse-background
4478
4540
  var rendering = getEventRendering(event);
4479
4541
  return rendering === 'background' || rendering === 'inverse-background';
4480
4542
  }
4543
+ FC.isBgEvent = isBgEvent; // export
4481
4544
 
4482
4545
 
4483
4546
  function isInverseBgEvent(event) {
@@ -4856,13 +4919,23 @@ var DayTableMixin = FC.DayTableMixin = {
4856
4919
  },
4857
4920
 
4858
4921
 
4859
- renderHeadDateCellHtml: function(date, colspan) {
4922
+ // TODO: when internalApiVersion, accept an object for HTML attributes
4923
+ // (colspan should be no different)
4924
+ renderHeadDateCellHtml: function(date, colspan, otherAttrs) {
4860
4925
  var view = this.view;
4861
4926
 
4862
4927
  return '' +
4863
4928
  '<th class="fc-day-header ' + view.widgetHeaderClass + ' fc-' + dayIDs[date.day()] + '"' +
4864
- (colspan > 1 ? ' colspan="' + colspan + '"' : '') +
4865
- '>' +
4929
+ (this.rowCnt == 1 ?
4930
+ ' data-date="' + date.format('YYYY-MM-DD') + '"' :
4931
+ '') +
4932
+ (colspan > 1 ?
4933
+ ' colspan="' + colspan + '"' :
4934
+ '') +
4935
+ (otherAttrs ?
4936
+ ' ' + otherAttrs :
4937
+ '') +
4938
+ '>' +
4866
4939
  htmlEscape(date.format(this.colHeadFormat)) +
4867
4940
  '</th>';
4868
4941
  },
@@ -4900,7 +4973,7 @@ var DayTableMixin = FC.DayTableMixin = {
4900
4973
  },
4901
4974
 
4902
4975
 
4903
- renderBgCellHtml: function(date) {
4976
+ renderBgCellHtml: function(date, otherAttrs) {
4904
4977
  var view = this.view;
4905
4978
  var classes = this.getDayClasses(date);
4906
4979
 
@@ -4908,6 +4981,9 @@ var DayTableMixin = FC.DayTableMixin = {
4908
4981
 
4909
4982
  return '<td class="' + classes.join(' ') + '"' +
4910
4983
  ' data-date="' + date.format('YYYY-MM-DD') + '"' + // if date has a time, won't format it
4984
+ (otherAttrs ?
4985
+ ' ' + otherAttrs :
4986
+ '') +
4911
4987
  '></td>';
4912
4988
  },
4913
4989
 
@@ -4921,6 +4997,11 @@ var DayTableMixin = FC.DayTableMixin = {
4921
4997
  },
4922
4998
 
4923
4999
 
5000
+ // TODO: a generic method for dealing with <tr>, RTL, intro
5001
+ // when increment internalApiVersion
5002
+ // wrapTr (scheduler)
5003
+
5004
+
4924
5005
  /* Utils
4925
5006
  ------------------------------------------------------------------------------------------------------------------*/
4926
5007
 
@@ -6073,13 +6154,11 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
6073
6154
 
6074
6155
  colEls: null, // cells elements in the day-row background
6075
6156
  slatEls: null, // elements running horizontally across all columns
6076
- helperEl: null, // cell skeleton element for rendering the mock event "helper"
6157
+ nowIndicatorEls: null,
6077
6158
 
6078
6159
  colCoordCache: null,
6079
6160
  slatCoordCache: null,
6080
6161
 
6081
- businessHourSegs: null,
6082
-
6083
6162
 
6084
6163
  constructor: function() {
6085
6164
  Grid.apply(this, arguments); // call the super-constructor
@@ -6103,12 +6182,8 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
6103
6182
  els: this.slatEls,
6104
6183
  isVertical: true
6105
6184
  });
6106
- },
6107
6185
 
6108
-
6109
- renderBusinessHours: function() {
6110
- var events = this.view.calendar.getBusinessHoursEvents();
6111
- this.businessHourSegs = this.renderFill('businessHours', this.eventsToSegs(events), 'bgevent');
6186
+ this.renderContentSkeleton();
6112
6187
  },
6113
6188
 
6114
6189
 
@@ -6154,7 +6229,9 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
6154
6229
  '</td>';
6155
6230
 
6156
6231
  html +=
6157
- '<tr ' + (isLabeled ? '' : 'class="fc-minor"') + '>' +
6232
+ '<tr data-time="' + slotDate.format('HH:mm:ss') + '"' +
6233
+ (isLabeled ? '' : ' class="fc-minor"') +
6234
+ '>' +
6158
6235
  (!isRTL ? axisHtml : '') +
6159
6236
  '<td class="' + view.widgetContentClass + '"/>' +
6160
6237
  (isRTL ? axisHtml : '') +
@@ -6367,7 +6444,9 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
6367
6444
  this.slatCoordCache.build();
6368
6445
 
6369
6446
  if (isResize) {
6370
- this.updateSegVerticals();
6447
+ this.updateSegVerticals(
6448
+ [].concat(this.fgSegs || [], this.bgSegs || [], this.businessSegs || [])
6449
+ );
6371
6450
  }
6372
6451
  },
6373
6452
 
@@ -6421,7 +6500,10 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
6421
6500
 
6422
6501
  if (seg) { // if there is event information for this drag, render a helper event
6423
6502
  this.renderEventLocationHelper(eventLocation, seg);
6424
- this.applyDragOpacity(this.helperEl);
6503
+
6504
+ for (var i = 0; i < this.helperSegs.length; i++) {
6505
+ this.applyDragOpacity(this.helperSegs[i].el);
6506
+ }
6425
6507
 
6426
6508
  return true; // signal that a helper has been rendered
6427
6509
  }
@@ -6461,39 +6543,72 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
6461
6543
 
6462
6544
  // Renders a mock "helper" event. `sourceSeg` is the original segment object and might be null (an external drag)
6463
6545
  renderHelper: function(event, sourceSeg) {
6464
- var segs = this.eventToSegs(event);
6465
- var tableEl;
6466
- var i, seg;
6467
- var sourceEl;
6546
+ this.renderHelperSegs(this.eventToSegs(event), sourceSeg);
6547
+ },
6468
6548
 
6469
- segs = this.renderFgSegEls(segs); // assigns each seg's el and returns a subset of segs that were rendered
6470
- tableEl = this.renderSegTable(segs);
6471
6549
 
6472
- // Try to make the segment that is in the same row as sourceSeg look the same
6550
+ // Unrenders any mock helper event
6551
+ unrenderHelper: function() {
6552
+ this.unrenderHelperSegs();
6553
+ },
6554
+
6555
+
6556
+ /* Business Hours
6557
+ ------------------------------------------------------------------------------------------------------------------*/
6558
+
6559
+
6560
+ renderBusinessHours: function() {
6561
+ var events = this.view.calendar.getBusinessHoursEvents();
6562
+ var segs = this.eventsToSegs(events);
6563
+
6564
+ this.renderBusinessSegs(segs);
6565
+ },
6566
+
6567
+
6568
+ unrenderBusinessHours: function() {
6569
+ this.unrenderBusinessSegs();
6570
+ },
6571
+
6572
+
6573
+ /* Now Indicator
6574
+ ------------------------------------------------------------------------------------------------------------------*/
6575
+
6576
+
6577
+ getNowIndicatorUnit: function() {
6578
+ return 'minute'; // will refresh on the minute
6579
+ },
6580
+
6581
+
6582
+ renderNowIndicator: function(date) {
6583
+ // seg system might be overkill, but it handles scenario where line needs to be rendered
6584
+ // more than once because of columns with the same date (resources columns for example)
6585
+ var segs = this.spanToSegs({ start: date, end: date });
6586
+ var top = this.computeDateTop(date, date);
6587
+ var nodes = [];
6588
+ var i;
6589
+
6590
+ // render lines within the columns
6473
6591
  for (i = 0; i < segs.length; i++) {
6474
- seg = segs[i];
6475
- if (sourceSeg && sourceSeg.col === seg.col) {
6476
- sourceEl = sourceSeg.el;
6477
- seg.el.css({
6478
- left: sourceEl.css('left'),
6479
- right: sourceEl.css('right'),
6480
- 'margin-left': sourceEl.css('margin-left'),
6481
- 'margin-right': sourceEl.css('margin-right')
6482
- });
6483
- }
6592
+ nodes.push($('<div class="fc-now-indicator fc-now-indicator-line"></div>')
6593
+ .css('top', top)
6594
+ .appendTo(this.colContainerEls.eq(segs[i].col))[0]);
6595
+ }
6596
+
6597
+ // render an arrow over the axis
6598
+ if (segs.length > 0) { // is the current time in view?
6599
+ nodes.push($('<div class="fc-now-indicator fc-now-indicator-arrow"></div>')
6600
+ .css('top', top)
6601
+ .appendTo(this.el.find('.fc-content-skeleton'))[0]);
6484
6602
  }
6485
6603
 
6486
- this.helperEl = $('<div class="fc-helper-skeleton"/>')
6487
- .append(tableEl)
6488
- .appendTo(this.el);
6604
+ this.nowIndicatorEls = $(nodes);
6489
6605
  },
6490
6606
 
6491
6607
 
6492
- // Unrenders any mock helper event
6493
- unrenderHelper: function() {
6494
- if (this.helperEl) {
6495
- this.helperEl.remove();
6496
- this.helperEl = null;
6608
+ unrenderNowIndicator: function() {
6609
+ if (this.nowIndicatorEls) {
6610
+ this.nowIndicatorEls.remove();
6611
+ this.nowIndicatorEls = null;
6497
6612
  }
6498
6613
  },
6499
6614
 
@@ -6522,143 +6637,398 @@ var TimeGrid = FC.TimeGrid = Grid.extend(DayTableMixin, {
6522
6637
  },
6523
6638
 
6524
6639
 
6525
- /* Fill System (highlight, background events, business hours)
6640
+ /* Highlight
6526
6641
  ------------------------------------------------------------------------------------------------------------------*/
6527
6642
 
6528
6643
 
6529
- // Renders a set of rectangles over the given time segments.
6530
- // Only returns segments that successfully rendered.
6531
- renderFill: function(type, segs, className) {
6532
- var segCols;
6644
+ renderHighlight: function(span) {
6645
+ this.renderHighlightSegs(this.spanToSegs(span));
6646
+ },
6647
+
6648
+
6649
+ unrenderHighlight: function() {
6650
+ this.unrenderHighlightSegs();
6651
+ }
6652
+
6653
+ });
6654
+
6655
+ ;;
6656
+
6657
+ /* Methods for rendering SEGMENTS, pieces of content that live on the view
6658
+ ( this file is no longer just for events )
6659
+ ----------------------------------------------------------------------------------------------------------------------*/
6660
+
6661
+ TimeGrid.mixin({
6662
+
6663
+ colContainerEls: null, // containers for each column
6664
+
6665
+ // inner-containers for each column where different types of segs live
6666
+ fgContainerEls: null,
6667
+ bgContainerEls: null,
6668
+ helperContainerEls: null,
6669
+ highlightContainerEls: null,
6670
+ businessContainerEls: null,
6671
+
6672
+ // arrays of different types of displayed segments
6673
+ fgSegs: null,
6674
+ bgSegs: null,
6675
+ helperSegs: null,
6676
+ highlightSegs: null,
6677
+ businessSegs: null,
6678
+
6679
+
6680
+ // Renders the DOM that the view's content will live in
6681
+ renderContentSkeleton: function() {
6682
+ var cellHtml = '';
6683
+ var i;
6533
6684
  var skeletonEl;
6534
- var trEl;
6535
- var col, colSegs;
6536
- var tdEl;
6537
- var containerEl;
6538
- var dayDate;
6539
- var i, seg;
6540
6685
 
6541
- if (segs.length) {
6686
+ for (i = 0; i < this.colCnt; i++) {
6687
+ cellHtml +=
6688
+ '<td>' +
6689
+ '<div class="fc-content-col">' +
6690
+ '<div class="fc-event-container fc-helper-container"></div>' +
6691
+ '<div class="fc-event-container"></div>' +
6692
+ '<div class="fc-highlight-container"></div>' +
6693
+ '<div class="fc-bgevent-container"></div>' +
6694
+ '<div class="fc-business-container"></div>' +
6695
+ '</div>' +
6696
+ '</td>';
6697
+ }
6542
6698
 
6543
- segs = this.renderFillSegEls(type, segs); // assignes `.el` to each seg. returns successfully rendered segs
6544
- segCols = this.groupSegCols(segs); // group into sub-arrays, and assigns 'col' to each seg
6699
+ skeletonEl = $(
6700
+ '<div class="fc-content-skeleton">' +
6701
+ '<table>' +
6702
+ '<tr>' + cellHtml + '</tr>' +
6703
+ '</table>' +
6704
+ '</div>'
6705
+ );
6545
6706
 
6546
- className = className || type.toLowerCase();
6547
- skeletonEl = $(
6548
- '<div class="fc-' + className + '-skeleton">' +
6549
- '<table><tr/></table>' +
6550
- '</div>'
6551
- );
6552
- trEl = skeletonEl.find('tr');
6553
-
6554
- for (col = 0; col < segCols.length; col++) {
6555
- colSegs = segCols[col];
6556
- tdEl = $('<td/>').appendTo(trEl);
6557
-
6558
- if (colSegs.length) {
6559
- containerEl = $('<div class="fc-' + className + '-container"/>').appendTo(tdEl);
6560
- dayDate = this.getCellDate(0, col); // row=0
6561
-
6562
- for (i = 0; i < colSegs.length; i++) {
6563
- seg = colSegs[i];
6564
- containerEl.append(
6565
- seg.el.css({
6566
- top: this.computeDateTop(seg.start, dayDate),
6567
- bottom: -this.computeDateTop(seg.end, dayDate) // the y position of the bottom edge
6568
- })
6569
- );
6570
- }
6571
- }
6707
+ this.colContainerEls = skeletonEl.find('.fc-content-col');
6708
+ this.helperContainerEls = skeletonEl.find('.fc-helper-container');
6709
+ this.fgContainerEls = skeletonEl.find('.fc-event-container:not(.fc-helper-container)');
6710
+ this.bgContainerEls = skeletonEl.find('.fc-bgevent-container');
6711
+ this.highlightContainerEls = skeletonEl.find('.fc-highlight-container');
6712
+ this.businessContainerEls = skeletonEl.find('.fc-business-container');
6713
+
6714
+ this.bookendCells(skeletonEl.find('tr')); // TODO: do this on string level
6715
+ this.el.append(skeletonEl);
6716
+ },
6717
+
6718
+
6719
+ /* Foreground Events
6720
+ ------------------------------------------------------------------------------------------------------------------*/
6721
+
6722
+
6723
+ renderFgSegs: function(segs) {
6724
+ segs = this.renderFgSegsIntoContainers(segs, this.fgContainerEls);
6725
+ this.fgSegs = segs;
6726
+ return segs; // needed for Grid::renderEvents
6727
+ },
6728
+
6729
+
6730
+ unrenderFgSegs: function() {
6731
+ this.unrenderNamedSegs('fgSegs');
6732
+ },
6733
+
6734
+
6735
+ /* Foreground Helper Events
6736
+ ------------------------------------------------------------------------------------------------------------------*/
6737
+
6738
+
6739
+ renderHelperSegs: function(segs, sourceSeg) {
6740
+ var i, seg;
6741
+ var sourceEl;
6742
+
6743
+ segs = this.renderFgSegsIntoContainers(segs, this.helperContainerEls);
6744
+
6745
+ // Try to make the segment that is in the same row as sourceSeg look the same
6746
+ for (i = 0; i < segs.length; i++) {
6747
+ seg = segs[i];
6748
+ if (sourceSeg && sourceSeg.col === seg.col) {
6749
+ sourceEl = sourceSeg.el;
6750
+ seg.el.css({
6751
+ left: sourceEl.css('left'),
6752
+ right: sourceEl.css('right'),
6753
+ 'margin-left': sourceEl.css('margin-left'),
6754
+ 'margin-right': sourceEl.css('margin-right')
6755
+ });
6572
6756
  }
6757
+ }
6758
+
6759
+ this.helperSegs = segs;
6760
+ },
6573
6761
 
6574
- this.bookendCells(trEl);
6575
6762
 
6576
- this.el.append(skeletonEl);
6577
- this.elsByFill[type] = skeletonEl;
6763
+ unrenderHelperSegs: function() {
6764
+ this.unrenderNamedSegs('helperSegs');
6765
+ },
6766
+
6767
+
6768
+ /* Background Events
6769
+ ------------------------------------------------------------------------------------------------------------------*/
6770
+
6771
+
6772
+ renderBgSegs: function(segs) {
6773
+ segs = this.renderFillSegEls('bgEvent', segs); // TODO: old fill system
6774
+ this.updateSegVerticals(segs);
6775
+ this.attachSegsByCol(this.groupSegsByCol(segs), this.bgContainerEls);
6776
+ this.bgSegs = segs;
6777
+ return segs; // needed for Grid::renderEvents
6778
+ },
6779
+
6780
+
6781
+ unrenderBgSegs: function() {
6782
+ this.unrenderNamedSegs('bgSegs');
6783
+ },
6784
+
6785
+
6786
+ /* Highlight
6787
+ ------------------------------------------------------------------------------------------------------------------*/
6788
+
6789
+
6790
+ renderHighlightSegs: function(segs) {
6791
+ segs = this.renderFillSegEls('highlight', segs); // TODO: old fill system
6792
+ this.updateSegVerticals(segs);
6793
+ this.attachSegsByCol(this.groupSegsByCol(segs), this.highlightContainerEls);
6794
+ this.highlightSegs = segs;
6795
+ },
6796
+
6797
+
6798
+ unrenderHighlightSegs: function() {
6799
+ this.unrenderNamedSegs('highlightSegs');
6800
+ },
6801
+
6802
+
6803
+ /* Business Hours
6804
+ ------------------------------------------------------------------------------------------------------------------*/
6805
+
6806
+
6807
+ renderBusinessSegs: function(segs) {
6808
+ segs = this.renderFillSegEls('businessHours', segs); // TODO: old fill system
6809
+ this.updateSegVerticals(segs);
6810
+ this.attachSegsByCol(this.groupSegsByCol(segs), this.businessContainerEls);
6811
+ this.businessSegs = segs;
6812
+ },
6813
+
6814
+
6815
+ unrenderBusinessSegs: function() {
6816
+ this.unrenderNamedSegs('businessSegs');
6817
+ },
6818
+
6819
+
6820
+ /* Seg Rendering Utils
6821
+ ------------------------------------------------------------------------------------------------------------------*/
6822
+
6823
+
6824
+ // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's col
6825
+ groupSegsByCol: function(segs) {
6826
+ var segsByCol = [];
6827
+ var i;
6828
+
6829
+ for (i = 0; i < this.colCnt; i++) {
6830
+ segsByCol.push([]);
6578
6831
  }
6579
6832
 
6580
- return segs;
6581
- }
6833
+ for (i = 0; i < segs.length; i++) {
6834
+ segsByCol[segs[i].col].push(segs[i]);
6835
+ }
6582
6836
 
6583
- });
6837
+ return segsByCol;
6838
+ },
6584
6839
 
6585
- ;;
6586
6840
 
6587
- /* Event-rendering methods for the TimeGrid class
6588
- ----------------------------------------------------------------------------------------------------------------------*/
6841
+ // Given segments grouped by column, insert the segments' elements into a parallel array of container
6842
+ // elements, each living within a column.
6843
+ attachSegsByCol: function(segsByCol, containerEls) {
6844
+ var col;
6845
+ var segs;
6846
+ var i;
6589
6847
 
6590
- TimeGrid.mixin({
6848
+ for (col = 0; col < this.colCnt; col++) { // iterate each column grouping
6849
+ segs = segsByCol[col];
6591
6850
 
6592
- eventSkeletonEl: null, // has cells with event-containers, which contain absolutely positioned event elements
6851
+ for (i = 0; i < segs.length; i++) {
6852
+ containerEls.eq(col).append(segs[i].el);
6853
+ }
6854
+ }
6855
+ },
6593
6856
 
6594
6857
 
6595
- // Renders the given foreground event segments onto the grid
6596
- renderFgSegs: function(segs) {
6597
- segs = this.renderFgSegEls(segs); // returns a subset of the segs. segs that were actually rendered
6858
+ // Given the name of a property of `this` object, assumed to be an array of segments,
6859
+ // loops through each segment and removes from DOM. Will null-out the property afterwards.
6860
+ unrenderNamedSegs: function(propName) {
6861
+ var segs = this[propName];
6862
+ var i;
6598
6863
 
6599
- this.el.append(
6600
- this.eventSkeletonEl = $('<div class="fc-content-skeleton"/>')
6601
- .append(this.renderSegTable(segs))
6602
- );
6864
+ if (segs) {
6865
+ for (i = 0; i < segs.length; i++) {
6866
+ segs[i].el.remove();
6867
+ }
6868
+ this[propName] = null;
6869
+ }
6870
+ },
6603
6871
 
6604
- return segs; // return only the segs that were actually rendered
6872
+
6873
+
6874
+ /* Foreground Event Rendering Utils
6875
+ ------------------------------------------------------------------------------------------------------------------*/
6876
+
6877
+
6878
+ // Given an array of foreground segments, render a DOM element for each, computes position,
6879
+ // and attaches to the column inner-container elements.
6880
+ renderFgSegsIntoContainers: function(segs, containerEls) {
6881
+ var segsByCol;
6882
+ var col;
6883
+
6884
+ segs = this.renderFgSegEls(segs); // will call fgSegHtml
6885
+ segsByCol = this.groupSegsByCol(segs);
6886
+
6887
+ for (col = 0; col < this.colCnt; col++) {
6888
+ this.updateFgSegCoords(segsByCol[col]);
6889
+ }
6890
+
6891
+ this.attachSegsByCol(segsByCol, containerEls);
6892
+
6893
+ return segs;
6605
6894
  },
6606
6895
 
6607
6896
 
6608
- // Unrenders all currently rendered foreground event segments
6609
- unrenderFgSegs: function(segs) {
6610
- if (this.eventSkeletonEl) {
6611
- this.eventSkeletonEl.remove();
6612
- this.eventSkeletonEl = null;
6897
+ // Renders the HTML for a single event segment's default rendering
6898
+ fgSegHtml: function(seg, disableResizing) {
6899
+ var view = this.view;
6900
+ var event = seg.event;
6901
+ var isDraggable = view.isEventDraggable(event);
6902
+ var isResizableFromStart = !disableResizing && seg.isStart && view.isEventResizableFromStart(event);
6903
+ var isResizableFromEnd = !disableResizing && seg.isEnd && view.isEventResizableFromEnd(event);
6904
+ var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd);
6905
+ var skinCss = cssToStr(this.getEventSkinCss(event));
6906
+ var timeText;
6907
+ var fullTimeText; // more verbose time text. for the print stylesheet
6908
+ var startTimeText; // just the start time text
6909
+
6910
+ classes.unshift('fc-time-grid-event', 'fc-v-event');
6911
+
6912
+ if (view.isMultiDayEvent(event)) { // if the event appears to span more than one day...
6913
+ // Don't display time text on segments that run entirely through a day.
6914
+ // That would appear as midnight-midnight and would look dumb.
6915
+ // Otherwise, display the time text for the *segment's* times (like 6pm-midnight or midnight-10am)
6916
+ if (seg.isStart || seg.isEnd) {
6917
+ timeText = this.getEventTimeText(seg);
6918
+ fullTimeText = this.getEventTimeText(seg, 'LT');
6919
+ startTimeText = this.getEventTimeText(seg, null, false); // displayEnd=false
6920
+ }
6921
+ } else {
6922
+ // Display the normal time text for the *event's* times
6923
+ timeText = this.getEventTimeText(event);
6924
+ fullTimeText = this.getEventTimeText(event, 'LT');
6925
+ startTimeText = this.getEventTimeText(event, null, false); // displayEnd=false
6613
6926
  }
6927
+
6928
+ return '<a class="' + classes.join(' ') + '"' +
6929
+ (event.url ?
6930
+ ' href="' + htmlEscape(event.url) + '"' :
6931
+ ''
6932
+ ) +
6933
+ (skinCss ?
6934
+ ' style="' + skinCss + '"' :
6935
+ ''
6936
+ ) +
6937
+ '>' +
6938
+ '<div class="fc-content">' +
6939
+ (timeText ?
6940
+ '<div class="fc-time"' +
6941
+ ' data-start="' + htmlEscape(startTimeText) + '"' +
6942
+ ' data-full="' + htmlEscape(fullTimeText) + '"' +
6943
+ '>' +
6944
+ '<span>' + htmlEscape(timeText) + '</span>' +
6945
+ '</div>' :
6946
+ ''
6947
+ ) +
6948
+ (event.title ?
6949
+ '<div class="fc-title">' +
6950
+ htmlEscape(event.title) +
6951
+ '</div>' :
6952
+ ''
6953
+ ) +
6954
+ '</div>' +
6955
+ '<div class="fc-bg"/>' +
6956
+ /* TODO: write CSS for this
6957
+ (isResizableFromStart ?
6958
+ '<div class="fc-resizer fc-start-resizer" />' :
6959
+ ''
6960
+ ) +
6961
+ */
6962
+ (isResizableFromEnd ?
6963
+ '<div class="fc-resizer fc-end-resizer" />' :
6964
+ ''
6965
+ ) +
6966
+ '</a>';
6967
+ },
6968
+
6969
+
6970
+ /* Seg Position Utils
6971
+ ------------------------------------------------------------------------------------------------------------------*/
6972
+
6973
+
6974
+ // Refreshes the CSS top/bottom coordinates for each segment element.
6975
+ // Works when called after initial render, after a window resize/zoom for example.
6976
+ updateSegVerticals: function(segs) {
6977
+ this.computeSegVerticals(segs);
6978
+ this.assignSegVerticals(segs);
6614
6979
  },
6615
6980
 
6616
6981
 
6617
- // Renders and returns the <table> portion of the event-skeleton.
6618
- // Returns an object with properties 'tbodyEl' and 'segs'.
6619
- renderSegTable: function(segs) {
6620
- var tableEl = $('<table><tr/></table>');
6621
- var trEl = tableEl.find('tr');
6622
- var segCols;
6982
+ // For each segment in an array, computes and assigns its top and bottom properties
6983
+ computeSegVerticals: function(segs) {
6623
6984
  var i, seg;
6624
- var col, colSegs;
6625
- var containerEl;
6626
6985
 
6627
- segCols = this.groupSegCols(segs); // group into sub-arrays, and assigns 'col' to each seg
6986
+ for (i = 0; i < segs.length; i++) {
6987
+ seg = segs[i];
6988
+ seg.top = this.computeDateTop(seg.start, seg.start);
6989
+ seg.bottom = this.computeDateTop(seg.end, seg.start);
6990
+ }
6991
+ },
6628
6992
 
6629
- this.computeSegVerticals(segs); // compute and assign top/bottom
6630
6993
 
6631
- for (col = 0; col < segCols.length; col++) { // iterate each column grouping
6632
- colSegs = segCols[col];
6633
- this.placeSlotSegs(colSegs); // compute horizontal coordinates, z-index's, and reorder the array
6994
+ // Given segments that already have their top/bottom properties computed, applies those values to
6995
+ // the segments' elements.
6996
+ assignSegVerticals: function(segs) {
6997
+ var i, seg;
6634
6998
 
6635
- containerEl = $('<div class="fc-event-container"/>');
6999
+ for (i = 0; i < segs.length; i++) {
7000
+ seg = segs[i];
7001
+ seg.el.css(this.generateSegVerticalCss(seg));
7002
+ }
7003
+ },
6636
7004
 
6637
- // assign positioning CSS and insert into container
6638
- for (i = 0; i < colSegs.length; i++) {
6639
- seg = colSegs[i];
6640
- seg.el.css(this.generateSegPositionCss(seg));
6641
7005
 
6642
- // if the height is short, add a className for alternate styling
6643
- if (seg.bottom - seg.top < 30) {
6644
- seg.el.addClass('fc-short');
6645
- }
7006
+ // Generates an object with CSS properties for the top/bottom coordinates of a segment element
7007
+ generateSegVerticalCss: function(seg) {
7008
+ return {
7009
+ top: seg.top,
7010
+ bottom: -seg.bottom // flipped because needs to be space beyond bottom edge of event container
7011
+ };
7012
+ },
6646
7013
 
6647
- containerEl.append(seg.el);
6648
- }
6649
7014
 
6650
- trEl.append($('<td/>').append(containerEl));
6651
- }
7015
+ /* Foreground Event Positioning Utils
7016
+ ------------------------------------------------------------------------------------------------------------------*/
6652
7017
 
6653
- this.bookendCells(trEl);
6654
7018
 
6655
- return tableEl;
7019
+ // Given segments that are assumed to all live in the *same column*,
7020
+ // compute their verical/horizontal coordinates and assign to their elements.
7021
+ updateFgSegCoords: function(segs) {
7022
+ this.computeSegVerticals(segs); // horizontals relies on this
7023
+ this.computeFgSegHorizontals(segs); // compute horizontal coordinates, z-index's, and reorder the array
7024
+ this.assignSegVerticals(segs);
7025
+ this.assignFgSegHorizontals(segs);
6656
7026
  },
6657
7027
 
6658
7028
 
6659
7029
  // Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each.
6660
7030
  // NOTE: Also reorders the given array by date!
6661
- placeSlotSegs: function(segs) {
7031
+ computeFgSegHorizontals: function(segs) {
6662
7032
  var levels;
6663
7033
  var level0;
6664
7034
  var i;
@@ -6674,7 +7044,7 @@ TimeGrid.mixin({
6674
7044
  }
6675
7045
 
6676
7046
  for (i = 0; i < level0.length; i++) {
6677
- this.computeSlotSegCoords(level0[i], 0, 0);
7047
+ this.computeFgSegForwardBack(level0[i], 0, 0);
6678
7048
  }
6679
7049
  }
6680
7050
  },
@@ -6688,7 +7058,7 @@ TimeGrid.mixin({
6688
7058
  // who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of
6689
7059
  // segments behind this one in the current series, and `seriesBackwardCoord` is the starting
6690
7060
  // coordinate of the first segment in the series.
6691
- computeSlotSegCoords: function(seg, seriesBackwardPressure, seriesBackwardCoord) {
7061
+ computeFgSegForwardBack: function(seg, seriesBackwardPressure, seriesBackwardCoord) {
6692
7062
  var forwardSegs = seg.forwardSegs;
6693
7063
  var i;
6694
7064
 
@@ -6702,11 +7072,11 @@ TimeGrid.mixin({
6702
7072
  else {
6703
7073
 
6704
7074
  // sort highest pressure first
6705
- this.sortForwardSlotSegs(forwardSegs);
7075
+ this.sortForwardSegs(forwardSegs);
6706
7076
 
6707
7077
  // this segment's forwardCoord will be calculated from the backwardCoord of the
6708
7078
  // highest-pressure forward segment.
6709
- this.computeSlotSegCoords(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
7079
+ this.computeFgSegForwardBack(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
6710
7080
  seg.forwardCoord = forwardSegs[0].backwardCoord;
6711
7081
  }
6712
7082
 
@@ -6718,116 +7088,48 @@ TimeGrid.mixin({
6718
7088
  // use this segment's coordinates to computed the coordinates of the less-pressurized
6719
7089
  // forward segments
6720
7090
  for (i=0; i<forwardSegs.length; i++) {
6721
- this.computeSlotSegCoords(forwardSegs[i], 0, seg.forwardCoord);
7091
+ this.computeFgSegForwardBack(forwardSegs[i], 0, seg.forwardCoord);
6722
7092
  }
6723
7093
  }
6724
7094
  },
6725
7095
 
6726
7096
 
6727
- // Refreshes the CSS top/bottom coordinates for each segment element. Probably after a window resize/zoom.
6728
- // Repositions business hours segs too, so not just for events. Maybe shouldn't be here.
6729
- updateSegVerticals: function() {
6730
- var allSegs = (this.segs || []).concat(this.businessHourSegs || []);
6731
- var i;
7097
+ sortForwardSegs: function(forwardSegs) {
7098
+ forwardSegs.sort(proxy(this, 'compareForwardSegs'));
7099
+ },
6732
7100
 
6733
- this.computeSegVerticals(allSegs);
6734
7101
 
6735
- for (i = 0; i < allSegs.length; i++) {
6736
- allSegs[i].el.css(
6737
- this.generateSegVerticalCss(allSegs[i])
6738
- );
6739
- }
7102
+ // A cmp function for determining which forward segment to rely on more when computing coordinates.
7103
+ compareForwardSegs: function(seg1, seg2) {
7104
+ // put higher-pressure first
7105
+ return seg2.forwardPressure - seg1.forwardPressure ||
7106
+ // put segments that are closer to initial edge first (and favor ones with no coords yet)
7107
+ (seg1.backwardCoord || 0) - (seg2.backwardCoord || 0) ||
7108
+ // do normal sorting...
7109
+ this.compareEventSegs(seg1, seg2);
6740
7110
  },
6741
7111
 
6742
7112
 
6743
- // For each segment in an array, computes and assigns its top and bottom properties
6744
- computeSegVerticals: function(segs) {
7113
+ // Given foreground event segments that have already had their position coordinates computed,
7114
+ // assigns position-related CSS values to their elements.
7115
+ assignFgSegHorizontals: function(segs) {
6745
7116
  var i, seg;
6746
7117
 
6747
7118
  for (i = 0; i < segs.length; i++) {
6748
7119
  seg = segs[i];
6749
- seg.top = this.computeDateTop(seg.start, seg.start);
6750
- seg.bottom = this.computeDateTop(seg.end, seg.start);
6751
- }
6752
- },
6753
-
6754
-
6755
- // Renders the HTML for a single event segment's default rendering
6756
- fgSegHtml: function(seg, disableResizing) {
6757
- var view = this.view;
6758
- var event = seg.event;
6759
- var isDraggable = view.isEventDraggable(event);
6760
- var isResizableFromStart = !disableResizing && seg.isStart && view.isEventResizableFromStart(event);
6761
- var isResizableFromEnd = !disableResizing && seg.isEnd && view.isEventResizableFromEnd(event);
6762
- var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd);
6763
- var skinCss = cssToStr(this.getEventSkinCss(event));
6764
- var timeText;
6765
- var fullTimeText; // more verbose time text. for the print stylesheet
6766
- var startTimeText; // just the start time text
7120
+ seg.el.css(this.generateFgSegHorizontalCss(seg));
6767
7121
 
6768
- classes.unshift('fc-time-grid-event', 'fc-v-event');
6769
-
6770
- if (view.isMultiDayEvent(event)) { // if the event appears to span more than one day...
6771
- // Don't display time text on segments that run entirely through a day.
6772
- // That would appear as midnight-midnight and would look dumb.
6773
- // Otherwise, display the time text for the *segment's* times (like 6pm-midnight or midnight-10am)
6774
- if (seg.isStart || seg.isEnd) {
6775
- timeText = this.getEventTimeText(seg);
6776
- fullTimeText = this.getEventTimeText(seg, 'LT');
6777
- startTimeText = this.getEventTimeText(seg, null, false); // displayEnd=false
7122
+ // if the height is short, add a className for alternate styling
7123
+ if (seg.bottom - seg.top < 30) {
7124
+ seg.el.addClass('fc-short');
6778
7125
  }
6779
- } else {
6780
- // Display the normal time text for the *event's* times
6781
- timeText = this.getEventTimeText(event);
6782
- fullTimeText = this.getEventTimeText(event, 'LT');
6783
- startTimeText = this.getEventTimeText(event, null, false); // displayEnd=false
6784
7126
  }
6785
-
6786
- return '<a class="' + classes.join(' ') + '"' +
6787
- (event.url ?
6788
- ' href="' + htmlEscape(event.url) + '"' :
6789
- ''
6790
- ) +
6791
- (skinCss ?
6792
- ' style="' + skinCss + '"' :
6793
- ''
6794
- ) +
6795
- '>' +
6796
- '<div class="fc-content">' +
6797
- (timeText ?
6798
- '<div class="fc-time"' +
6799
- ' data-start="' + htmlEscape(startTimeText) + '"' +
6800
- ' data-full="' + htmlEscape(fullTimeText) + '"' +
6801
- '>' +
6802
- '<span>' + htmlEscape(timeText) + '</span>' +
6803
- '</div>' :
6804
- ''
6805
- ) +
6806
- (event.title ?
6807
- '<div class="fc-title">' +
6808
- htmlEscape(event.title) +
6809
- '</div>' :
6810
- ''
6811
- ) +
6812
- '</div>' +
6813
- '<div class="fc-bg"/>' +
6814
- /* TODO: write CSS for this
6815
- (isResizableFromStart ?
6816
- '<div class="fc-resizer fc-start-resizer" />' :
6817
- ''
6818
- ) +
6819
- */
6820
- (isResizableFromEnd ?
6821
- '<div class="fc-resizer fc-end-resizer" />' :
6822
- ''
6823
- ) +
6824
- '</a>';
6825
7127
  },
6826
7128
 
6827
7129
 
6828
7130
  // Generates an object with CSS properties/values that should be applied to an event segment element.
6829
7131
  // Contains important positioning-related properties that should be applied to any event element, customized or not.
6830
- generateSegPositionCss: function(seg) {
7132
+ generateFgSegHorizontalCss: function(seg) {
6831
7133
  var shouldOverlap = this.view.opt('slotEventOverlap');
6832
7134
  var backwardCoord = seg.backwardCoord; // the left side if LTR. the right side if RTL. floating-point
6833
7135
  var forwardCoord = seg.forwardCoord; // the right side if LTR. the left side if RTL. floating-point
@@ -6859,48 +7161,6 @@ TimeGrid.mixin({
6859
7161
  }
6860
7162
 
6861
7163
  return props;
6862
- },
6863
-
6864
-
6865
- // Generates an object with CSS properties for the top/bottom coordinates of a segment element
6866
- generateSegVerticalCss: function(seg) {
6867
- return {
6868
- top: seg.top,
6869
- bottom: -seg.bottom // flipped because needs to be space beyond bottom edge of event container
6870
- };
6871
- },
6872
-
6873
-
6874
- // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's col
6875
- groupSegCols: function(segs) {
6876
- var segCols = [];
6877
- var i;
6878
-
6879
- for (i = 0; i < this.colCnt; i++) {
6880
- segCols.push([]);
6881
- }
6882
-
6883
- for (i = 0; i < segs.length; i++) {
6884
- segCols[segs[i].col].push(segs[i]);
6885
- }
6886
-
6887
- return segCols;
6888
- },
6889
-
6890
-
6891
- sortForwardSlotSegs: function(forwardSegs) {
6892
- forwardSegs.sort(proxy(this, 'compareForwardSlotSegs'));
6893
- },
6894
-
6895
-
6896
- // A cmp function for determining which forward segment to rely on more when computing coordinates.
6897
- compareForwardSlotSegs: function(seg1, seg2) {
6898
- // put higher-pressure first
6899
- return seg2.forwardPressure - seg1.forwardPressure ||
6900
- // put segments that are closer to initial edge first (and favor ones with no coords yet)
6901
- (seg1.backwardCoord || 0) - (seg2.backwardCoord || 0) ||
6902
- // do normal sorting...
6903
- this.compareEventSegs(seg1, seg2);
6904
7164
  }
6905
7165
 
6906
7166
  });
@@ -7053,6 +7313,10 @@ var View = FC.View = Class.extend({
7053
7313
  // document handlers, bound to `this` object
7054
7314
  documentMousedownProxy: null, // TODO: doesn't work with touch
7055
7315
 
7316
+ // for refresh timing of now indicator
7317
+ nowIndicatorTimeoutID: null,
7318
+ nowIndicatorIntervalID: null,
7319
+
7056
7320
 
7057
7321
  constructor: function(calendar, type, options, intervalDuration) {
7058
7322
 
@@ -7331,7 +7595,7 @@ var View = FC.View = Class.extend({
7331
7595
  this.clearView();
7332
7596
  this.displayView();
7333
7597
  if (wasEventsRendered) { // only render and trigger handlers if events previously rendered
7334
- this.displayEvents();
7598
+ this.displayEvents(this.calendar.getEventCache());
7335
7599
  }
7336
7600
  }
7337
7601
  },
@@ -7354,6 +7618,10 @@ var View = FC.View = Class.extend({
7354
7618
  this.renderDates();
7355
7619
  this.updateSize();
7356
7620
  this.renderBusinessHours(); // might need coordinates, so should go after updateSize()
7621
+
7622
+ if (this.opt('nowIndicator')) {
7623
+ this.startNowIndicator();
7624
+ }
7357
7625
  },
7358
7626
 
7359
7627
 
@@ -7361,6 +7629,7 @@ var View = FC.View = Class.extend({
7361
7629
  // Can be asynchronous and return a promise.
7362
7630
  clearView: function() {
7363
7631
  this.unselect();
7632
+ this.stopNowIndicator();
7364
7633
  this.triggerUnrender();
7365
7634
  this.unrenderBusinessHours();
7366
7635
  this.unrenderDates();
@@ -7395,18 +7664,6 @@ var View = FC.View = Class.extend({
7395
7664
  },
7396
7665
 
7397
7666
 
7398
- // Renders business-hours onto the view. Assumes updateSize has already been called.
7399
- renderBusinessHours: function() {
7400
- // subclasses should implement
7401
- },
7402
-
7403
-
7404
- // Unrenders previously-rendered business-hours
7405
- unrenderBusinessHours: function() {
7406
- // subclasses should implement
7407
- },
7408
-
7409
-
7410
7667
  // Signals that the view's content has been rendered
7411
7668
  triggerRender: function() {
7412
7669
  this.trigger('viewRender', this, this, this.el);
@@ -7441,6 +7698,102 @@ var View = FC.View = Class.extend({
7441
7698
  },
7442
7699
 
7443
7700
 
7701
+ /* Business Hours
7702
+ ------------------------------------------------------------------------------------------------------------------*/
7703
+
7704
+
7705
+ // Renders business-hours onto the view. Assumes updateSize has already been called.
7706
+ renderBusinessHours: function() {
7707
+ // subclasses should implement
7708
+ },
7709
+
7710
+
7711
+ // Unrenders previously-rendered business-hours
7712
+ unrenderBusinessHours: function() {
7713
+ // subclasses should implement
7714
+ },
7715
+
7716
+
7717
+ /* Now Indicator
7718
+ ------------------------------------------------------------------------------------------------------------------*/
7719
+
7720
+
7721
+ // Immediately render the current time indicator and begins re-rendering it at an interval,
7722
+ // which is defined by this.getNowIndicatorUnit().
7723
+ // TODO: somehow do this for the current whole day's background too
7724
+ startNowIndicator: function() {
7725
+ var _this = this;
7726
+ var unit = this.getNowIndicatorUnit();
7727
+ var initialNow; // result first getNow call
7728
+ var initialNowQueried; // ms time of then getNow was called
7729
+ var delay; // ms wait value
7730
+
7731
+ // rerenders the now indicator, computing the new current time from the amount of time that has passed
7732
+ // since the initial getNow call.
7733
+ function update() {
7734
+ _this.unrenderNowIndicator();
7735
+ _this.renderNowIndicator(
7736
+ initialNow.clone().add(new Date() - initialNowQueried) // add ms
7737
+ );
7738
+ }
7739
+
7740
+ if (unit) {
7741
+ initialNow = this.calendar.getNow();
7742
+ initialNowQueried = +new Date();
7743
+ this.renderNowIndicator(initialNow);
7744
+
7745
+ // wait until the beginning of the next interval
7746
+ delay = initialNow.clone().startOf(unit).add(1, unit) - initialNow;
7747
+ this.nowIndicatorTimeoutID = setTimeout(function() {
7748
+ this.nowIndicatorTimeoutID = null;
7749
+ update();
7750
+ delay = +moment.duration(1, unit);
7751
+ delay = Math.max(100, delay); // prevent too frequent
7752
+ this.nowIndicatorIntervalID = setInterval(update, delay); // update every interval
7753
+ }, delay);
7754
+ }
7755
+ },
7756
+
7757
+
7758
+ // Immediately unrenders the view's current time indicator and stops any re-rendering timers.
7759
+ // Won't cause side effects if indicator isn't rendered.
7760
+ stopNowIndicator: function() {
7761
+ var cleared = false;
7762
+
7763
+ if (this.nowIndicatorTimeoutID) {
7764
+ clearTimeout(this.nowIndicatorTimeoutID);
7765
+ cleared = true;
7766
+ }
7767
+ if (this.nowIndicatorIntervalID) {
7768
+ clearTimeout(this.nowIndicatorIntervalID);
7769
+ cleared = true;
7770
+ }
7771
+
7772
+ if (cleared) { // is the indicator currently display?
7773
+ this.unrenderNowIndicator();
7774
+ }
7775
+ },
7776
+
7777
+
7778
+ // Returns a string unit, like 'second' or 'minute' that defined how often the current time indicator
7779
+ // should be refreshed. If something falsy is returned, no time indicator is rendered at all.
7780
+ getNowIndicatorUnit: function() {
7781
+ // subclasses should implement
7782
+ },
7783
+
7784
+
7785
+ // Renders a current time indicator at the given datetime
7786
+ renderNowIndicator: function(date) {
7787
+ // subclasses should implement
7788
+ },
7789
+
7790
+
7791
+ // Undoes the rendering actions from renderNowIndicator
7792
+ unrenderNowIndicator: function() {
7793
+ // subclasses should implement
7794
+ },
7795
+
7796
+
7444
7797
  /* Dimensions
7445
7798
  ------------------------------------------------------------------------------------------------------------------*/
7446
7799
 
@@ -7563,12 +7916,19 @@ var View = FC.View = Class.extend({
7563
7916
 
7564
7917
  // Does everything necessary to clear the view's currently-rendered events
7565
7918
  clearEvents: function() {
7919
+ var scrollState;
7920
+
7566
7921
  if (this.isEventsRendered) {
7922
+
7923
+ // TODO: optimize: if we know this is part of a displayEvents call, don't queryScroll/setScroll
7924
+ scrollState = this.queryScroll();
7925
+
7567
7926
  this.triggerEventUnrender();
7568
7927
  if (this.destroyEvents) {
7569
7928
  this.destroyEvents(); // TODO: deprecate
7570
7929
  }
7571
7930
  this.unrenderEvents();
7931
+ this.setScroll(scrollState);
7572
7932
  this.isEventsRendered = false;
7573
7933
  }
7574
7934
  },
@@ -8942,6 +9302,8 @@ Calendar.defaults = {
8942
9302
 
8943
9303
  //editable: false,
8944
9304
 
9305
+ //nowIndicator: false,
9306
+
8945
9307
  scrollTime: '06:00:00',
8946
9308
 
8947
9309
  // event ajax
@@ -11106,15 +11468,6 @@ var AgendaView = FC.AgendaView = View.extend({
11106
11468
  },
11107
11469
 
11108
11470
 
11109
- renderBusinessHours: function() {
11110
- this.timeGrid.renderBusinessHours();
11111
-
11112
- if (this.dayGrid) {
11113
- this.dayGrid.renderBusinessHours();
11114
- }
11115
- },
11116
-
11117
-
11118
11471
  // Builds the HTML skeleton for the view.
11119
11472
  // The day-grid and time-grid components will render inside containers defined by this HTML.
11120
11473
  renderSkeletonHtml: function() {
@@ -11152,6 +11505,47 @@ var AgendaView = FC.AgendaView = View.extend({
11152
11505
  },
11153
11506
 
11154
11507
 
11508
+ /* Business Hours
11509
+ ------------------------------------------------------------------------------------------------------------------*/
11510
+
11511
+
11512
+ renderBusinessHours: function() {
11513
+ this.timeGrid.renderBusinessHours();
11514
+
11515
+ if (this.dayGrid) {
11516
+ this.dayGrid.renderBusinessHours();
11517
+ }
11518
+ },
11519
+
11520
+
11521
+ unrenderBusinessHours: function() {
11522
+ this.timeGrid.unrenderBusinessHours();
11523
+
11524
+ if (this.dayGrid) {
11525
+ this.dayGrid.unrenderBusinessHours();
11526
+ }
11527
+ },
11528
+
11529
+
11530
+ /* Now Indicator
11531
+ ------------------------------------------------------------------------------------------------------------------*/
11532
+
11533
+
11534
+ getNowIndicatorUnit: function() {
11535
+ return this.timeGrid.getNowIndicatorUnit();
11536
+ },
11537
+
11538
+
11539
+ renderNowIndicator: function(date) {
11540
+ this.timeGrid.renderNowIndicator(date);
11541
+ },
11542
+
11543
+
11544
+ unrenderNowIndicator: function() {
11545
+ this.timeGrid.unrenderNowIndicator();
11546
+ },
11547
+
11548
+
11155
11549
  /* Dimensions
11156
11550
  ------------------------------------------------------------------------------------------------------------------*/
11157
11551
 
@@ -11387,6 +11781,7 @@ var AgendaView = FC.AgendaView = View.extend({
11387
11781
 
11388
11782
 
11389
11783
  // Methods that will customize the rendering behavior of the AgendaView's timeGrid
11784
+ // TODO: move into TimeGrid
11390
11785
  var agendaTimeGridMethods = {
11391
11786
 
11392
11787