fullcalendar-rails 2.4.0.0 → 2.4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a57803defd76b125976c317bba27759aba1f50f2
4
- data.tar.gz: 73633f0a64546122a4bb8744a6f7916a3925d392
3
+ metadata.gz: 673dd0676cb4b611063a1934897f589144a61959
4
+ data.tar.gz: a31d5bb86c88562ba3c2d4df0ec67ba5033ff656
5
5
  SHA512:
6
- metadata.gz: 53a139267104df7d7ef4ad1dbedfdd87be387d7143e819be12069e1a03931dad8e3d088082dd58f4216cd322c5b951525838dcaf4208ecdd93ec8e6f839669ee
7
- data.tar.gz: 150d95419c543c9a174fa62398defdc95130470447674aba559977870c5bce4c75dcb5c79992c0c28c05fed4f149e02c179b814f5df4d8981edd007be3c0bbde
6
+ metadata.gz: a5ad43a3ddc1113c9e657226456a4167a97a44961887a0eec9697da821c6fd76adb82a082176ba4a129c8dfeee7a793acb9cb6a83849043d841cdb12fdfc2766
7
+ data.tar.gz: 85e378bb55846a7abdab1a883e539ca80c70133d57542cedfd017e0869603cb306e8082d1bce8983c68cd0b09d22663e792dc369c3b25267269e27b5e600a2d7
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
16
16
  gem.version = Fullcalendar::Rails::VERSION
17
17
  gem.license = 'MIT'
18
18
 
19
- gem.add_runtime_dependency 'jquery-rails', '>= 3.1.1', '< 5.0.0'
19
+ gem.add_runtime_dependency 'jquery-rails', '>= 4.0.5', '< 5.0.0'
20
20
  gem.add_runtime_dependency 'momentjs-rails', '>= 2.9.0'
21
21
  gem.add_development_dependency 'rake', '~> 0'
22
22
  end
@@ -1,5 +1,5 @@
1
1
  module Fullcalendar
2
2
  module Rails
3
- VERSION = "2.4.0.0"
3
+ VERSION = "2.4.0.1"
4
4
  end
5
5
  end
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * FullCalendar v2.3.1
2
+ * FullCalendar v2.4.0
3
3
  * Docs & License: http://fullcalendar.io/
4
4
  * (c) 2015 Adam Shaw
5
5
  */
@@ -18,7 +18,7 @@
18
18
 
19
19
  ;;
20
20
 
21
- var fc = $.fullCalendar = { version: "2.3.1" };
21
+ var fc = $.fullCalendar = { version: "2.4.0" };
22
22
  var fcViews = fc.views = {};
23
23
 
24
24
 
@@ -45,7 +45,7 @@ $.fn.fullCalendar = function(options) {
45
45
  }
46
46
  // a new calendar initialization
47
47
  else if (!calendar) { // don't initialize twice
48
- calendar = new fc.CalendarBase(element, options);
48
+ calendar = new Calendar(element, options);
49
49
  element.data('fullCalendar', calendar);
50
50
  calendar.render();
51
51
  }
@@ -63,41 +63,9 @@ var complexOptions = [ // names of options that are objects whose properties sho
63
63
  ];
64
64
 
65
65
 
66
- // Recursively combines all passed-in option-hash arguments into a new single option-hash.
67
- // Given option-hashes are ordered from lowest to highest priority.
68
- function mergeOptions() {
69
- var chain = Array.prototype.slice.call(arguments); // convert to a real array
70
- var complexVals = {}; // hash for each complex option's combined values
71
- var i, name;
72
- var combinedVal;
73
- var j;
74
- var val;
75
-
76
- // for each complex option, loop through each option-hash and accumulate the combined values
77
- for (i = 0; i < complexOptions.length; i++) {
78
- name = complexOptions[i];
79
- combinedVal = null; // an object holding the merge of all the values
80
-
81
- for (j = 0; j < chain.length; j++) {
82
- val = chain[j][name];
83
-
84
- if ($.isPlainObject(val)) {
85
- combinedVal = $.extend(combinedVal || {}, val); // merge new properties
86
- }
87
- else if (val != null) { // a non-null non-undefined atomic option
88
- combinedVal = null; // signal to use the atomic value
89
- }
90
- }
91
-
92
- // if not null, the final value was a combination of other objects. record it
93
- if (combinedVal !== null) {
94
- complexVals[name] = combinedVal;
95
- }
96
- }
97
-
98
- chain.unshift({}); // $.extend will mutate this with the result
99
- chain.push(complexVals); // computed complex values are applied last
100
- return $.extend.apply($, chain); // combine
66
+ // Merges an array of option objects into a single object
67
+ function mergeOptions(optionObjs) {
68
+ return mergeProps(optionObjs, complexOptions);
101
69
  }
102
70
 
103
71
 
@@ -160,6 +128,7 @@ fc.isInt = isInt;
160
128
  fc.htmlEscape = htmlEscape;
161
129
  fc.cssToStr = cssToStr;
162
130
  fc.proxy = proxy;
131
+ fc.capitaliseFirstLetter = capitaliseFirstLetter;
163
132
 
164
133
 
165
134
  /* FullCalendar-specific DOM Utilities
@@ -450,6 +419,7 @@ function isPrimaryMouseButton(ev) {
450
419
  /* Geometry
451
420
  ----------------------------------------------------------------------------------------------------------------------*/
452
421
 
422
+ fc.intersectRects = intersectRects;
453
423
 
454
424
  // Returns a new rectangle that is the intersection of the two rectangles. If they don't intersect, returns false
455
425
  function intersectRects(rect1, rect2) {
@@ -494,6 +464,90 @@ function diffPoints(point1, point2) {
494
464
  }
495
465
 
496
466
 
467
+ /* Object Ordering by Field
468
+ ----------------------------------------------------------------------------------------------------------------------*/
469
+
470
+ fc.parseFieldSpecs = parseFieldSpecs;
471
+ fc.compareByFieldSpecs = compareByFieldSpecs;
472
+ fc.compareByFieldSpec = compareByFieldSpec;
473
+ fc.flexibleCompare = flexibleCompare;
474
+
475
+
476
+ function parseFieldSpecs(input) {
477
+ var specs = [];
478
+ var tokens = [];
479
+ var i, token;
480
+
481
+ if (typeof input === 'string') {
482
+ tokens = input.split(/\s*,\s*/);
483
+ }
484
+ else if (typeof input === 'function') {
485
+ tokens = [ input ];
486
+ }
487
+ else if ($.isArray(input)) {
488
+ tokens = input;
489
+ }
490
+
491
+ for (i = 0; i < tokens.length; i++) {
492
+ token = tokens[i];
493
+
494
+ if (typeof token === 'string') {
495
+ specs.push(
496
+ token.charAt(0) == '-' ?
497
+ { field: token.substring(1), order: -1 } :
498
+ { field: token, order: 1 }
499
+ );
500
+ }
501
+ else if (typeof token === 'function') {
502
+ specs.push({ func: token });
503
+ }
504
+ }
505
+
506
+ return specs;
507
+ }
508
+
509
+
510
+ function compareByFieldSpecs(obj1, obj2, fieldSpecs) {
511
+ var i;
512
+ var cmp;
513
+
514
+ for (i = 0; i < fieldSpecs.length; i++) {
515
+ cmp = compareByFieldSpec(obj1, obj2, fieldSpecs[i]);
516
+ if (cmp) {
517
+ return cmp;
518
+ }
519
+ }
520
+
521
+ return 0;
522
+ }
523
+
524
+
525
+ function compareByFieldSpec(obj1, obj2, fieldSpec) {
526
+ if (fieldSpec.func) {
527
+ return fieldSpec.func(obj1, obj2);
528
+ }
529
+ return flexibleCompare(obj1[fieldSpec.field], obj2[fieldSpec.field]) *
530
+ (fieldSpec.order || 1);
531
+ }
532
+
533
+
534
+ function flexibleCompare(a, b) {
535
+ if (!a && !b) {
536
+ return 0;
537
+ }
538
+ if (b == null) {
539
+ return -1;
540
+ }
541
+ if (a == null) {
542
+ return 1;
543
+ }
544
+ if ($.type(a) === 'string' || $.type(b) === 'string') {
545
+ return String(a).localeCompare(String(b));
546
+ }
547
+ return a - b;
548
+ }
549
+
550
+
497
551
  /* FullCalendar-specific Misc Utilities
498
552
  ----------------------------------------------------------------------------------------------------------------------*/
499
553
 
@@ -543,6 +597,9 @@ function intersectionToSeg(subjectRange, constraintRange) {
543
597
  ----------------------------------------------------------------------------------------------------------------------*/
544
598
 
545
599
  fc.computeIntervalUnit = computeIntervalUnit;
600
+ fc.divideRangeByDuration = divideRangeByDuration;
601
+ fc.divideDurationByDuration = divideDurationByDuration;
602
+ fc.multiplyDuration = multiplyDuration;
546
603
  fc.durationHasTime = durationHasTime;
547
604
 
548
605
  var dayIDs = [ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ];
@@ -614,6 +671,55 @@ function computeRangeAs(unit, start, end) {
614
671
  }
615
672
 
616
673
 
674
+ // Intelligently divides a range (specified by a start/end params) by a duration
675
+ function divideRangeByDuration(start, end, dur) {
676
+ var months;
677
+
678
+ if (durationHasTime(dur)) {
679
+ return (end - start) / dur;
680
+ }
681
+ months = dur.asMonths();
682
+ if (Math.abs(months) >= 1 && isInt(months)) {
683
+ return end.diff(start, 'months', true) / months;
684
+ }
685
+ return end.diff(start, 'days', true) / dur.asDays();
686
+ }
687
+
688
+
689
+ // Intelligently divides one duration by another
690
+ function divideDurationByDuration(dur1, dur2) {
691
+ var months1, months2;
692
+
693
+ if (durationHasTime(dur1) || durationHasTime(dur2)) {
694
+ return dur1 / dur2;
695
+ }
696
+ months1 = dur1.asMonths();
697
+ months2 = dur2.asMonths();
698
+ if (
699
+ Math.abs(months1) >= 1 && isInt(months1) &&
700
+ Math.abs(months2) >= 1 && isInt(months2)
701
+ ) {
702
+ return months1 / months2;
703
+ }
704
+ return dur1.asDays() / dur2.asDays();
705
+ }
706
+
707
+
708
+ // Intelligently multiplies a duration by a number
709
+ function multiplyDuration(dur, n) {
710
+ var months;
711
+
712
+ if (durationHasTime(dur)) {
713
+ return moment.duration(dur * n);
714
+ }
715
+ months = dur.asMonths();
716
+ if (Math.abs(months) >= 1 && isInt(months)) {
717
+ return moment.duration({ months: months * n });
718
+ }
719
+ return moment.duration({ days: dur.asDays() * n });
720
+ }
721
+
722
+
617
723
  // Returns a boolean about whether the given duration has any time parts (hours/minutes/seconds/ms)
618
724
  function durationHasTime(dur) {
619
725
  return Boolean(dur.hours() || dur.minutes() || dur.seconds() || dur.milliseconds());
@@ -631,12 +737,84 @@ function isTimeString(str) {
631
737
  }
632
738
 
633
739
 
740
+ /* Logging and Debug
741
+ ----------------------------------------------------------------------------------------------------------------------*/
742
+
743
+ fc.log = function() {
744
+ var console = window.console;
745
+
746
+ if (console && console.log) {
747
+ return console.log.apply(console, arguments);
748
+ }
749
+ };
750
+
751
+ fc.warn = function() {
752
+ var console = window.console;
753
+
754
+ if (console && console.warn) {
755
+ return console.warn.apply(console, arguments);
756
+ }
757
+ else {
758
+ return fc.log.apply(fc, arguments);
759
+ }
760
+ };
761
+
762
+
634
763
  /* General Utilities
635
764
  ----------------------------------------------------------------------------------------------------------------------*/
636
765
 
637
766
  var hasOwnPropMethod = {}.hasOwnProperty;
638
767
 
639
768
 
769
+ // Merges an array of objects into a single object.
770
+ // The second argument allows for an array of property names who's object values will be merged together.
771
+ function mergeProps(propObjs, complexProps) {
772
+ var dest = {};
773
+ var i, name;
774
+ var complexObjs;
775
+ var j, val;
776
+ var props;
777
+
778
+ if (complexProps) {
779
+ for (i = 0; i < complexProps.length; i++) {
780
+ name = complexProps[i];
781
+ complexObjs = [];
782
+
783
+ // collect the trailing object values, stopping when a non-object is discovered
784
+ for (j = propObjs.length - 1; j >= 0; j--) {
785
+ val = propObjs[j][name];
786
+
787
+ if (typeof val === 'object') {
788
+ complexObjs.unshift(val);
789
+ }
790
+ else if (val !== undefined) {
791
+ dest[name] = val; // if there were no objects, this value will be used
792
+ break;
793
+ }
794
+ }
795
+
796
+ // if the trailing values were objects, use the merged value
797
+ if (complexObjs.length) {
798
+ dest[name] = mergeProps(complexObjs);
799
+ }
800
+ }
801
+ }
802
+
803
+ // copy values into the destination, going from last to first
804
+ for (i = propObjs.length - 1; i >= 0; i--) {
805
+ props = propObjs[i];
806
+
807
+ for (name in props) {
808
+ if (!(name in dest)) { // if already assigned by previous props or complex props, don't reassign
809
+ dest[name] = props[name];
810
+ }
811
+ }
812
+ }
813
+
814
+ return dest;
815
+ }
816
+
817
+
640
818
  // Create an object that has the given prototype. Just like Object.create
641
819
  function createObject(proto) {
642
820
  var f = function() {};
@@ -1570,10 +1748,63 @@ Class.extend = function(members) {
1570
1748
  // adds new member variables/methods to the class's prototype.
1571
1749
  // can be called with another class, or a plain object hash containing new members.
1572
1750
  Class.mixin = function(members) {
1573
- copyOwnProps(members.prototype || members, this.prototype);
1751
+ copyOwnProps(members.prototype || members, this.prototype); // TODO: copyNativeMethods?
1574
1752
  };
1575
1753
  ;;
1576
1754
 
1755
+ var Emitter = fc.Emitter = Class.extend({
1756
+
1757
+ callbackHash: null,
1758
+
1759
+
1760
+ on: function(name, callback) {
1761
+ this.getCallbacks(name).add(callback);
1762
+ return this; // for chaining
1763
+ },
1764
+
1765
+
1766
+ off: function(name, callback) {
1767
+ this.getCallbacks(name).remove(callback);
1768
+ return this; // for chaining
1769
+ },
1770
+
1771
+
1772
+ trigger: function(name) { // args...
1773
+ var args = Array.prototype.slice.call(arguments, 1);
1774
+
1775
+ this.triggerWith(name, this, args);
1776
+
1777
+ return this; // for chaining
1778
+ },
1779
+
1780
+
1781
+ triggerWith: function(name, context, args) {
1782
+ var callbacks = this.getCallbacks(name);
1783
+
1784
+ callbacks.fireWith(context, args);
1785
+
1786
+ return this; // for chaining
1787
+ },
1788
+
1789
+
1790
+ getCallbacks: function(name) {
1791
+ var callbacks;
1792
+
1793
+ if (!this.callbackHash) {
1794
+ this.callbackHash = {};
1795
+ }
1796
+
1797
+ callbacks = this.callbackHash[name];
1798
+ if (!callbacks) {
1799
+ callbacks = this.callbackHash[name] = $.Callbacks();
1800
+ }
1801
+
1802
+ return callbacks;
1803
+ }
1804
+
1805
+ });
1806
+ ;;
1807
+
1577
1808
  /* A rectangular panel that is absolutely positioned over other content
1578
1809
  ------------------------------------------------------------------------------------------------------------------------
1579
1810
  Options:
@@ -1662,7 +1893,7 @@ var Popover = Class.extend({
1662
1893
 
1663
1894
 
1664
1895
  // Hides and unregisters any handlers
1665
- destroy: function() {
1896
+ removeElement: function() {
1666
1897
  this.hide();
1667
1898
 
1668
1899
  if (this.el) {
@@ -1774,6 +2005,7 @@ var GridCoordMap = Class.extend({
1774
2005
 
1775
2006
  // Queries the grid for the coordinates of all the cells
1776
2007
  build: function() {
2008
+ this.grid.build();
1777
2009
  this.rowCoords = this.grid.computeRowCoords();
1778
2010
  this.colCoords = this.grid.computeColCoords();
1779
2011
  this.computeBounds();
@@ -1782,6 +2014,7 @@ var GridCoordMap = Class.extend({
1782
2014
 
1783
2015
  // Clears the coordinates data to free up memory
1784
2016
  clear: function() {
2017
+ this.grid.clear();
1785
2018
  this.rowCoords = null;
1786
2019
  this.colCoords = null;
1787
2020
  },
@@ -2549,7 +2782,7 @@ var MouseFollower = Class.extend({
2549
2782
 
2550
2783
  function complete() {
2551
2784
  this.isAnimating = false;
2552
- _this.destroyEl();
2785
+ _this.removeElement();
2553
2786
 
2554
2787
  this.top0 = this.left0 = null; // reset state for future updatePosition calls
2555
2788
 
@@ -2607,7 +2840,7 @@ var MouseFollower = Class.extend({
2607
2840
 
2608
2841
 
2609
2842
  // Removes the tracking element if it has already been created
2610
- destroyEl: function() {
2843
+ removeElement: function() {
2611
2844
  if (this.el) {
2612
2845
  this.el.remove();
2613
2846
  this.el = null;
@@ -2787,8 +3020,6 @@ var Grid = fc.Grid = RowRenderer.extend({
2787
3020
 
2788
3021
  rowCnt: 0, // number of rows
2789
3022
  colCnt: 0, // number of cols
2790
- rowData: null, // array of objects, holding misc data for each row
2791
- colData: null, // array of objects, holding misc data for each column
2792
3023
 
2793
3024
  el: null, // the containing element
2794
3025
  coordMap: null, // a GridCoordMap that converts pixel values to datetimes
@@ -2854,18 +3085,27 @@ var Grid = fc.Grid = RowRenderer.extend({
2854
3085
  ------------------------------------------------------------------------------------------------------------------*/
2855
3086
 
2856
3087
 
2857
- // Tells the grid about what period of time to display. Grid will subsequently compute dates for cell system.
3088
+ // Tells the grid about what period of time to display.
3089
+ // Any date-related cell system internal data should be generated.
2858
3090
  setRange: function(range) {
2859
- var view = this.view;
2860
- var displayEventTime;
2861
- var displayEventEnd;
2862
-
2863
3091
  this.start = range.start.clone();
2864
3092
  this.end = range.end.clone();
2865
3093
 
2866
- this.rowData = [];
2867
- this.colData = [];
2868
- this.updateCells();
3094
+ this.rangeUpdated();
3095
+ this.processRangeOptions();
3096
+ },
3097
+
3098
+
3099
+ // Called when internal variables that rely on the range should be updated
3100
+ rangeUpdated: function() {
3101
+ },
3102
+
3103
+
3104
+ // Updates values that rely on options and also relate to range
3105
+ processRangeOptions: function() {
3106
+ var view = this.view;
3107
+ var displayEventTime;
3108
+ var displayEventEnd;
2869
3109
 
2870
3110
  // Populate option-derived settings. Look for override first, then compute if necessary.
2871
3111
  this.colHeadFormat = view.opt('columnFormat') || this.computeColHeadFormat();
@@ -2890,9 +3130,15 @@ var Grid = fc.Grid = RowRenderer.extend({
2890
3130
  },
2891
3131
 
2892
3132
 
2893
- // Responsible for setting rowCnt/colCnt and any other row/col data
2894
- updateCells: function() {
2895
- // subclasses must implement
3133
+ // Called before the grid's coordinates will need to be queried for cells.
3134
+ // Any non-date-related cell system internal data should be built.
3135
+ build: function() {
3136
+ },
3137
+
3138
+
3139
+ // Called after the grid's coordinates are done being relied upon.
3140
+ // Any non-date-related cell system internal data should be cleared.
3141
+ clear: function() {
2896
3142
  },
2897
3143
 
2898
3144
 
@@ -2964,13 +3210,13 @@ var Grid = fc.Grid = RowRenderer.extend({
2964
3210
 
2965
3211
  // Retrieves misc data about the given row
2966
3212
  getRowData: function(row) {
2967
- return this.rowData[row] || {};
3213
+ return {};
2968
3214
  },
2969
3215
 
2970
3216
 
2971
3217
  // Retrieves misc data baout the given column
2972
3218
  getColData: function(col) {
2973
- return this.colData[col] || {};
3219
+ return {};
2974
3220
  },
2975
3221
 
2976
3222
 
@@ -3067,7 +3313,7 @@ var Grid = fc.Grid = RowRenderer.extend({
3067
3313
 
3068
3314
 
3069
3315
  // Removes the grid's container element from the DOM. Undoes any other DOM-related attachments.
3070
- // DOES NOT remove any content before hand (doens't clear events or call destroyDates), unlike View
3316
+ // DOES NOT remove any content beforehand (doesn't clear events or call unrenderDates), unlike View
3071
3317
  removeElement: function() {
3072
3318
  this.unbindGlobalHandlers();
3073
3319
 
@@ -3091,7 +3337,7 @@ var Grid = fc.Grid = RowRenderer.extend({
3091
3337
 
3092
3338
 
3093
3339
  // Unrenders the grid's date-related content
3094
- destroyDates: function() {
3340
+ unrenderDates: function() {
3095
3341
  // subclasses should implement
3096
3342
  },
3097
3343
 
@@ -3146,12 +3392,12 @@ var Grid = fc.Grid = RowRenderer.extend({
3146
3392
  cellOut: function(cell) {
3147
3393
  dayClickCell = null;
3148
3394
  selectionRange = null;
3149
- _this.destroySelection();
3395
+ _this.unrenderSelection();
3150
3396
  enableCursor();
3151
3397
  },
3152
3398
  listenStop: function(ev) {
3153
3399
  if (dayClickCell) {
3154
- view.trigger('dayClick', _this.getCellDayEl(dayClickCell), dayClickCell.start, ev);
3400
+ view.triggerDayClick(dayClickCell, _this.getCellDayEl(dayClickCell), ev);
3155
3401
  }
3156
3402
  if (selectionRange) {
3157
3403
  // the selection will already have been rendered. just report it
@@ -3208,7 +3454,7 @@ var Grid = fc.Grid = RowRenderer.extend({
3208
3454
 
3209
3455
 
3210
3456
  // Unrenders a mock event
3211
- destroyHelper: function() {
3457
+ unrenderHelper: function() {
3212
3458
  // subclasses must implement
3213
3459
  },
3214
3460
 
@@ -3219,13 +3465,13 @@ var Grid = fc.Grid = RowRenderer.extend({
3219
3465
 
3220
3466
  // Renders a visual indication of a selection. Will highlight by default but can be overridden by subclasses.
3221
3467
  renderSelection: function(range) {
3222
- this.renderHighlight(range);
3468
+ this.renderHighlight(this.selectionRangeToSegs(range));
3223
3469
  },
3224
3470
 
3225
3471
 
3226
3472
  // Unrenders any visual indications of a selection. Will unrender a highlight by default.
3227
- destroySelection: function() {
3228
- this.destroyHighlight();
3473
+ unrenderSelection: function() {
3474
+ this.unrenderHighlight();
3229
3475
  },
3230
3476
 
3231
3477
 
@@ -3256,19 +3502,24 @@ var Grid = fc.Grid = RowRenderer.extend({
3256
3502
  },
3257
3503
 
3258
3504
 
3505
+ selectionRangeToSegs: function(range) {
3506
+ return this.rangeToSegs(range);
3507
+ },
3508
+
3509
+
3259
3510
  /* Highlight
3260
3511
  ------------------------------------------------------------------------------------------------------------------*/
3261
3512
 
3262
3513
 
3263
- // Renders an emphasis on the given date range. `start` is inclusive. `end` is exclusive.
3264
- renderHighlight: function(range) {
3265
- this.renderFill('highlight', this.rangeToSegs(range));
3514
+ // Renders an emphasis on the given date range. Given an array of segments.
3515
+ renderHighlight: function(segs) {
3516
+ this.renderFill('highlight', segs);
3266
3517
  },
3267
3518
 
3268
3519
 
3269
3520
  // Unrenders the emphasis on a date range
3270
- destroyHighlight: function() {
3271
- this.destroyFill('highlight');
3521
+ unrenderHighlight: function() {
3522
+ this.unrenderFill('highlight');
3272
3523
  },
3273
3524
 
3274
3525
 
@@ -3283,7 +3534,7 @@ var Grid = fc.Grid = RowRenderer.extend({
3283
3534
 
3284
3535
 
3285
3536
  // Renders a set of rectangles over the given segments of time.
3286
- // Returns a subset of segs, the segs that were actually rendered.
3537
+ // MUST RETURN a subset of segs, the segs that were actually rendered.
3287
3538
  // Responsible for populating this.elsByFill. TODO: better API for expressing this requirement
3288
3539
  renderFill: function(type, segs) {
3289
3540
  // subclasses must implement
@@ -3291,7 +3542,7 @@ var Grid = fc.Grid = RowRenderer.extend({
3291
3542
 
3292
3543
 
3293
3544
  // Unrenders a specific type of fill that is currently rendered on the grid
3294
- destroyFill: function(type) {
3545
+ unrenderFill: function(type) {
3295
3546
  var el = this.elsByFill[type];
3296
3547
 
3297
3548
  if (el) {
@@ -3484,11 +3735,11 @@ Grid.mixin({
3484
3735
 
3485
3736
 
3486
3737
  // Unrenders all events currently rendered on the grid
3487
- destroyEvents: function() {
3738
+ unrenderEvents: function() {
3488
3739
  this.triggerSegMouseout(); // trigger an eventMouseout if user's mouse is over an event
3489
3740
 
3490
- this.destroyFgSegs();
3491
- this.destroyBgSegs();
3741
+ this.unrenderFgSegs();
3742
+ this.unrenderBgSegs();
3492
3743
 
3493
3744
  this.segs = null;
3494
3745
  },
@@ -3511,7 +3762,7 @@ Grid.mixin({
3511
3762
 
3512
3763
 
3513
3764
  // Unrenders all currently rendered foreground segments
3514
- destroyFgSegs: function() {
3765
+ unrenderFgSegs: function() {
3515
3766
  // subclasses must implement
3516
3767
  },
3517
3768
 
@@ -3568,8 +3819,8 @@ Grid.mixin({
3568
3819
 
3569
3820
 
3570
3821
  // Unrenders all the currently rendered background event segments
3571
- destroyBgSegs: function() {
3572
- this.destroyFill('bgEvent');
3822
+ unrenderBgSegs: function() {
3823
+ this.unrenderFill('bgEvent');
3573
3824
  },
3574
3825
 
3575
3826
 
@@ -3749,7 +4000,7 @@ Grid.mixin({
3749
4000
  }
3750
4001
  },
3751
4002
  cellOut: function() { // called before mouse moves to a different cell OR moved out of all cells
3752
- view.destroyDrag(); // unrender whatever was done in renderDrag
4003
+ view.unrenderDrag(); // unrender whatever was done in renderDrag
3753
4004
  mouseFollower.show(); // show in case we are moving out of all cells
3754
4005
  dropLocation = null;
3755
4006
  },
@@ -3759,7 +4010,7 @@ Grid.mixin({
3759
4010
  dragStop: function(ev) {
3760
4011
  // do revert animation if hasn't changed. calls a callback when finished (whether animation or not)
3761
4012
  mouseFollower.stop(!dropLocation, function() {
3762
- view.destroyDrag();
4013
+ view.unrenderDrag();
3763
4014
  view.showEvent(event);
3764
4015
  _this.segDragStop(seg, ev);
3765
4016
 
@@ -3903,11 +4154,11 @@ Grid.mixin({
3903
4154
  },
3904
4155
  cellOut: function() {
3905
4156
  dropLocation = null; // signal unsuccessful
3906
- _this.destroyDrag();
4157
+ _this.unrenderDrag();
3907
4158
  enableCursor();
3908
4159
  },
3909
4160
  dragStop: function() {
3910
- _this.destroyDrag();
4161
+ _this.unrenderDrag();
3911
4162
  enableCursor();
3912
4163
 
3913
4164
  if (dropLocation) { // element was dropped on a valid date/time cell
@@ -3964,7 +4215,7 @@ Grid.mixin({
3964
4215
 
3965
4216
 
3966
4217
  // Unrenders a visual indication of an event or external element being dragged
3967
- destroyDrag: function() {
4218
+ unrenderDrag: function() {
3968
4219
  // subclasses must implement
3969
4220
  },
3970
4221
 
@@ -4019,7 +4270,7 @@ Grid.mixin({
4019
4270
  resizeLocation = null;
4020
4271
  },
4021
4272
  cellDone: function() { // resets the rendering to show the original event
4022
- _this.destroyEventResize();
4273
+ _this.unrenderEventResize();
4023
4274
  view.showEvent(event);
4024
4275
  enableCursor();
4025
4276
  },
@@ -4118,7 +4369,7 @@ Grid.mixin({
4118
4369
 
4119
4370
 
4120
4371
  // Unrenders a visual indication of an event being resized.
4121
- destroyEventResize: function() {
4372
+ unrenderEventResize: function() {
4122
4373
  // subclasses must implement
4123
4374
  },
4124
4375
 
@@ -4334,6 +4585,8 @@ Grid.mixin({
4334
4585
  var segs;
4335
4586
  var i, seg;
4336
4587
 
4588
+ eventRange = this.view.calendar.ensureVisibleEventRange(eventRange);
4589
+
4337
4590
  if (rangeToSegsFunc) {
4338
4591
  segs = rangeToSegsFunc(eventRange);
4339
4592
  }
@@ -4349,6 +4602,21 @@ Grid.mixin({
4349
4602
  }
4350
4603
 
4351
4604
  return segs;
4605
+ },
4606
+
4607
+
4608
+ sortSegs: function(segs) {
4609
+ segs.sort(proxy(this, 'compareSegs'));
4610
+ },
4611
+
4612
+
4613
+ // A cmp function for determining which segments should take visual priority
4614
+ // DOES NOT WORK ON INVERTED BACKGROUND EVENTS because they have no eventStartMS/eventDurationMS
4615
+ compareSegs: function(seg1, seg2) {
4616
+ return seg1.eventStartMS - seg2.eventStartMS || // earlier events go first
4617
+ seg2.eventDurationMS - seg1.eventDurationMS || // tie? longer events go first
4618
+ seg2.event.allDay - seg1.event.allDay || // tie? put all-day events first (booleans cast to 0/1)
4619
+ compareByFieldSpecs(seg1.event, seg2.event, this.view.eventOrderSpecs);
4352
4620
  }
4353
4621
 
4354
4622
  });
@@ -4393,18 +4661,6 @@ function compareNormalRanges(range1, range2) {
4393
4661
  }
4394
4662
 
4395
4663
 
4396
- // A cmp function for determining which segments should take visual priority
4397
- // DOES NOT WORK ON INVERTED BACKGROUND EVENTS because they have no eventStartMS/eventDurationMS
4398
- function compareSegs(seg1, seg2) {
4399
- return seg1.eventStartMS - seg2.eventStartMS || // earlier events go first
4400
- seg2.eventDurationMS - seg1.eventDurationMS || // tie? longer events go first
4401
- seg2.event.allDay - seg1.event.allDay || // tie? put all-day events first (booleans cast to 0/1)
4402
- (seg1.event.title || '').localeCompare(seg2.event.title); // tie? alphabetically by title
4403
- }
4404
-
4405
- fc.compareSegs = compareSegs; // export
4406
-
4407
-
4408
4664
  /* External-Dragging-Element Data
4409
4665
  ----------------------------------------------------------------------------------------------------------------------*/
4410
4666
 
@@ -4513,8 +4769,8 @@ var DayGrid = Grid.extend({
4513
4769
  },
4514
4770
 
4515
4771
 
4516
- destroyDates: function() {
4517
- this.destroySegPopover();
4772
+ unrenderDates: function() {
4773
+ this.removeSegPopover();
4518
4774
  },
4519
4775
 
4520
4776
 
@@ -4598,8 +4854,7 @@ var DayGrid = Grid.extend({
4598
4854
  ------------------------------------------------------------------------------------------------------------------*/
4599
4855
 
4600
4856
 
4601
- // Initializes row/col information
4602
- updateCells: function() {
4857
+ rangeUpdated: function() {
4603
4858
  var cellDates;
4604
4859
  var firstDay;
4605
4860
  var rowCnt;
@@ -4782,9 +5037,7 @@ var DayGrid = Grid.extend({
4782
5037
  renderDrag: function(dropLocation, seg) {
4783
5038
 
4784
5039
  // always render a highlight underneath
4785
- this.renderHighlight(
4786
- this.view.calendar.ensureVisibleEventRange(dropLocation) // needs to be a proper range
4787
- );
5040
+ this.renderHighlight(this.eventRangeToSegs(dropLocation));
4788
5041
 
4789
5042
  // if a segment from the same calendar but another component is being dragged, render a helper event
4790
5043
  if (seg && !seg.el.closest(this.el).length) {
@@ -4798,9 +5051,9 @@ var DayGrid = Grid.extend({
4798
5051
 
4799
5052
 
4800
5053
  // Unrenders any visual indication of a hovering event
4801
- destroyDrag: function() {
4802
- this.destroyHighlight();
4803
- this.destroyHelper();
5054
+ unrenderDrag: function() {
5055
+ this.unrenderHighlight();
5056
+ this.unrenderHelper();
4804
5057
  },
4805
5058
 
4806
5059
 
@@ -4810,15 +5063,15 @@ var DayGrid = Grid.extend({
4810
5063
 
4811
5064
  // Renders a visual indication of an event being resized
4812
5065
  renderEventResize: function(range, seg) {
4813
- this.renderHighlight(range);
5066
+ this.renderHighlight(this.eventRangeToSegs(range));
4814
5067
  this.renderRangeHelper(range, seg);
4815
5068
  },
4816
5069
 
4817
5070
 
4818
5071
  // Unrenders a visual indication of an event being resized
4819
- destroyEventResize: function() {
4820
- this.destroyHighlight();
4821
- this.destroyHelper();
5072
+ unrenderEventResize: function() {
5073
+ this.unrenderHighlight();
5074
+ this.unrenderHelper();
4822
5075
  },
4823
5076
 
4824
5077
 
@@ -4862,7 +5115,7 @@ var DayGrid = Grid.extend({
4862
5115
 
4863
5116
 
4864
5117
  // Unrenders any visual indication of a mock helper event
4865
- destroyHelper: function() {
5118
+ unrenderHelper: function() {
4866
5119
  if (this.helperEls) {
4867
5120
  this.helperEls.remove();
4868
5121
  this.helperEls = null;
@@ -4946,9 +5199,9 @@ DayGrid.mixin({
4946
5199
 
4947
5200
 
4948
5201
  // Unrenders all events currently rendered on the grid
4949
- destroyEvents: function() {
4950
- this.destroySegPopover(); // removes the "more.." events popover
4951
- Grid.prototype.destroyEvents.apply(this, arguments); // calls the super-method
5202
+ unrenderEvents: function() {
5203
+ this.removeSegPopover(); // removes the "more.." events popover
5204
+ Grid.prototype.unrenderEvents.apply(this, arguments); // calls the super-method
4952
5205
  },
4953
5206
 
4954
5207
 
@@ -4993,7 +5246,7 @@ DayGrid.mixin({
4993
5246
 
4994
5247
 
4995
5248
  // Unrenders all currently rendered foreground event segments
4996
- destroyFgSegs: function() {
5249
+ unrenderFgSegs: function() {
4997
5250
  var rowStructs = this.rowStructs || [];
4998
5251
  var rowStruct;
4999
5252
 
@@ -5183,7 +5436,7 @@ DayGrid.mixin({
5183
5436
 
5184
5437
  // Give preference to elements with certain criteria, so they have
5185
5438
  // a chance to be closer to the top.
5186
- segs.sort(compareSegs);
5439
+ this.sortSegs(segs);
5187
5440
 
5188
5441
  for (i = 0; i < segs.length; i++) {
5189
5442
  seg = segs[i];
@@ -5265,9 +5518,9 @@ DayGrid.mixin({
5265
5518
  popoverSegs: null, // an array of segment objects that the segPopover holds. null when not visible
5266
5519
 
5267
5520
 
5268
- destroySegPopover: function() {
5521
+ removeSegPopover: function() {
5269
5522
  if (this.segPopover) {
5270
- this.segPopover.hide(); // will trigger destruction of `segPopover` and `popoverSegs`
5523
+ this.segPopover.hide(); // in handler, will call segPopover's removeElement
5271
5524
  }
5272
5525
  },
5273
5526
 
@@ -5502,8 +5755,8 @@ DayGrid.mixin({
5502
5755
  autoHide: true, // when the user clicks elsewhere, hide the popover
5503
5756
  viewportConstrain: view.opt('popoverViewportConstrain'),
5504
5757
  hide: function() {
5505
- // destroy everything when the popover is hidden
5506
- _this.segPopover.destroy();
5758
+ // kill everything when the popover is hidden
5759
+ _this.segPopover.removeElement();
5507
5760
  _this.segPopover = null;
5508
5761
  _this.popoverSegs = null;
5509
5762
  }
@@ -5584,7 +5837,7 @@ DayGrid.mixin({
5584
5837
  );
5585
5838
 
5586
5839
  // force an order because eventsToSegs doesn't guarantee one
5587
- segs.sort(compareSegs);
5840
+ this.sortSegs(segs);
5588
5841
 
5589
5842
  return segs;
5590
5843
  },
@@ -5633,11 +5886,11 @@ var TimeGrid = Grid.extend({
5633
5886
 
5634
5887
  slotDuration: null, // duration of a "slot", a distinct time segment on given day, visualized by lines
5635
5888
  snapDuration: null, // granularity of time for dragging and selecting
5636
-
5637
5889
  minTime: null, // Duration object that denotes the first visible time of any given day
5638
5890
  maxTime: null, // Duration object that denotes the exclusive visible end time of any given day
5639
-
5640
- axisFormat: null, // formatting string for times running along vertical axis
5891
+ colDates: null, // whole-day dates for each column. left to right
5892
+ labelFormat: null, // formatting string for times running along vertical axis
5893
+ labelInterval: null, // duration of how often a label should be displayed for a slot
5641
5894
 
5642
5895
  dayEls: null, // cells elements in the day-row background
5643
5896
  slatEls: null, // elements running horizontally across all columns
@@ -5698,29 +5951,28 @@ var TimeGrid = Grid.extend({
5698
5951
  var view = this.view;
5699
5952
  var isRTL = this.isRTL;
5700
5953
  var html = '';
5701
- var slotNormal = this.slotDuration.asMinutes() % 15 === 0;
5702
5954
  var slotTime = moment.duration(+this.minTime); // wish there was .clone() for durations
5703
5955
  var slotDate; // will be on the view's first day, but we only care about its time
5704
- var minutes;
5956
+ var isLabeled;
5705
5957
  var axisHtml;
5706
5958
 
5707
5959
  // Calculate the time for each slot
5708
5960
  while (slotTime < this.maxTime) {
5709
- slotDate = this.start.clone().time(slotTime); // will be in UTC but that's good. to avoid DST issues
5710
- minutes = slotDate.minutes();
5961
+ slotDate = this.start.clone().time(slotTime); // after .time() will be in UTC. but that's good, avoids DST issues
5962
+ isLabeled = isInt(divideDurationByDuration(slotTime, this.labelInterval));
5711
5963
 
5712
5964
  axisHtml =
5713
5965
  '<td class="fc-axis fc-time ' + view.widgetContentClass + '" ' + view.axisStyleAttr() + '>' +
5714
- ((!slotNormal || !minutes) ? // if irregular slot duration, or on the hour, then display the time
5966
+ (isLabeled ?
5715
5967
  '<span>' + // for matchCellWidths
5716
- htmlEscape(slotDate.format(this.axisFormat)) +
5968
+ htmlEscape(slotDate.format(this.labelFormat)) +
5717
5969
  '</span>' :
5718
5970
  ''
5719
5971
  ) +
5720
5972
  '</td>';
5721
5973
 
5722
5974
  html +=
5723
- '<tr ' + (!minutes ? '' : 'class="fc-minor"') + '>' +
5975
+ '<tr ' + (isLabeled ? '' : 'class="fc-minor"') + '>' +
5724
5976
  (!isRTL ? axisHtml : '') +
5725
5977
  '<td class="' + view.widgetContentClass + '"/>' +
5726
5978
  (isRTL ? axisHtml : '') +
@@ -5742,6 +5994,7 @@ var TimeGrid = Grid.extend({
5742
5994
  var view = this.view;
5743
5995
  var slotDuration = view.opt('slotDuration');
5744
5996
  var snapDuration = view.opt('snapDuration');
5997
+ var input;
5745
5998
 
5746
5999
  slotDuration = moment.duration(slotDuration);
5747
6000
  snapDuration = snapDuration ? moment.duration(snapDuration) : slotDuration;
@@ -5753,7 +6006,41 @@ var TimeGrid = Grid.extend({
5753
6006
  this.minTime = moment.duration(view.opt('minTime'));
5754
6007
  this.maxTime = moment.duration(view.opt('maxTime'));
5755
6008
 
5756
- this.axisFormat = view.opt('axisFormat') || view.opt('smallTimeFormat');
6009
+ // might be an array value (for TimelineView).
6010
+ // if so, getting the most granular entry (the last one probably).
6011
+ input = view.opt('slotLabelFormat');
6012
+ if ($.isArray(input)) {
6013
+ input = input[input.length - 1];
6014
+ }
6015
+
6016
+ this.labelFormat =
6017
+ input ||
6018
+ view.opt('axisFormat') || // deprecated
6019
+ view.opt('smallTimeFormat'); // the computed default
6020
+
6021
+ input = view.opt('slotLabelInterval');
6022
+ this.labelInterval = input ?
6023
+ moment.duration(input) :
6024
+ this.computeLabelInterval(slotDuration);
6025
+ },
6026
+
6027
+
6028
+ // Computes an automatic value for slotLabelInterval
6029
+ computeLabelInterval: function(slotDuration) {
6030
+ var i;
6031
+ var labelInterval;
6032
+ var slotsPerLabel;
6033
+
6034
+ // find the smallest stock label interval that results in more than one slots-per-label
6035
+ for (i = AGENDA_STOCK_SUB_DURATIONS.length - 1; i >= 0; i--) {
6036
+ labelInterval = moment.duration(AGENDA_STOCK_SUB_DURATIONS[i]);
6037
+ slotsPerLabel = divideDurationByDuration(labelInterval, slotDuration);
6038
+ if (isInt(slotsPerLabel) && slotsPerLabel > 1) {
6039
+ return labelInterval;
6040
+ }
6041
+ }
6042
+
6043
+ return moment.duration(slotDuration); // fall back. clone
5757
6044
  },
5758
6045
 
5759
6046
 
@@ -5784,36 +6071,37 @@ var TimeGrid = Grid.extend({
5784
6071
  ------------------------------------------------------------------------------------------------------------------*/
5785
6072
 
5786
6073
 
5787
- // Initializes row/col information
5788
- updateCells: function() {
6074
+ rangeUpdated: function() {
5789
6075
  var view = this.view;
5790
- var colData = [];
6076
+ var colDates = [];
5791
6077
  var date;
5792
6078
 
5793
6079
  date = this.start.clone();
5794
6080
  while (date.isBefore(this.end)) {
5795
- colData.push({
5796
- day: date.clone()
5797
- });
6081
+ colDates.push(date.clone());
5798
6082
  date.add(1, 'day');
5799
6083
  date = view.skipHiddenDays(date);
5800
6084
  }
5801
6085
 
5802
6086
  if (this.isRTL) {
5803
- colData.reverse();
6087
+ colDates.reverse();
5804
6088
  }
5805
6089
 
5806
- this.colData = colData;
5807
- this.colCnt = colData.length;
6090
+ this.colDates = colDates;
6091
+ this.colCnt = colDates.length;
5808
6092
  this.rowCnt = Math.ceil((this.maxTime - this.minTime) / this.snapDuration); // # of vertical snaps
5809
6093
  },
5810
6094
 
5811
6095
 
5812
6096
  // Given a cell object, generates its start date. Returns a reference-free copy.
5813
6097
  computeCellDate: function(cell) {
6098
+ var date = this.colDates[cell.col];
5814
6099
  var time = this.computeSnapTime(cell.row);
5815
6100
 
5816
- return this.view.calendar.rezoneDate(cell.day).time(time);
6101
+ date = this.view.calendar.rezoneDate(date); // give it a 00:00 time
6102
+ date.time(time);
6103
+
6104
+ return date;
5817
6105
  },
5818
6106
 
5819
6107
 
@@ -5849,7 +6137,7 @@ var TimeGrid = Grid.extend({
5849
6137
  };
5850
6138
 
5851
6139
  for (col = 0; col < colCnt; col++) {
5852
- colDate = this.colData[col].day; // will be ambig time/timezone
6140
+ colDate = this.colDates[col]; // will be ambig time/timezone
5853
6141
  colRange = {
5854
6142
  start: colDate.clone().time(this.minTime),
5855
6143
  end: colDate.clone().time(this.maxTime)
@@ -5971,17 +6259,15 @@ var TimeGrid = Grid.extend({
5971
6259
  }
5972
6260
  else {
5973
6261
  // otherwise, just render a highlight
5974
- this.renderHighlight(
5975
- this.view.calendar.ensureVisibleEventRange(dropLocation) // needs to be a proper range
5976
- );
6262
+ this.renderHighlight(this.eventRangeToSegs(dropLocation));
5977
6263
  }
5978
6264
  },
5979
6265
 
5980
6266
 
5981
6267
  // Unrenders any visual indication of an event being dragged
5982
- destroyDrag: function() {
5983
- this.destroyHelper();
5984
- this.destroyHighlight();
6268
+ unrenderDrag: function() {
6269
+ this.unrenderHelper();
6270
+ this.unrenderHighlight();
5985
6271
  },
5986
6272
 
5987
6273
 
@@ -5996,8 +6282,8 @@ var TimeGrid = Grid.extend({
5996
6282
 
5997
6283
 
5998
6284
  // Unrenders any visual indication of an event being resized
5999
- destroyEventResize: function() {
6000
- this.destroyHelper();
6285
+ unrenderEventResize: function() {
6286
+ this.unrenderHelper();
6001
6287
  },
6002
6288
 
6003
6289
 
@@ -6036,7 +6322,7 @@ var TimeGrid = Grid.extend({
6036
6322
 
6037
6323
 
6038
6324
  // Unrenders any mock helper event
6039
- destroyHelper: function() {
6325
+ unrenderHelper: function() {
6040
6326
  if (this.helperEl) {
6041
6327
  this.helperEl.remove();
6042
6328
  this.helperEl = null;
@@ -6054,15 +6340,15 @@ var TimeGrid = Grid.extend({
6054
6340
  this.renderRangeHelper(range);
6055
6341
  }
6056
6342
  else {
6057
- this.renderHighlight(range);
6343
+ this.renderHighlight(this.selectionRangeToSegs(range));
6058
6344
  }
6059
6345
  },
6060
6346
 
6061
6347
 
6062
6348
  // Unrenders any visual indication of a selection
6063
- destroySelection: function() {
6064
- this.destroyHelper();
6065
- this.destroyHighlight();
6349
+ unrenderSelection: function() {
6350
+ this.unrenderHelper();
6351
+ this.unrenderHighlight();
6066
6352
  },
6067
6353
 
6068
6354
 
@@ -6101,7 +6387,7 @@ var TimeGrid = Grid.extend({
6101
6387
 
6102
6388
  if (colSegs.length) {
6103
6389
  containerEl = $('<div class="fc-' + className + '-container"/>').appendTo(tdEl);
6104
- dayDate = this.colData[col].day;
6390
+ dayDate = this.colDates[col];
6105
6391
 
6106
6392
  for (i = 0; i < colSegs.length; i++) {
6107
6393
  seg = colSegs[i];
@@ -6150,7 +6436,7 @@ TimeGrid.mixin({
6150
6436
 
6151
6437
 
6152
6438
  // Unrenders all currently rendered foreground event segments
6153
- destroyFgSegs: function(segs) {
6439
+ unrenderFgSegs: function(segs) {
6154
6440
  if (this.eventSkeletonEl) {
6155
6441
  this.eventSkeletonEl.remove();
6156
6442
  this.eventSkeletonEl = null;
@@ -6174,7 +6460,7 @@ TimeGrid.mixin({
6174
6460
 
6175
6461
  for (col = 0; col < segCols.length; col++) { // iterate each column grouping
6176
6462
  colSegs = segCols[col];
6177
- placeSlotSegs(colSegs); // compute horizontal coordinates, z-index's, and reorder the array
6463
+ this.placeSlotSegs(colSegs); // compute horizontal coordinates, z-index's, and reorder the array
6178
6464
 
6179
6465
  containerEl = $('<div class="fc-event-container"/>');
6180
6466
 
@@ -6200,6 +6486,74 @@ TimeGrid.mixin({
6200
6486
  },
6201
6487
 
6202
6488
 
6489
+ // Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each.
6490
+ // NOTE: Also reorders the given array by date!
6491
+ placeSlotSegs: function(segs) {
6492
+ var levels;
6493
+ var level0;
6494
+ var i;
6495
+
6496
+ this.sortSegs(segs); // order by date
6497
+ levels = buildSlotSegLevels(segs);
6498
+ computeForwardSlotSegs(levels);
6499
+
6500
+ if ((level0 = levels[0])) {
6501
+
6502
+ for (i = 0; i < level0.length; i++) {
6503
+ computeSlotSegPressures(level0[i]);
6504
+ }
6505
+
6506
+ for (i = 0; i < level0.length; i++) {
6507
+ this.computeSlotSegCoords(level0[i], 0, 0);
6508
+ }
6509
+ }
6510
+ },
6511
+
6512
+
6513
+ // Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range
6514
+ // from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and
6515
+ // seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left.
6516
+ //
6517
+ // The segment might be part of a "series", which means consecutive segments with the same pressure
6518
+ // who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of
6519
+ // segments behind this one in the current series, and `seriesBackwardCoord` is the starting
6520
+ // coordinate of the first segment in the series.
6521
+ computeSlotSegCoords: function(seg, seriesBackwardPressure, seriesBackwardCoord) {
6522
+ var forwardSegs = seg.forwardSegs;
6523
+ var i;
6524
+
6525
+ if (seg.forwardCoord === undefined) { // not already computed
6526
+
6527
+ if (!forwardSegs.length) {
6528
+
6529
+ // if there are no forward segments, this segment should butt up against the edge
6530
+ seg.forwardCoord = 1;
6531
+ }
6532
+ else {
6533
+
6534
+ // sort highest pressure first
6535
+ this.sortForwardSlotSegs(forwardSegs);
6536
+
6537
+ // this segment's forwardCoord will be calculated from the backwardCoord of the
6538
+ // highest-pressure forward segment.
6539
+ this.computeSlotSegCoords(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
6540
+ seg.forwardCoord = forwardSegs[0].backwardCoord;
6541
+ }
6542
+
6543
+ // calculate the backwardCoord from the forwardCoord. consider the series
6544
+ seg.backwardCoord = seg.forwardCoord -
6545
+ (seg.forwardCoord - seriesBackwardCoord) / // available width for series
6546
+ (seriesBackwardPressure + 1); // # of segments in the series
6547
+
6548
+ // use this segment's coordinates to computed the coordinates of the less-pressurized
6549
+ // forward segments
6550
+ for (i=0; i<forwardSegs.length; i++) {
6551
+ this.computeSlotSegCoords(forwardSegs[i], 0, seg.forwardCoord);
6552
+ }
6553
+ }
6554
+ },
6555
+
6556
+
6203
6557
  // Refreshes the CSS top/bottom coordinates for each segment element. Probably after a window resize/zoom.
6204
6558
  // Repositions business hours segs too, so not just for events. Maybe shouldn't be here.
6205
6559
  updateSegVerticals: function() {
@@ -6361,33 +6715,25 @@ TimeGrid.mixin({
6361
6715
  }
6362
6716
 
6363
6717
  return segCols;
6364
- }
6365
-
6366
- });
6367
-
6368
-
6369
- // Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each.
6370
- // NOTE: Also reorders the given array by date!
6371
- function placeSlotSegs(segs) {
6372
- var levels;
6373
- var level0;
6374
- var i;
6718
+ },
6375
6719
 
6376
- segs.sort(compareSegs); // order by date
6377
- levels = buildSlotSegLevels(segs);
6378
- computeForwardSlotSegs(levels);
6379
6720
 
6380
- if ((level0 = levels[0])) {
6721
+ sortForwardSlotSegs: function(forwardSegs) {
6722
+ forwardSegs.sort(proxy(this, 'compareForwardSlotSegs'));
6723
+ },
6381
6724
 
6382
- for (i = 0; i < level0.length; i++) {
6383
- computeSlotSegPressures(level0[i]);
6384
- }
6385
6725
 
6386
- for (i = 0; i < level0.length; i++) {
6387
- computeSlotSegCoords(level0[i], 0, 0);
6388
- }
6726
+ // A cmp function for determining which forward segment to rely on more when computing coordinates.
6727
+ compareForwardSlotSegs: function(seg1, seg2) {
6728
+ // put higher-pressure first
6729
+ return seg2.forwardPressure - seg1.forwardPressure ||
6730
+ // put segments that are closer to initial edge first (and favor ones with no coords yet)
6731
+ (seg1.backwardCoord || 0) - (seg2.backwardCoord || 0) ||
6732
+ // do normal sorting...
6733
+ this.compareSegs(seg1, seg2);
6389
6734
  }
6390
- }
6735
+
6736
+ });
6391
6737
 
6392
6738
 
6393
6739
  // Builds an array of segments "levels". The first level will be the leftmost tier of segments if the calendar is
@@ -6466,50 +6812,6 @@ function computeSlotSegPressures(seg) {
6466
6812
  }
6467
6813
 
6468
6814
 
6469
- // Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range
6470
- // from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and
6471
- // seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left.
6472
- //
6473
- // The segment might be part of a "series", which means consecutive segments with the same pressure
6474
- // who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of
6475
- // segments behind this one in the current series, and `seriesBackwardCoord` is the starting
6476
- // coordinate of the first segment in the series.
6477
- function computeSlotSegCoords(seg, seriesBackwardPressure, seriesBackwardCoord) {
6478
- var forwardSegs = seg.forwardSegs;
6479
- var i;
6480
-
6481
- if (seg.forwardCoord === undefined) { // not already computed
6482
-
6483
- if (!forwardSegs.length) {
6484
-
6485
- // if there are no forward segments, this segment should butt up against the edge
6486
- seg.forwardCoord = 1;
6487
- }
6488
- else {
6489
-
6490
- // sort highest pressure first
6491
- forwardSegs.sort(compareForwardSlotSegs);
6492
-
6493
- // this segment's forwardCoord will be calculated from the backwardCoord of the
6494
- // highest-pressure forward segment.
6495
- computeSlotSegCoords(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
6496
- seg.forwardCoord = forwardSegs[0].backwardCoord;
6497
- }
6498
-
6499
- // calculate the backwardCoord from the forwardCoord. consider the series
6500
- seg.backwardCoord = seg.forwardCoord -
6501
- (seg.forwardCoord - seriesBackwardCoord) / // available width for series
6502
- (seriesBackwardPressure + 1); // # of segments in the series
6503
-
6504
- // use this segment's coordinates to computed the coordinates of the less-pressurized
6505
- // forward segments
6506
- for (i=0; i<forwardSegs.length; i++) {
6507
- computeSlotSegCoords(forwardSegs[i], 0, seg.forwardCoord);
6508
- }
6509
- }
6510
- }
6511
-
6512
-
6513
6815
  // Find all the segments in `otherSegs` that vertically collide with `seg`.
6514
6816
  // Append into an optionally-supplied `results` array and return.
6515
6817
  function computeSlotSegCollisions(seg, otherSegs, results) {
@@ -6530,17 +6832,6 @@ function isSlotSegCollision(seg1, seg2) {
6530
6832
  return seg1.bottom > seg2.top && seg1.top < seg2.bottom;
6531
6833
  }
6532
6834
 
6533
-
6534
- // A cmp function for determining which forward segment to rely on more when computing coordinates.
6535
- function compareForwardSlotSegs(seg1, seg2) {
6536
- // put higher-pressure first
6537
- return seg2.forwardPressure - seg1.forwardPressure ||
6538
- // put segments that are closer to initial edge first (and favor ones with no coords yet)
6539
- (seg1.backwardCoord || 0) - (seg2.backwardCoord || 0) ||
6540
- // do normal sorting...
6541
- compareSegs(seg1, seg2);
6542
- }
6543
-
6544
6835
  ;;
6545
6836
 
6546
6837
  /* An abstract class from which other views inherit from
@@ -6557,7 +6848,7 @@ var View = fc.View = Class.extend({
6557
6848
  coordMap: null, // a CoordMap object for converting pixel regions to dates
6558
6849
  el: null, // the view's containing element. set by Calendar
6559
6850
 
6560
- isDisplayed: false,
6851
+ displaying: null, // a promise representing the state of rendering. null if no render requested
6561
6852
  isSkeletonRendered: false,
6562
6853
  isEventsRendered: false,
6563
6854
 
@@ -6572,8 +6863,11 @@ var View = fc.View = Class.extend({
6572
6863
  intervalDuration: null,
6573
6864
  intervalUnit: null, // name of largest unit being displayed, like "month" or "week"
6574
6865
 
6866
+ isRTL: false,
6575
6867
  isSelected: false, // boolean whether a range of time is user-selected or not
6576
6868
 
6869
+ eventOrderSpecs: null, // criteria for ordering events when they have same date/time
6870
+
6577
6871
  // subclasses can optionally use a scroll container
6578
6872
  scrollerEl: null, // the element that will most likely scroll when content is too tall
6579
6873
  scrollTop: null, // cached vertical scroll value
@@ -6601,6 +6895,9 @@ var View = fc.View = Class.extend({
6601
6895
  this.nextDayThreshold = moment.duration(this.opt('nextDayThreshold'));
6602
6896
  this.initThemingProps();
6603
6897
  this.initHiddenDays();
6898
+ this.isRTL = this.opt('isRTL');
6899
+
6900
+ this.eventOrderSpecs = parseFieldSpecs(this.opt('eventOrder'));
6604
6901
 
6605
6902
  this.documentMousedownProxy = proxy(this, 'documentMousedown');
6606
6903
 
@@ -6790,7 +7087,7 @@ var View = fc.View = Class.extend({
6790
7087
 
6791
7088
  // clean up the skeleton
6792
7089
  if (this.isSkeletonRendered) {
6793
- this.destroySkeleton();
7090
+ this.unrenderSkeleton();
6794
7091
  this.isSkeletonRendered = false;
6795
7092
  }
6796
7093
 
@@ -6799,62 +7096,83 @@ var View = fc.View = Class.extend({
6799
7096
  this.el.remove();
6800
7097
 
6801
7098
  // NOTE: don't null-out this.el in case the View was destroyed within an API callback.
6802
- // We don't null-out the View's other jQuery element references upon destroy, so why should we kill this.el?
7099
+ // We don't null-out the View's other jQuery element references upon destroy,
7100
+ // so we shouldn't kill this.el either.
6803
7101
  },
6804
7102
 
6805
7103
 
6806
7104
  // Does everything necessary to display the view centered around the given date.
6807
7105
  // Does every type of rendering EXCEPT rendering events.
7106
+ // Is asychronous and returns a promise.
6808
7107
  display: function(date) {
7108
+ var _this = this;
6809
7109
  var scrollState = null;
6810
7110
 
6811
- if (this.isDisplayed) {
7111
+ if (this.displaying) {
6812
7112
  scrollState = this.queryScroll();
6813
7113
  }
6814
7114
 
6815
- this.clear(); // clear the old content
6816
- this.setDate(date);
6817
- this.render();
6818
- this.updateSize();
6819
- this.renderBusinessHours(); // might need coordinates, so should go after updateSize()
6820
- this.isDisplayed = true;
6821
-
6822
- scrollState = this.computeInitialScroll(scrollState);
6823
- this.forceScroll(scrollState);
6824
-
6825
- this.triggerRender();
7115
+ return this.clear().then(function() { // clear the content first (async)
7116
+ return (
7117
+ _this.displaying =
7118
+ $.when(_this.displayView(date)) // displayView might return a promise
7119
+ .then(function() {
7120
+ _this.forceScroll(_this.computeInitialScroll(scrollState));
7121
+ _this.triggerRender();
7122
+ })
7123
+ );
7124
+ });
6826
7125
  },
6827
7126
 
6828
7127
 
6829
7128
  // Does everything necessary to clear the content of the view.
6830
7129
  // Clears dates and events. Does not clear the skeleton.
6831
- clear: function() { // clears the view of *content* but not the skeleton
6832
- if (this.isDisplayed) {
6833
- this.unselect();
6834
- this.clearEvents();
6835
- this.triggerDestroy();
6836
- this.destroyBusinessHours();
6837
- this.destroy();
6838
- this.isDisplayed = false;
7130
+ // Is asychronous and returns a promise.
7131
+ clear: function() {
7132
+ var _this = this;
7133
+ var displaying = this.displaying;
7134
+
7135
+ if (displaying) { // previously displayed, or in the process of being displayed?
7136
+ return displaying.then(function() { // wait for the display to finish
7137
+ _this.displaying = null;
7138
+ _this.clearEvents();
7139
+ return _this.clearView(); // might return a promise. chain it
7140
+ });
7141
+ }
7142
+ else {
7143
+ return $.when(); // an immediately-resolved promise
6839
7144
  }
6840
7145
  },
6841
7146
 
6842
7147
 
6843
- // Renders the view's date-related content, rendering the view's non-content skeleton if necessary
6844
- render: function() {
7148
+ // Displays the view's non-event content, such as date-related content or anything required by events.
7149
+ // Renders the view's non-content skeleton if necessary.
7150
+ // Can be asynchronous and return a promise.
7151
+ displayView: function(date) {
6845
7152
  if (!this.isSkeletonRendered) {
6846
7153
  this.renderSkeleton();
6847
7154
  this.isSkeletonRendered = true;
6848
7155
  }
7156
+ this.setDate(date);
7157
+ if (this.render) {
7158
+ this.render(); // TODO: deprecate
7159
+ }
6849
7160
  this.renderDates();
7161
+ this.updateSize();
7162
+ this.renderBusinessHours(); // might need coordinates, so should go after updateSize()
6850
7163
  },
6851
7164
 
6852
7165
 
6853
- // Unrenders the view's date-related content.
6854
- // Call this instead of destroyDates directly in case the View subclass wants to use a render/destroy pattern
6855
- // where both the skeleton and the content always get rendered/unrendered together.
6856
- destroy: function() {
6857
- this.destroyDates();
7166
+ // Unrenders the view content that was rendered in displayView.
7167
+ // Can be asynchronous and return a promise.
7168
+ clearView: function() {
7169
+ this.unselect();
7170
+ this.triggerUnrender();
7171
+ this.unrenderBusinessHours();
7172
+ this.unrenderDates();
7173
+ if (this.destroy) {
7174
+ this.destroy(); // TODO: deprecate
7175
+ }
6858
7176
  },
6859
7177
 
6860
7178
 
@@ -6865,7 +7183,7 @@ var View = fc.View = Class.extend({
6865
7183
 
6866
7184
 
6867
7185
  // Unrenders the basic structure of the view
6868
- destroySkeleton: function() {
7186
+ unrenderSkeleton: function() {
6869
7187
  // subclasses should implement
6870
7188
  },
6871
7189
 
@@ -6878,7 +7196,7 @@ var View = fc.View = Class.extend({
6878
7196
 
6879
7197
 
6880
7198
  // Unrenders the view's date-related content
6881
- destroyDates: function() {
7199
+ unrenderDates: function() {
6882
7200
  // subclasses should override
6883
7201
  },
6884
7202
 
@@ -6890,7 +7208,7 @@ var View = fc.View = Class.extend({
6890
7208
 
6891
7209
 
6892
7210
  // Unrenders previously-rendered business-hours
6893
- destroyBusinessHours: function() {
7211
+ unrenderBusinessHours: function() {
6894
7212
  // subclasses should implement
6895
7213
  },
6896
7214
 
@@ -6902,7 +7220,7 @@ var View = fc.View = Class.extend({
6902
7220
 
6903
7221
 
6904
7222
  // Signals that the view's content is about to be unrendered
6905
- triggerDestroy: function() {
7223
+ triggerUnrender: function() {
6906
7224
  this.trigger('viewDestroy', this, this, this.el);
6907
7225
  },
6908
7226
 
@@ -6941,8 +7259,8 @@ var View = fc.View = Class.extend({
6941
7259
  scrollState = this.queryScroll();
6942
7260
  }
6943
7261
 
6944
- this.updateHeight();
6945
- this.updateWidth();
7262
+ this.updateHeight(isResize);
7263
+ this.updateWidth(isResize);
6946
7264
 
6947
7265
  if (isResize) {
6948
7266
  this.setScroll(scrollState);
@@ -6951,13 +7269,13 @@ var View = fc.View = Class.extend({
6951
7269
 
6952
7270
 
6953
7271
  // Refreshes the horizontal dimensions of the calendar
6954
- updateWidth: function() {
7272
+ updateWidth: function(isResize) {
6955
7273
  // subclasses should implement
6956
7274
  },
6957
7275
 
6958
7276
 
6959
7277
  // Refreshes the vertical dimensions of the calendar
6960
- updateHeight: function() {
7278
+ updateHeight: function(isResize) {
6961
7279
  var calendar = this.calendar; // we poll the calendar for height information
6962
7280
 
6963
7281
  this.setHeight(
@@ -7052,8 +7370,11 @@ var View = fc.View = Class.extend({
7052
7370
  // Does everything necessary to clear the view's currently-rendered events
7053
7371
  clearEvents: function() {
7054
7372
  if (this.isEventsRendered) {
7055
- this.triggerEventDestroy();
7056
- this.destroyEvents();
7373
+ this.triggerEventUnrender();
7374
+ if (this.destroyEvents) {
7375
+ this.destroyEvents(); // TODO: deprecate
7376
+ }
7377
+ this.unrenderEvents();
7057
7378
  this.isEventsRendered = false;
7058
7379
  }
7059
7380
  },
@@ -7066,7 +7387,7 @@ var View = fc.View = Class.extend({
7066
7387
 
7067
7388
 
7068
7389
  // Removes event elements from the view.
7069
- destroyEvents: function() {
7390
+ unrenderEvents: function() {
7070
7391
  // subclasses should implement
7071
7392
  },
7072
7393
 
@@ -7081,7 +7402,7 @@ var View = fc.View = Class.extend({
7081
7402
 
7082
7403
 
7083
7404
  // Signals that all event elements are about to be removed
7084
- triggerEventDestroy: function() {
7405
+ triggerEventUnrender: function() {
7085
7406
  this.renderedEventSegEach(function(seg) {
7086
7407
  this.trigger('eventDestroy', seg.event, seg.event, seg.el);
7087
7408
  });
@@ -7230,7 +7551,7 @@ var View = fc.View = Class.extend({
7230
7551
 
7231
7552
 
7232
7553
  // Unrenders a visual indication of an event or external-element being dragged.
7233
- destroyDrag: function() {
7554
+ unrenderDrag: function() {
7234
7555
  // subclasses must implement
7235
7556
  },
7236
7557
 
@@ -7308,6 +7629,12 @@ var View = fc.View = Class.extend({
7308
7629
  // Called when a new selection is made. Updates internal state and triggers handlers.
7309
7630
  reportSelection: function(range, ev) {
7310
7631
  this.isSelected = true;
7632
+ this.triggerSelect(range, ev);
7633
+ },
7634
+
7635
+
7636
+ // Triggers handlers to 'select'
7637
+ triggerSelect: function(range, ev) {
7311
7638
  this.trigger('select', null, range.start, range.end, ev);
7312
7639
  },
7313
7640
 
@@ -7317,14 +7644,17 @@ var View = fc.View = Class.extend({
7317
7644
  unselect: function(ev) {
7318
7645
  if (this.isSelected) {
7319
7646
  this.isSelected = false;
7320
- this.destroySelection();
7647
+ if (this.destroySelection) {
7648
+ this.destroySelection(); // TODO: deprecate
7649
+ }
7650
+ this.unrenderSelection();
7321
7651
  this.trigger('unselect', null, ev);
7322
7652
  }
7323
7653
  },
7324
7654
 
7325
7655
 
7326
7656
  // Unrenders a visual indication of selection
7327
- destroySelection: function() {
7657
+ unrenderSelection: function() {
7328
7658
  // subclasses should implement
7329
7659
  },
7330
7660
 
@@ -7345,6 +7675,16 @@ var View = fc.View = Class.extend({
7345
7675
  },
7346
7676
 
7347
7677
 
7678
+ /* Day Click
7679
+ ------------------------------------------------------------------------------------------------------------------*/
7680
+
7681
+
7682
+ // Triggers handlers to 'dayClick'
7683
+ triggerDayClick: function(cell, dayEl, ev) {
7684
+ this.trigger('dayClick', dayEl, cell.start, ev);
7685
+ },
7686
+
7687
+
7348
7688
  /* Date Utils
7349
7689
  ------------------------------------------------------------------------------------------------------------------*/
7350
7690
 
@@ -7443,7 +7783,7 @@ var View = fc.View = Class.extend({
7443
7783
 
7444
7784
  ;;
7445
7785
 
7446
- var Calendar = fc.Calendar = fc.CalendarBase = Class.extend({
7786
+ var Calendar = fc.Calendar = Class.extend({
7447
7787
 
7448
7788
  dirDefaults: null, // option defaults related to LTR or RTL
7449
7789
  langDefaults: null, // option defaults related to current locale
@@ -7452,6 +7792,7 @@ var Calendar = fc.Calendar = fc.CalendarBase = Class.extend({
7452
7792
  viewSpecCache: null, // cache of view definitions
7453
7793
  view: null, // current View object
7454
7794
  header: null,
7795
+ loadingLevel: 0, // number of simultaneous loading tasks
7455
7796
 
7456
7797
 
7457
7798
  // a lot of this class' OOP logic is scoped within this constructor function,
@@ -7459,6 +7800,11 @@ var Calendar = fc.Calendar = fc.CalendarBase = Class.extend({
7459
7800
  constructor: Calendar_constructor,
7460
7801
 
7461
7802
 
7803
+ // Subclasses can override this for initialization logic after the constructor has been called
7804
+ initialize: function() {
7805
+ },
7806
+
7807
+
7462
7808
  // Initializes `this.options` and other important options-related objects
7463
7809
  initOptions: function(overrides) {
7464
7810
  var lang, langDefaults;
@@ -7485,12 +7831,12 @@ var Calendar = fc.Calendar = fc.CalendarBase = Class.extend({
7485
7831
  this.dirDefaults = dirDefaults;
7486
7832
  this.langDefaults = langDefaults;
7487
7833
  this.overrides = overrides;
7488
- this.options = mergeOptions( // merge defaults and overrides. lowest to highest precedence
7834
+ this.options = mergeOptions([ // merge defaults and overrides. lowest to highest precedence
7489
7835
  Calendar.defaults, // global defaults
7490
7836
  dirDefaults,
7491
7837
  langDefaults,
7492
7838
  overrides
7493
- );
7839
+ ]);
7494
7840
  populateInstanceComputableOptions(this.options);
7495
7841
 
7496
7842
  this.viewSpecCache = {}; // somewhat unrelated
@@ -7535,43 +7881,48 @@ var Calendar = fc.Calendar = fc.CalendarBase = Class.extend({
7535
7881
  // Builds an object with information on how to create a given view
7536
7882
  buildViewSpec: function(requestedViewType) {
7537
7883
  var viewOverrides = this.overrides.views || {};
7884
+ var specChain = []; // for the view. lowest to highest priority
7538
7885
  var defaultsChain = []; // for the view. lowest to highest priority
7539
7886
  var overridesChain = []; // for the view. lowest to highest priority
7540
7887
  var viewType = requestedViewType;
7541
- var viewClass;
7542
- var defaults; // for the view
7888
+ var spec; // for the view
7543
7889
  var overrides; // for the view
7544
7890
  var duration;
7545
7891
  var unit;
7546
- var spec;
7547
7892
 
7548
7893
  // iterate from the specific view definition to a more general one until we hit an actual View class
7549
- while (viewType && !viewClass) {
7550
- defaults = fcViews[viewType] || {};
7551
- overrides = viewOverrides[viewType] || {};
7552
- duration = duration || overrides.duration || defaults.duration;
7553
- viewType = overrides.type || defaults.type; // for next iteration
7894
+ while (viewType) {
7895
+ spec = fcViews[viewType];
7896
+ overrides = viewOverrides[viewType];
7897
+ viewType = null; // clear. might repopulate for another iteration
7898
+
7899
+ if (typeof spec === 'function') { // TODO: deprecate
7900
+ spec = { 'class': spec };
7901
+ }
7554
7902
 
7555
- if (typeof defaults === 'function') { // a class
7556
- viewClass = defaults;
7557
- defaultsChain.unshift(viewClass.defaults || {});
7903
+ if (spec) {
7904
+ specChain.unshift(spec);
7905
+ defaultsChain.unshift(spec.defaults || {});
7906
+ duration = duration || spec.duration;
7907
+ viewType = viewType || spec.type;
7558
7908
  }
7559
- else { // an options object
7560
- defaultsChain.unshift(defaults);
7909
+
7910
+ if (overrides) {
7911
+ overridesChain.unshift(overrides); // view-specific option hashes have options at zero-level
7912
+ duration = duration || overrides.duration;
7913
+ viewType = viewType || overrides.type;
7561
7914
  }
7562
- overridesChain.unshift(overrides);
7563
7915
  }
7564
7916
 
7565
- if (viewClass) {
7566
- spec = { 'class': viewClass, type: requestedViewType };
7917
+ spec = mergeProps(specChain);
7918
+ spec.type = requestedViewType;
7919
+ if (!spec['class']) {
7920
+ return false;
7921
+ }
7567
7922
 
7568
- if (duration) {
7569
- duration = moment.duration(duration);
7570
- if (!duration.valueOf()) { // invalid?
7571
- duration = null;
7572
- }
7573
- }
7574
- if (duration) {
7923
+ if (duration) {
7924
+ duration = moment.duration(duration);
7925
+ if (duration.valueOf()) { // valid?
7575
7926
  spec.duration = duration;
7576
7927
  unit = computeIntervalUnit(duration);
7577
7928
 
@@ -7582,29 +7933,28 @@ var Calendar = fc.Calendar = fc.CalendarBase = Class.extend({
7582
7933
  overridesChain.unshift(viewOverrides[unit] || {});
7583
7934
  }
7584
7935
  }
7936
+ }
7585
7937
 
7586
- // collapse into single objects
7587
- spec.defaults = mergeOptions.apply(null, defaultsChain);
7588
- spec.overrides = mergeOptions.apply(null, overridesChain);
7938
+ spec.defaults = mergeOptions(defaultsChain);
7939
+ spec.overrides = mergeOptions(overridesChain);
7589
7940
 
7590
- this.buildViewSpecOptions(spec);
7591
- this.buildViewSpecButtonText(spec, requestedViewType);
7941
+ this.buildViewSpecOptions(spec);
7942
+ this.buildViewSpecButtonText(spec, requestedViewType);
7592
7943
 
7593
- return spec;
7594
- }
7944
+ return spec;
7595
7945
  },
7596
7946
 
7597
7947
 
7598
7948
  // Builds and assigns a view spec's options object from its already-assigned defaults and overrides
7599
7949
  buildViewSpecOptions: function(spec) {
7600
- spec.options = mergeOptions( // lowest to highest priority
7950
+ spec.options = mergeOptions([ // lowest to highest priority
7601
7951
  Calendar.defaults, // global defaults
7602
7952
  spec.defaults, // view's defaults (from ViewSubclass.defaults)
7603
7953
  this.dirDefaults,
7604
7954
  this.langDefaults, // locale and dir take precedence over view's defaults!
7605
7955
  this.overrides, // calendar's overrides (options given to constructor)
7606
7956
  spec.overrides // view's overrides (view-specific options)
7607
- );
7957
+ ]);
7608
7958
  populateInstanceComputableOptions(spec.options);
7609
7959
  },
7610
7960
 
@@ -7647,11 +7997,48 @@ var Calendar = fc.Calendar = fc.CalendarBase = Class.extend({
7647
7997
  // Returns a boolean about whether the view is okay to instantiate at some point
7648
7998
  isValidViewType: function(viewType) {
7649
7999
  return Boolean(this.getViewSpec(viewType));
8000
+ },
8001
+
8002
+
8003
+ // Should be called when any type of async data fetching begins
8004
+ pushLoading: function() {
8005
+ if (!(this.loadingLevel++)) {
8006
+ this.trigger('loading', null, true, this.view);
8007
+ }
8008
+ },
8009
+
8010
+
8011
+ // Should be called when any type of async data fetching completes
8012
+ popLoading: function() {
8013
+ if (!(--this.loadingLevel)) {
8014
+ this.trigger('loading', null, false, this.view);
8015
+ }
8016
+ },
8017
+
8018
+
8019
+ // Given arguments to the select method in the API, returns a range
8020
+ buildSelectRange: function(start, end) {
8021
+
8022
+ start = this.moment(start);
8023
+ if (end) {
8024
+ end = this.moment(end);
8025
+ }
8026
+ else if (start.hasTime()) {
8027
+ end = start.clone().add(this.defaultTimedEventDuration);
8028
+ }
8029
+ else {
8030
+ end = start.clone().add(this.defaultAllDayEventDuration);
8031
+ }
8032
+
8033
+ return { start: start, end: end };
7650
8034
  }
7651
8035
 
7652
8036
  });
7653
8037
 
7654
8038
 
8039
+ Calendar.mixin(Emitter);
8040
+
8041
+
7655
8042
  function Calendar_constructor(element, overrides) {
7656
8043
  var t = this;
7657
8044
 
@@ -7930,7 +8317,7 @@ function Calendar_constructor(element, overrides) {
7930
8317
  // It is still the "current" view, just not rendered.
7931
8318
  }
7932
8319
 
7933
- header.destroy();
8320
+ header.removeElement();
7934
8321
  content.remove();
7935
8322
  element.removeClass('fc fc-ltr fc-rtl fc-unthemed ui-widget');
7936
8323
 
@@ -7955,7 +8342,7 @@ function Calendar_constructor(element, overrides) {
7955
8342
  function renderView(viewType) {
7956
8343
  ignoreWindowResize++;
7957
8344
 
7958
- // if viewType is changing, destroy the old view
8345
+ // if viewType is changing, remove the old view's rendering
7959
8346
  if (currentView && viewType && currentView.type !== viewType) {
7960
8347
  header.deactivateButton(currentView.type);
7961
8348
  freezeContentHeight(); // prevent a scroll jump when view element is removed
@@ -7982,14 +8369,14 @@ function Calendar_constructor(element, overrides) {
7982
8369
 
7983
8370
  // render or rerender the view
7984
8371
  if (
7985
- !currentView.isDisplayed ||
8372
+ !currentView.displaying ||
7986
8373
  !date.isWithin(currentView.intervalStart, currentView.intervalEnd) // implicit date window change
7987
8374
  ) {
7988
8375
  if (elementVisible()) {
7989
8376
 
7990
8377
  freezeContentHeight();
7991
8378
  currentView.display(date);
7992
- unfreezeContentHeight();
8379
+ unfreezeContentHeight(); // immediately unfreeze regardless of whether display is async
7993
8380
 
7994
8381
  // need to do this after View::render, so dates are calculated
7995
8382
  updateHeaderTitle();
@@ -8157,19 +8544,9 @@ function Calendar_constructor(element, overrides) {
8157
8544
 
8158
8545
 
8159
8546
  function select(start, end) {
8160
-
8161
- start = t.moment(start);
8162
- if (end) {
8163
- end = t.moment(end);
8164
- }
8165
- else if (start.hasTime()) {
8166
- end = start.clone().add(t.defaultTimedEventDuration);
8167
- }
8168
- else {
8169
- end = start.clone().add(t.defaultAllDayEventDuration);
8170
- }
8171
-
8172
- currentView.select({ start: start, end: end }); // accepts a range
8547
+ currentView.select(
8548
+ t.buildSelectRange.apply(t, arguments)
8549
+ );
8173
8550
  }
8174
8551
 
8175
8552
 
@@ -8295,15 +8672,18 @@ function Calendar_constructor(element, overrides) {
8295
8672
  }
8296
8673
 
8297
8674
 
8298
- function trigger(name, thisObj) {
8675
+ function trigger(name, thisObj) { // overrides the Emitter's trigger method :(
8676
+ var args = Array.prototype.slice.call(arguments, 2);
8677
+
8678
+ thisObj = thisObj || _element;
8679
+ this.triggerWith(name, thisObj, args); // Emitter's method
8680
+
8299
8681
  if (options[name]) {
8300
- return options[name].apply(
8301
- thisObj || _element,
8302
- Array.prototype.slice.call(arguments, 2)
8303
- );
8682
+ return options[name].apply(thisObj, args);
8304
8683
  }
8305
8684
  }
8306
8685
 
8686
+ t.initialize();
8307
8687
  }
8308
8688
 
8309
8689
  ;;
@@ -8333,6 +8713,8 @@ Calendar.defaults = {
8333
8713
  weekNumberCalculation: 'local',
8334
8714
 
8335
8715
  //editable: false,
8716
+
8717
+ scrollTime: '06:00:00',
8336
8718
 
8337
8719
  // event ajax
8338
8720
  lazyFetching: true,
@@ -8384,6 +8766,8 @@ Calendar.defaults = {
8384
8766
 
8385
8767
  dropAccept: '*',
8386
8768
 
8769
+ eventOrder: 'title',
8770
+
8387
8771
  eventLimit: false,
8388
8772
  eventLimitText: 'more',
8389
8773
  eventLimitClick: 'popover',
@@ -8475,7 +8859,7 @@ fc.lang = function(langCode, newFcOptions) {
8475
8859
 
8476
8860
  // provided new options for this language? merge them in
8477
8861
  if (newFcOptions) {
8478
- fcOptions = langOptionHash[langCode] = mergeOptions(fcOptions, newFcOptions);
8862
+ fcOptions = langOptionHash[langCode] = mergeOptions([ fcOptions, newFcOptions ]);
8479
8863
  }
8480
8864
 
8481
8865
  // compute language options that weren't defined.
@@ -8631,7 +9015,7 @@ function Header(calendar, options) {
8631
9015
 
8632
9016
  // exports
8633
9017
  t.render = render;
8634
- t.destroy = destroy;
9018
+ t.removeElement = removeElement;
8635
9019
  t.updateTitle = updateTitle;
8636
9020
  t.activateButton = activateButton;
8637
9021
  t.deactivateButton = deactivateButton;
@@ -8662,8 +9046,9 @@ function Header(calendar, options) {
8662
9046
  }
8663
9047
 
8664
9048
 
8665
- function destroy() {
9049
+ function removeElement() {
8666
9050
  el.remove();
9051
+ el = $();
8667
9052
  }
8668
9053
 
8669
9054
 
@@ -8678,6 +9063,7 @@ function Header(calendar, options) {
8678
9063
  var groupEl;
8679
9064
 
8680
9065
  $.each(this.split(','), function(j, buttonName) {
9066
+ var customButtonProps;
8681
9067
  var viewSpec;
8682
9068
  var buttonClick;
8683
9069
  var overrideText; // text explicitly set by calendar's constructor options. overcomes icons
@@ -8686,16 +9072,23 @@ function Header(calendar, options) {
8686
9072
  var normalIcon;
8687
9073
  var innerHtml;
8688
9074
  var classes;
8689
- var button;
9075
+ var button; // the element
8690
9076
 
8691
9077
  if (buttonName == 'title') {
8692
9078
  groupChildren = groupChildren.add($('<h2>&nbsp;</h2>')); // we always want it to take up height
8693
9079
  isOnlyButtons = false;
8694
9080
  }
8695
9081
  else {
8696
- viewSpec = calendar.getViewSpec(buttonName);
8697
-
8698
- if (viewSpec) {
9082
+ if ((customButtonProps = (calendar.options.customButtons || {})[buttonName])) {
9083
+ buttonClick = function(ev) {
9084
+ if (customButtonProps.click) {
9085
+ customButtonProps.click.call(button[0], ev);
9086
+ }
9087
+ };
9088
+ overrideText = ''; // icons will override text
9089
+ defaultText = customButtonProps.text;
9090
+ }
9091
+ else if ((viewSpec = calendar.getViewSpec(buttonName))) {
8699
9092
  buttonClick = function() {
8700
9093
  calendar.changeView(buttonName);
8701
9094
  };
@@ -8713,8 +9106,15 @@ function Header(calendar, options) {
8713
9106
 
8714
9107
  if (buttonClick) {
8715
9108
 
8716
- themeIcon = options.themeButtonIcons[buttonName];
8717
- normalIcon = options.buttonIcons[buttonName];
9109
+ themeIcon =
9110
+ customButtonProps ?
9111
+ customButtonProps.themeIcon :
9112
+ options.themeButtonIcons[buttonName];
9113
+
9114
+ normalIcon =
9115
+ customButtonProps ?
9116
+ customButtonProps.icon :
9117
+ options.buttonIcons[buttonName];
8718
9118
 
8719
9119
  if (overrideText) {
8720
9120
  innerHtml = htmlEscape(overrideText);
@@ -8740,11 +9140,11 @@ function Header(calendar, options) {
8740
9140
  innerHtml +
8741
9141
  '</button>'
8742
9142
  )
8743
- .click(function() {
9143
+ .click(function(ev) {
8744
9144
  // don't process clicks for disabled buttons
8745
9145
  if (!button.hasClass(tm + '-state-disabled')) {
8746
9146
 
8747
- buttonClick();
9147
+ buttonClick(ev);
8748
9148
 
8749
9149
  // after the click action, if the button becomes the "active" tab, or disabled,
8750
9150
  // it should never have a hover class, so remove it now.
@@ -8884,8 +9284,6 @@ function EventManager(options) { // assumed to be a calendar
8884
9284
 
8885
9285
 
8886
9286
  // imports
8887
- var trigger = t.trigger;
8888
- var getView = t.getView;
8889
9287
  var reportEvents = t.reportEvents;
8890
9288
 
8891
9289
 
@@ -8895,7 +9293,6 @@ function EventManager(options) { // assumed to be a calendar
8895
9293
  var rangeStart, rangeEnd;
8896
9294
  var currentFetchID = 0;
8897
9295
  var pendingSourceCnt = 0;
8898
- var loadingLevel = 0;
8899
9296
  var cache = []; // holds events that have already been expanded
8900
9297
 
8901
9298
 
@@ -9002,7 +9399,7 @@ function EventManager(options) { // assumed to be a calendar
9002
9399
  var events = source.events;
9003
9400
  if (events) {
9004
9401
  if ($.isFunction(events)) {
9005
- pushLoading();
9402
+ t.pushLoading();
9006
9403
  events.call(
9007
9404
  t, // this, the Calendar object
9008
9405
  rangeStart.clone(),
@@ -9010,7 +9407,7 @@ function EventManager(options) { // assumed to be a calendar
9010
9407
  options.timezone,
9011
9408
  function(events) {
9012
9409
  callback(events);
9013
- popLoading();
9410
+ t.popLoading();
9014
9411
  }
9015
9412
  );
9016
9413
  }
@@ -9056,7 +9453,7 @@ function EventManager(options) { // assumed to be a calendar
9056
9453
  data[timezoneParam] = options.timezone;
9057
9454
  }
9058
9455
 
9059
- pushLoading();
9456
+ t.pushLoading();
9060
9457
  $.ajax($.extend({}, ajaxDefaults, source, {
9061
9458
  data: data,
9062
9459
  success: function(events) {
@@ -9073,7 +9470,7 @@ function EventManager(options) { // assumed to be a calendar
9073
9470
  },
9074
9471
  complete: function() {
9075
9472
  applyAll(complete, this, arguments);
9076
- popLoading();
9473
+ t.popLoading();
9077
9474
  }
9078
9475
  }));
9079
9476
  }else{
@@ -9288,25 +9685,6 @@ function EventManager(options) { // assumed to be a calendar
9288
9685
 
9289
9686
 
9290
9687
 
9291
- /* Loading State
9292
- -----------------------------------------------------------------------------*/
9293
-
9294
-
9295
- function pushLoading() {
9296
- if (!(loadingLevel++)) {
9297
- trigger('loading', null, true, getView());
9298
- }
9299
- }
9300
-
9301
-
9302
- function popLoading() {
9303
- if (!(--loadingLevel)) {
9304
- trigger('loading', null, false, getView());
9305
- }
9306
- }
9307
-
9308
-
9309
-
9310
9688
  /* Event Normalization
9311
9689
  -----------------------------------------------------------------------------*/
9312
9690
 
@@ -9991,7 +10369,7 @@ function backupEventDates(event) {
9991
10369
  // It is a manager for a DayGrid subcomponent, which does most of the heavy lifting.
9992
10370
  // It is responsible for managing width/height.
9993
10371
 
9994
- var BasicView = fcViews.basic = View.extend({
10372
+ var BasicView = View.extend({
9995
10373
 
9996
10374
  dayGrid: null, // the main subcomponent that does most of the heavy lifting
9997
10375
 
@@ -10039,7 +10417,7 @@ var BasicView = fcViews.basic = View.extend({
10039
10417
 
10040
10418
 
10041
10419
  // Renders the view into `this.el`, which should already be assigned
10042
- render: function() {
10420
+ renderDates: function() {
10043
10421
 
10044
10422
  this.dayNumbersVisible = this.dayGrid.rowCnt > 1; // TODO: make grid responsible
10045
10423
  this.weekNumbersVisible = this.opt('weekNumbers');
@@ -10059,8 +10437,8 @@ var BasicView = fcViews.basic = View.extend({
10059
10437
 
10060
10438
  // Unrenders the content of the view. Since we haven't separated skeleton rendering from date rendering,
10061
10439
  // always completely kill the dayGrid's rendering.
10062
- destroy: function() {
10063
- this.dayGrid.destroyDates();
10440
+ unrenderDates: function() {
10441
+ this.dayGrid.unrenderDates();
10064
10442
  this.dayGrid.removeElement();
10065
10443
  },
10066
10444
 
@@ -10203,7 +10581,7 @@ var BasicView = fcViews.basic = View.extend({
10203
10581
  unsetScroller(this.scrollerEl);
10204
10582
  uncompensateScroll(this.headRowEl);
10205
10583
 
10206
- this.dayGrid.destroySegPopover(); // kill the "more" popover if displayed
10584
+ this.dayGrid.removeSegPopover(); // kill the "more" popover if displayed
10207
10585
 
10208
10586
  // is the event limit a constant level number?
10209
10587
  if (eventLimit && typeof eventLimit === 'number') {
@@ -10259,8 +10637,8 @@ var BasicView = fcViews.basic = View.extend({
10259
10637
 
10260
10638
 
10261
10639
  // Unrenders all event elements and clears internal segment data
10262
- destroyEvents: function() {
10263
- this.dayGrid.destroyEvents();
10640
+ unrenderEvents: function() {
10641
+ this.dayGrid.unrenderEvents();
10264
10642
 
10265
10643
  // we DON'T need to call updateHeight() because:
10266
10644
  // A) a renderEvents() call always happens after this, which will eventually call updateHeight()
@@ -10278,8 +10656,8 @@ var BasicView = fcViews.basic = View.extend({
10278
10656
  },
10279
10657
 
10280
10658
 
10281
- destroyDrag: function() {
10282
- this.dayGrid.destroyDrag();
10659
+ unrenderDrag: function() {
10660
+ this.dayGrid.unrenderDrag();
10283
10661
  },
10284
10662
 
10285
10663
 
@@ -10294,8 +10672,8 @@ var BasicView = fcViews.basic = View.extend({
10294
10672
 
10295
10673
 
10296
10674
  // Unrenders a visual indications of a selection
10297
- destroySelection: function() {
10298
- this.dayGrid.destroySelection();
10675
+ unrenderSelection: function() {
10676
+ this.dayGrid.unrenderSelection();
10299
10677
  }
10300
10678
 
10301
10679
  });
@@ -10305,7 +10683,7 @@ var BasicView = fcViews.basic = View.extend({
10305
10683
  /* A month view with day cells running in rows (one-per-week) and columns
10306
10684
  ----------------------------------------------------------------------------------------------------------------------*/
10307
10685
 
10308
- var MonthView = fcViews.month = BasicView.extend({
10686
+ var MonthView = BasicView.extend({
10309
10687
 
10310
10688
  // Produces information about what range to display
10311
10689
  computeRange: function(date) {
@@ -10347,28 +10725,28 @@ var MonthView = fcViews.month = BasicView.extend({
10347
10725
 
10348
10726
  });
10349
10727
 
10350
- MonthView.duration = { months: 1 }; // important for prev/next
10728
+ ;;
10351
10729
 
10352
- MonthView.defaults = {
10353
- fixedWeekCount: true
10730
+ fcViews.basic = {
10731
+ 'class': BasicView
10354
10732
  };
10355
- ;;
10356
10733
 
10357
- /* A week view with simple day cells running horizontally
10358
- ----------------------------------------------------------------------------------------------------------------------*/
10734
+ fcViews.basicDay = {
10735
+ type: 'basic',
10736
+ duration: { days: 1 }
10737
+ };
10359
10738
 
10360
10739
  fcViews.basicWeek = {
10361
10740
  type: 'basic',
10362
10741
  duration: { weeks: 1 }
10363
10742
  };
10364
- ;;
10365
10743
 
10366
- /* A view with a single simple day cell
10367
- ----------------------------------------------------------------------------------------------------------------------*/
10368
-
10369
- fcViews.basicDay = {
10370
- type: 'basic',
10371
- duration: { days: 1 }
10744
+ fcViews.month = {
10745
+ 'class': MonthView,
10746
+ duration: { months: 1 }, // important for prev/next
10747
+ defaults: {
10748
+ fixedWeekCount: true
10749
+ }
10372
10750
  };
10373
10751
  ;;
10374
10752
 
@@ -10377,19 +10755,7 @@ fcViews.basicDay = {
10377
10755
  // Is a manager for the TimeGrid subcomponent and possibly the DayGrid subcomponent (if allDaySlot is on).
10378
10756
  // Responsible for managing width/height.
10379
10757
 
10380
- var AGENDA_DEFAULTS = {
10381
- allDaySlot: true,
10382
- allDayText: 'all-day',
10383
- scrollTime: '06:00:00',
10384
- slotDuration: '00:30:00',
10385
- minTime: '00:00:00',
10386
- maxTime: '24:00:00',
10387
- slotEventOverlap: true // a bad name. confused with overlap/constraint system
10388
- };
10389
-
10390
- var AGENDA_ALL_DAY_EVENT_LIMIT = 5;
10391
-
10392
- var AgendaView = fcViews.agenda = View.extend({
10758
+ var AgendaView = View.extend({
10393
10759
 
10394
10760
  timeGrid: null, // the main time-grid subcomponent of this view
10395
10761
  dayGrid: null, // the "all-day" subcomponent. if all-day is turned off, this will be null
@@ -10437,7 +10803,7 @@ var AgendaView = fcViews.agenda = View.extend({
10437
10803
 
10438
10804
 
10439
10805
  // Renders the view into `this.el`, which has already been assigned
10440
- render: function() {
10806
+ renderDates: function() {
10441
10807
 
10442
10808
  this.el.addClass('fc-agenda-view').html(this.renderHtml());
10443
10809
 
@@ -10466,12 +10832,12 @@ var AgendaView = fcViews.agenda = View.extend({
10466
10832
 
10467
10833
  // Unrenders the content of the view. Since we haven't separated skeleton rendering from date rendering,
10468
10834
  // always completely kill each grid's rendering.
10469
- destroy: function() {
10470
- this.timeGrid.destroyDates();
10835
+ unrenderDates: function() {
10836
+ this.timeGrid.unrenderDates();
10471
10837
  this.timeGrid.removeElement();
10472
10838
 
10473
10839
  if (this.dayGrid) {
10474
- this.dayGrid.destroyDates();
10840
+ this.dayGrid.unrenderDates();
10475
10841
  this.dayGrid.removeElement();
10476
10842
  }
10477
10843
  },
@@ -10610,7 +10976,7 @@ var AgendaView = fcViews.agenda = View.extend({
10610
10976
 
10611
10977
  // limit number of events in the all-day area
10612
10978
  if (this.dayGrid) {
10613
- this.dayGrid.destroySegPopover(); // kill the "more" popover if displayed
10979
+ this.dayGrid.removeSegPopover(); // kill the "more" popover if displayed
10614
10980
 
10615
10981
  eventLimit = this.opt('eventLimit');
10616
10982
  if (eventLimit && typeof eventLimit !== 'number') {
@@ -10701,12 +11067,12 @@ var AgendaView = fcViews.agenda = View.extend({
10701
11067
 
10702
11068
 
10703
11069
  // Unrenders all event elements and clears internal segment data
10704
- destroyEvents: function() {
11070
+ unrenderEvents: function() {
10705
11071
 
10706
- // destroy the events in the subcomponents
10707
- this.timeGrid.destroyEvents();
11072
+ // unrender the events in the subcomponents
11073
+ this.timeGrid.unrenderEvents();
10708
11074
  if (this.dayGrid) {
10709
- this.dayGrid.destroyEvents();
11075
+ this.dayGrid.unrenderEvents();
10710
11076
  }
10711
11077
 
10712
11078
  // we DON'T need to call updateHeight() because:
@@ -10730,10 +11096,10 @@ var AgendaView = fcViews.agenda = View.extend({
10730
11096
  },
10731
11097
 
10732
11098
 
10733
- destroyDrag: function() {
10734
- this.timeGrid.destroyDrag();
11099
+ unrenderDrag: function() {
11100
+ this.timeGrid.unrenderDrag();
10735
11101
  if (this.dayGrid) {
10736
- this.dayGrid.destroyDrag();
11102
+ this.dayGrid.unrenderDrag();
10737
11103
  }
10738
11104
  },
10739
11105
 
@@ -10754,35 +11120,50 @@ var AgendaView = fcViews.agenda = View.extend({
10754
11120
 
10755
11121
 
10756
11122
  // Unrenders a visual indications of a selection
10757
- destroySelection: function() {
10758
- this.timeGrid.destroySelection();
11123
+ unrenderSelection: function() {
11124
+ this.timeGrid.unrenderSelection();
10759
11125
  if (this.dayGrid) {
10760
- this.dayGrid.destroySelection();
11126
+ this.dayGrid.unrenderSelection();
10761
11127
  }
10762
11128
  }
10763
11129
 
10764
11130
  });
10765
11131
 
10766
- AgendaView.defaults = AGENDA_DEFAULTS;
10767
-
10768
11132
  ;;
10769
11133
 
10770
- /* A week view with an all-day cell area at the top, and a time grid below
10771
- ----------------------------------------------------------------------------------------------------------------------*/
11134
+ var AGENDA_ALL_DAY_EVENT_LIMIT = 5;
10772
11135
 
10773
- fcViews.agendaWeek = {
10774
- type: 'agenda',
10775
- duration: { weeks: 1 }
10776
- };
10777
- ;;
11136
+ // potential nice values for the slot-duration and interval-duration
11137
+ // from largest to smallest
11138
+ var AGENDA_STOCK_SUB_DURATIONS = [
11139
+ { hours: 1 },
11140
+ { minutes: 30 },
11141
+ { minutes: 15 },
11142
+ { seconds: 30 },
11143
+ { seconds: 15 }
11144
+ ];
10778
11145
 
10779
- /* A day view with an all-day cell area at the top, and a time grid below
10780
- ----------------------------------------------------------------------------------------------------------------------*/
11146
+ fcViews.agenda = {
11147
+ 'class': AgendaView,
11148
+ defaults: {
11149
+ allDaySlot: true,
11150
+ allDayText: 'all-day',
11151
+ slotDuration: '00:30:00',
11152
+ minTime: '00:00:00',
11153
+ maxTime: '24:00:00',
11154
+ slotEventOverlap: true // a bad name. confused with overlap/constraint system
11155
+ }
11156
+ };
10781
11157
 
10782
11158
  fcViews.agendaDay = {
10783
11159
  type: 'agenda',
10784
11160
  duration: { days: 1 }
10785
11161
  };
11162
+
11163
+ fcViews.agendaWeek = {
11164
+ type: 'agenda',
11165
+ duration: { weeks: 1 }
11166
+ };
10786
11167
  ;;
10787
11168
 
10788
11169
  return fc; // export for Node/CommonJS