fullcalendar-rails 2.4.0.0 → 2.4.0.1

Sign up to get free protection for your applications and to get access to all the features.
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