fullcalendar.io-rails 2.5.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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